Payable

address payable on storage

 



address payable (on storage)

스마트 컨트랙트의 멤버변수를 payable address 타입으로 정의해두면, 스마트 컨트랙트 내의 함수들은 이 멤버변수를 사용해서 이더(ether)를 옮길 수 있다. 참고로, solidity언어는 멤버변수로 등록하는 모든 변수에 대해 ethereum storage(스토리지)를 사용하도록 하고 있다. 멤버변수 위치에서 payable address타입의 변수를 사용하는 상황을 보면서 payable의 사용법을 이해해본다.

 

 

 

Smart Contract 코드

아래 코드를 설명하면, 스마트 컨트랙트를 배포할 때(=생성자 constructor가 호출될 때), 배포자의 계좌주소를 멤버변수(=owner 변수)에 등록한 후, 나중에 누군가가 doSend()함수를 호출하면 배포자의 계좌로 이더(ether)를 송금시키는 코드이다. 만약에, owner 변수의 타입을 address payable이 아닌 address로 정의했었다면, 송금하는 함수인 transfer()를 호출하는 부분에서 에러가 발생하게 된다.

 

pragma solidity >=0.5.0 <0.7.0;

contract Joker {
   address payable owner;

   constructor() payable public {
       owner = msg.sender;
   }
    
   function doSend(uint value) public {
       owner.transfer(value); // owner변수를 address타입으로 변경하면 에러 발생
   }
}

 

 

 

Smart Contract 예제 시나리오

위 예제에서 payable address이 사용된 방법에 대해서 조금 더 잘 이해하기 위해, 단계별로 하나씩 생각해보자. 위 예제 코드처럼 작성된 Smart Contract를 Ethereum Platform에 배포한 후, 다른 어떤 사용자가 이 Smart Contract를 사용하는 한가지 시나리오는 아래와 같다.



시나리오 등장인물



시나리오 순서

1. Smart Contract (이더리움 플랫폼에) 배포 전

A가 Smart Contract를 배포할 예정이다. 배포하기 전 A의 계좌에는 총 100 ether가 들어있다.

 

2. Smart Contract (이더리움 플랫폼에) 배포 후

A가 Smart Contract를 배포시켰다. Smart Contract를 배포하면 이더리움 플랫폼에는 Smart Contract 자체를 가리키는 하나의 계좌가 만들어지는데, A는 배포할 때 이 계좌주소로 자신계좌에서 50 ether를 송금시켰다. 참고로, 배포 후 A의 계좌에는 50 ether 보다 조금 더 줄어드는데 그 이유는 이더리움 플랫폼을 사용할 때 조그만 수수료가 발생하기 때문이다. 일부 수수료를 제외하면 A의 계좌에는 결과적으로 49.99 ether 정도가 남게 된다.

 

3. Smart Contract 에서 doSend() 함수 호출 후

앞에서 배포한 Smart Contract의 함수 중 doSend()라는 함수를 호출시키면, 스마트 컨트랙트에 있는 ether 중 doSend()안에 작성한 value값을 배포자의 계좌주소로 전송하게 된다. 아래 그림처럼 B가 doSend()함수의 입력 파라미터인 value값을 20으로 입력하고 함수를 호출하면, 컨트랙트 계좌에 있는 ether 중 20 ether가 배포자인 A의 계좌로 전송하게 된다. 그러면 계약서의 50 ether가 30 ether로 줄어들고, A의 49.99 ether가 69.99 ether로 늘어나게 된다. 이때, B 계좌에서도 돈이 조금 줄어드는 것을 볼 수 있는데, 스마트 컨트랙트를 실행하기 위한 수수료를 이더리움 플랫폼에서 가져가기 때문이다. 만약에, doSend()함수 안에 작성한 transfer()함수를 실행하는 owner변수의 타입이 address payable이 아닌 address타입이었다면, 이 시점에서 에러가 발생할 것이다. 그 이유는 address payable타입의 변수만 transfer()함수를 호출할 수 있기 때문이다. 참고로, 이때 발생하는 에러는 런타임(runtime) 중에 발생하는 것이 아닌, 컴파일(compile) 중에 에러가 발생하기 때문에, 문제를 빠르게 발견할 수 있다.