.delegatecall() in Solidity v0.5.0

Words
362
Reading
2 min
Listen
Play
8y

cover.001.png

引き続き、Solidity v0.5.0 Breaking Changes から気になったものをピックアップしていくシリーズです。

今回は、外部コントラクトの function を呼び出すときに使う .delegatecall() について。前回の keccak256() in Solidity v0.5.0 の内容も関係してきます。



サンプルコード


v0.4.25



複数の引数を渡せました。また、返り値は bool 1 つでした。

 <address>.delegatecall(...) returns (bool)



まず、呼び出される側のコントラクトのサンプルコード。

 pragma solidity ^0.4.25;

 contract Callee {
     uint256 public v;

     function sum(uint256 _x, uint256 _y) public {
         v = _x + _y;
     }
 }



ただの足し算をして、結果を v に保存します。桁が大きいと overflow するのでよろしくないですが、今回は無視します。

次に、呼び出す側のコントラクトのサンプルコード。

 pragma solidity ^0.4.25;

 contract Caller {
     uint256 public v;
     address public callee;

     function setCallee(address _callee) public {
         callee = _callee;
     }

     function sum(uint256 _x, uint256 _y) public {
         require(callee.delegatecall(bytes4(keccak256("sum(uint256,uint256)")), _x, _y));
     }
 }



setCallee() で適切な Callee のアドレスを設定した後で sum() を実行すると、足し算の結果が Caller の v に保存されます。また、前述した通り .delegatecall() の返り値は bool 1 つなので、そのまま require() に渡せています。


v0.5.0



前回の keccak256() in Solidity v0.5.0 でも言及した通り、引数が bytes 1 つになりました。

 <address>.delegatecall(bytes memory) returns (bool, bytes memory)



また、返り値として、bool だけでなく bytes が返ってくるようになりました。すなわち、.delegatecall() で呼び出した function の返り値にアクセスできるようになったわけです。今回は、この返り値を使って、上記 v0.4.25 のコードを少し違うパターンで実装してみようと思います。



まず、呼び出される側のサンプルコード。

 pragma solidity >0.4.99 <0.6.0;

 contract Callee {
     function sum(uint256 _x, uint256 _y) public pure returns(uint256) {
         return _x + _y;
     }
 }



今回は足し算の結果を返すようにします。上記 v0.4.25 のサンプルコードでは v に保存するところまで行いましたが、返すだけです。

次に、呼び出す側のサンプルコード。

 pragma solidity >0.4.99 <0.6.0;

 contract Caller {
     uint256 public v;
     address public callee;

     function setCallee(address _callee) public {
         callee = _callee;
     }

     function sum(uint256 _x, uint256 _y) public {
         (bool success, bytes memory data) = callee.delegatecall(abi.encodeWithSignature("sum(uint256,uint256)", _x, _y));
         require(success);
         v = bytesToUint256(data);
     }
    
     function bytesToUint256(bytes memory _b) internal pure returns(uint256) {
         uint256 res;
         assembly {
             res := mload(add(_b, 32))
         }
         return res;
     }
}



まず、.delegatecall() の引数が bytes 1 つになったため、abi.encodeWithSignature() を使って、引数として渡すデータをまとめる必要があります。また、今回は Callee が足し算の結果を保存せずに返してくるので、これを受け取って Caller 自身が v に保存しています。

まあ、足し算程度でこんなことする必要もないのですが、.delegatecall() で呼び出した function の返り値を受け取れるようなったことで、外部ライブラリで実現できることの幅は広がりそうです。



補足



v0.5.0 では、上記に関連した以下のような変更も入っていますので、ご注意ください。

  • .delegatecall() だけでなく、.call() と .staticcall() も、呼び出した function の返り値を受け取れるようになりました
  • .callcode() は使えなくなりました
.delegatecall() in Solidity v0.5.0 | Ecency