앞서 어떻게 payable
을 사용해 contract으로 이더리움을 전송받는지 간단하게 체크해 봤는데요, 이제 여기서 조금 더 진화해서 어떻게 계약으로 전송받은 이더리움을 관리할 수 있는지 살펴보겠습니다.
일단 제가 만든 계약의 조건은 아래와 같습니다.
1. 계약으로는 누구든지 송금할 수 있다. 2. 계약으로 송금된 금액은 누구든지 확인할 수 있다. 3. 하지만 계약의 잔액을 누군가한테 송금하는 것은 해당 계약을 만든 자만 할 수 있다. 4. 출금은 기본적으로 계약의 잔액 전액을 계약을 만든 사람에게 보낸다.
먼저, 계약으로 누구든지 송금이 가능하게 하는 것은 앞서 다뤘다시피 payable
방법을 통해서 구현이 가능합니다.
... function () payable public {} ...
이번엔 계약으로 송금된 금액을 누구든지 확인할 수 있게 하는 함수를 구현합시다.
... function getBalance() constant public returns (uint) { address contractAddress = this; return contractAddress.balance; } ...
this
를 address 변수로 받아오면서 지금 계약의 주소를 가져올 수 있습니다. 그리고 그 address의 balance를 리턴함으로써 계약에 송금된 잔액을 확인할 수 있습니다.
이제는 잔액을 누군가한테 송금하는 함수와 전액 출금해서 계약 생성자에게 보내는 함수 두가지만 남았는데, 기본적으로 이 함수들은 반드시 계약 생성자에 의해서만 실행되어야 합니다.(안그러면 다른 사람들이 다 자기 주소로 잔액을 털어가 버리겠죠?) 이를 위해서 생성자에서 owner
라는 멤버 변수로 계약 생성자의 주소를 저장해 놓습니다.
contract ContractMoney { address public owner; function ContractMoney() public { owner = msg.sender; } ...
그리고 이 계약 생성자의 주소를 이용하여 특정 함수가 owner에 의해서만 실행될 수 있게 modifier를 정의할 수 있습니다. (modifier에 대한 설명은 이 링크를 참조하세요.)
modifier ownerOnly() { if (owner == msg.sender) {_;} }
이제 이 modifier를 가지고 특정 주소로 특정 금액을 송금하는 함수를 구현해 보겠습니다.
function send(address to, uint amount) public ownerOnly { to.transfer(amount); }
마지막으로 계약에 있는 잔액을 전액 출금해서 생성자 주소로 보내는 함수를 구현해 보겠습니다.
function withdraw() public ownerOnly { send(owner, getBalance()); // 계약 최초 생성자에게 계약에 있는 전액을 송금합니다. }
이제 이 함수들이 잘 동작하는지 실제로 테스트해 보겠습니다.
일단 계약 생성했을 때, 계약에 잔액이 0이고 생성자가 잘 세팅되었음을 확인할 수 있습니다.
그리고 이 계약으로 1000 wei를 송금해 보겠습니다.
잘 입금되었음을 확인할 수 있습니다. 계약 생성자가 아닌 주소에서 send
함수를 호출했을 때, transfer가 일어나지 않음을 확인하실 수 있습니다.
생성자 주소인 0xca35.. 가 아닌 0x147..에서 send함수를 실행했지만, 잔액은 여전히 1000입니다.
이제 좀 더 확실한 크기로 차이를 확인하기 위해서 이 계약으로 1 ether를 보내겠습니다.
잔액이 wei단위라서 엄청 숫자가 크네요 🙂 이제 생성자 주소에서 withdraw
함수를 사용하여 전액 본인의 주소로 송금해 보겠습니다.
일단 계약의 잔액은 성공적으로 0이 되었음을 보실 수 있구요,
생성자 주소에 원래 있던 100 ether에서 계약 생성 등의 gas로 나간 것들을 빼면 거의 101 ether가 되었으므로 성공적으로 계약에 들어온 이더리움을 출금했음을 확인할 수 있습니다 🙂