‘이더리움 댑 개발’ 세미나 보조 교재 – 이더리움 디앱 개발(Building Ethereum Dapps) 2부
이번 보조 교재 같이 읽기는 2부. 스마트 컨트랙트 입니다.
- 5장. 솔리디티로 스마트 컨트랙트 프로그래밍하기
- 6장. 복잡한 스마트 컨트랙트 작성하기
- 7장. 추상 컨트랙트와 인터페이스
- 8장. Web3.js로 스마트 컨트랙트 활용하기
컨트랙트 작성 수준이 갑자기 너무 높아지면서 객체지향 설계 개념까지 등장하기 때문에 읽기가 어려울 수도 있습니다. 하지만 코딩 내용이 많을 뿐 실제적인 솔리디티 내용은 몇 개 안 됩니다. 두 번 읽기 합니다. 첫 번째는 편안한 마음으로 이런 것도 가능하구나 수준에서 읽고, 두 번째는 코딩을 직접 해 보면서 코딩 중심으로 읽습니다. 솔리디티 버전을 최신 버전으로 해서 코딩하면 더 좋을 것 같습니다.
5장. 솔리디티로 스마트 컨트랙트 프로그래밍하기
대부분 세미나에서 이미 다룬 내용입니다. 복습한다고 생각하고 빠르게 읽어 나갑니다. 5.4 SimpleCoin 개선하고 리팩토링하기 부분은 코딩하면서 학습한 내용을 확인합니다.
6장. 복잡한 스마트 컨트랙트 작성하기
전체적인 내용을 한 번 읽고 아래 정리한 내용을 참고해서 코딩해 봅니다. 논리적인 전개가 매끄럽지 않은 부분들이 있어서 본문 내용을 일부 변경했습니다.
SimpleCrowdsale 소개
- 크라우드 세일은 판매 단계, 완료 단계로 구분할 수 있습니다.
- 판매 단계에서 크라우드 세일 컨트랙트(SimpleCrowdsale)는 투자자가 토큰 판매에 참여할 수 있도록 해 줘야 합니다.
- 투자자가 이더를 가지고 토큰 구매를 할 수 있습니다. 투자자 계정으로 토큰이 전달되지만 투자자가 사용할 수는 없도록 해야 합니다.
- 토큰 구매를 할 수 있는 기간이 정해져야 합니다.
- 시작시간(startTime), 종료시간(endTime)
- 투자자별(주소) 구매금액(이더)이 관리되어야 합니다.
- mapping(address => uint256) public investmentAmountOf;
- 토큰 판매가격(tokenPrice)이 관리되어야 합니다.
- 조달된 이더 총계(investmentReceived)가 관리되어야 합니다.
- 토큰 구매를 할 수 있는 기간이 정해져야 합니다.
- 투자자가 이더를 가지고 토큰 구매를 할 수 있습니다. 투자자 계정으로 토큰이 전달되지만 투자자가 사용할 수는 없도록 해야 합니다.
- 완료 단계에서 크라우드 세일 컨트랙트는 크라우드 세일이 성공한 경우 모금한 이더는 프로젝트 조직에 전달합니다. 투자자는 구매한 토큰을 사용할 수 있어야 합니다. 크라우드 세일이 실패한 경우 투자자들에게 이더를 환불해 줄 수 있어야 합니다.
- 크라우드 세일 목표(investmentObjective)가 관리되어야 합니다.
- 크라우드 세일이 완료(isFinalized)되었는지가 관리되어야 합니다.
- 크라우드 세일 컨트랙트 소유자 계정(owner)이 관리되어야 합니다.
- 판매되는 토큰 컨트랙트(crowdsaleToken)가 관리되어야 합니다.
- 토큰 컨트랙트 – SimpleCoin
- 논리적으로 마무리 단계는 의미가 없지만, 다수의 투자자에게 이더를 환불해주려면 반복문을 사용해서 이더를 전송하는 것은 보안 문제가 있어 피해야 합니다. 크라우드 세일이 실패한 경우 투자자에게 이더를 환불하는 과정을 마무리 단계로 봅니다.
- 판매 단계에서 크라우드 세일 컨트랙트(SimpleCrowdsale)는 투자자가 토큰 판매에 참여할 수 있도록 해 줘야 합니다.
SimpleCrowdsale 컨트랙트 작성
본문 내용과 위에서 설명한 내용을 참고하고, 아래 정리된 내용을 반영해서 SimpleCrowdsale 컨트랙트를 작성합니다.
본문에서는 상태변수를 ether와 wei 단위를 섞어서 사용하는데, 일관성을 위해 모두 wei를 사용하는 것으로 바꿉니다.
- 상태변수
- unit256 타입 public으로 startTime, endTime, tokenPrice, investmentObjective, investmentReceived를 작성합니다.
- address를 키로, unit256을 값으로 하는 매핑타입으로 investmentAmountOf를 작성합니다.
- bool 타입 public으로 isFinalized를 작성합니다.
- address 타입 public으로 owner를 작성합니다.
- SimpleCoin 컨트랙트 public으로 crowdsaleToken을 작성합니다.
- 함수
- 구매에 참여하는 것이 투자하는 것이니 함수 이름은 invest로 합니다.
- 투자는 이더로 받기 때문에 payable로 작성합니다.
- msg.sender를 투자자(investor)로, msg.value를 투자금(investment)으로 받습니다.
- 투자 금액이 0보다 큰지, 판매기간내에 들어온 금액인지 검증합니다.
- isValidInvestment
- 투자자별 토큰 수량이 관리되어야 하고, 총 모집금액에 투자자가 투자한 금액이 더해져야 합니다.
- 투자자는 여러 번 토큰 구매를 신청할 수 있습니다.
- 투자금이 누적되어야 합니다.
- 투자자는 여러 번 토큰 구매를 신청할 수 있습니다.
- 투자자에게 토큰이 할당되어야 합니다.
- assignTokens
- 투자금에 해당하는 토큰수를 계산해야 합니다.
- calculateNumberOfTokens
- 투자자 계정에 토큰을 발행해야 합니다.
- 토큰 컨트랙트의 mint 함수를 호출합니다.
- 투자금에 해당하는 토큰수를 계산해야 합니다.
- assignTokens
- 구매 신청 이벤트를 발생시킵니다.
- 판매를 완료해야 하니 finalize 함수를 작성합니다. finalize는 소유자만 가능하도록 합니다.
- 판매가 완료되었는지가 관리되어야 합니다.
- isFinalized
- 판매가 성공적으로 완료된 경우 프로젝트 팀에게 이더를 전송하고, 투자자가 토큰을 사용할 수 있도록 합니다. 본문 구현에는 프로젝트 팀에게 이더를 전송하는 부분은 없으니 이 부분은 무시하도록 합니다.
- 현재 시간(now)이 endTime보다 크거나 같고, 투자금이 목표액보다 크거나 같으면 성공입니다.
- 성공하면 투자자들이 구매한 토큰을 사용할 수 있도록(release) 합니다.
- 그렇지 않으면 실패이고, 투자자에게 환불합니다.
- 보안을 위해 환불이 가능한 상태로 설정(isRefundingAllowed)만 하고, 실제 환불은 개별 투자자별로 투자자의 요청(refund)에 의해 진행하도록 합니다.
- 현재 시간(now)이 endTime보다 크거나 같고, 투자금이 목표액보다 크거나 같으면 성공입니다.
- 판매가 완료되었는지가 관리되어야 합니다.
- 구매에 참여하는 것이 투자하는 것이니 함수 이름은 invest로 합니다.
- 생성자
- 크라우드 세일을 생성하려면 시작 시간, 종료 시간, 토큰 가격, 목표 금액이 설정되어야 합니다.
- 시작 시간은 지금 시간 보다 크거나 같아야 하고, 종료 시간은 시작 시간보다 커야 합니다.
- 토큰 가격과 목표 금액은 0보다 커야 합니다.
- SimpleCoin의 초기 공급량은 0으로 합니다.
- isFinalized를 false로, owner를 msg.sender로 설정합니다.
- 크라우드 세일을 생성하려면 시작 시간, 종료 시간, 토큰 가격, 목표 금액이 설정되어야 합니다.
ReleasableSimpleCoin 컨트랙트 작성
투자자에게 발행된 토큰은 크라우드 세일이 마감되기 전까지는 사용(전송)할 수 없습니다. SimpleCoin은 이런 제약이 없습니다. 이런 제약을 포함해서 ReleasableSimpleCoin을 작성합니다.
- 크라우드 세일이 성공적으로 마감되기 까지 사용할 수 없도록 릴리즈 여부(released)를 관리합니다.
- 크라우드 세일이 성공적으로 마감되면 SimpleCrowdSale 컨트랙트에서 ReleasableSimpleCoin의 release 함수를 호출합니다.
- release는 컨트랙트 소유자만 가능합니다.
- ReleasableSimpleCoin의 transfer, transferFrom 함수가 릴리즈된 경우에만 전송이 가능하도록 변경합니다.
상속을 사용해서 ReleasableSimpleCoin 작성
- SimpleCoin을 상속하도록 합니다.
- is 키워드 사용합니다.
- transfer, transfer 함수를 재정의합니다.
- isReleased modifier를 작성하고, 이를 사용해 해당 함수에 제약을 추가합니다.
Ownable 컨트랙트 작성
소유자인 경우를 제약하는 컨트랙트를 분리해서 작성하고 이를 상속받도록 합니다.
SimpleCrowdsale 실행하기
본문 내용을 참고하고 위의 정리 내용을 반영하여 컨트랙트를 실행해 봅니다.
구간 별 인센티브, 일시 중지, 파기
조달 금액을 구간 별로 나누고 인센티브를 줄 수 있습니다. 판매 초기에 참여하는 투자자에게 더 높은 인센티브를 줍니다.
토큰 전송을 일시 중지하고 싶을 때도 있을 수 있습니다.
컨트랙트를 파기(소멸)하고 싶을 때도 있을 수 있습니다.
본문 내용을 반영해서 컨트랙트를 개선합니다.
7장. 추상 컨트랙트와 인터페이스
최대 금액(fundingCap)을 설정할 수 도 있습니다.
본문 내용을 참고해서 컨트랙트를 개선합니다.
8장. Web3.js로 스마트 컨트랙트 활용하기
우리는 트러플을 사용해서 컴파일과 배포 및 테스트하고, 클라이언트로 패리티를 사용하기 때문에 해당 내용의 대부분에 관심이 없습니다. 이렇게 할 수도 있구나 트러플에서도 이렇게 하겠구나 정도를 느낄 수 있을 수준으로만 읽고 넘어갑니다.
아래 요약한 본문 내용을 집중해서 다시 한 번 읽어 봅니다.
- 다른 컨트랙트에서 배포된 컨트랙트 참조하기
- 로컬 프록시 컨트랙트를 원격 컨트랙트를 인터페이스할 수 있는 추상 컨트랙트로 정의합니다. 그런 다음 배포된 원격 컨트랙트 주소를 생성자에게 제공하여 프록시를 인스턴스화합니다.