LoginSignup
44
23

More than 5 years have passed since last update.

[図解] delegatecall callcode call の違い

Last updated at Posted at 2018-07-04

はじめに

Solidity には contract が別の contract を呼び出す仕組みがあるが、関数がいくつかあるためどれを使えば良いのか多少混乱しました。Remix で動かしつつ挙動を確認したのでそれぞれの特徴を整理してみました。

実際に動かして挙動を試したい方は、こちらの gist を remix に貼り付けて動きを見てみる事をオススメします。
https://gist.github.com/critesjosh/68593429fd2f84f8f55d4ff7b74f0323

関数と特徴

上記 gist のコントラクトを例として各関数の挙動を解説します。

pragma solidity ^0.4.15;

contract C1 {
  uint public num;
  address public sender;

  // 直接呼び出し
  function c2setNum(address _c2, uint _num) public{
      C2 c2 = C2(_c2);
      c2.setNum(_num);
  }

  // call 呼び出し
  function callSetNum(address c2, uint _num) public {
    if(!c2.call(bytes4(sha3("setNum(uint256)")), _num)) revert(); // C2's num is set 
  }

  // callcode 呼び出し
  function callcodeSetNum(address c2, uint _num) public {
    if(!c2.callcode(bytes4(sha3("setNum(uint256)")), _num)) revert(); // C1's num is set
  }

  // delegatecall 呼び出し
  function delegatecallSetNum(address c2, uint _num) public {
    if(!c2.delegatecall(bytes4(sha3("setNum(uint256)")), _num)) revert(); // C1's num is set 
  }
}

contract C2 {
  uint public num;
  address public sender;

  function setNum(uint _num) public {
    num = _num;
    sender = msg.sender;
    // msg.sender is C1 if invoked by C1.callcodeSetNum
    // msg.sender is C3 if invoked by C3.foo()

  }
}

関数の直接呼び出し

上記の c2setNum による呼び出し。

スクリーンショット 2018-07-04 11.16.33.png

一番オーソドックスな呼びだし方で、C2 側の msg.sender には C1 のアドレスが入る。
C2 が参照する storage 領域は C2 のものなので、C2 の numsender が更新される。

Call による呼び出し

上記の callSetNum による呼び出し。
具体的に呼び出しは関数名の sha3 の上位 4 バイトを使って呼び出す。

c2.call(bytes4(sha3("setNum(uint256)")), _num)

スクリーンショット 2018-07-04 11.16.37.png

挙動としては直接呼び出しと同じ。

callcode による呼び出し

上記の callcodeSetNum による呼び出し。

スクリーンショット 2018-07-04 11.16.41.png

この呼び出し方では、C2 が指す Storage 領域は C1 のものになる。また msg.sender は直接の呼び出し元である C1 のアドレスになる。
この関数が実行され終わったあとで、C1 の numstorage が更新されていて、C2 のnumstorage は更新されない。
現在は非推奨の method なので基本的には使わない方が良い。

delegatecall による 呼び出し

上記の delegatecallSetNum による呼び出し。

スクリーンショット 2018-07-04 11.16.47.png

この呼び出し方では、C2 が指す Storage 領域は C1 のものになる。また msg.sender は元々の呼び出し元である EOA のアドレスになる。
EOA からみるとあたかも C1 コントラクトが全ての処理を行い結果を格納しているように見えるので、ライブラリなどを利用する際にはこの呼び出し方を使うと良い。

まとめ

それぞれの関数はの特徴をまとめるよ以下のようになります。

関数の呼び出し方 msg.sender storage 用途
直接呼び出し 直接の呼び出し元 呼び出された contract の storage を指す Crowdsale contract が token contract の関数を呼び出すなど
call 直接の呼び出し元 呼び出された contract の storage を指す Crowdsale contract が token contract の関数を呼び出すなど
callcode 直接の呼び出し元 直接の呼び出し元 contract の storage を指す 非推奨
delegatecall EOA 直接の呼び出し元 contract の storage を指す ライブラリなど
44
23
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
44
23