はじめに
Solidityには、コントラクトから別のコントラクトを呼び出す機能が有ります。
特に気をつける点として、delegatecall() と call() で、どのコントラクトにstorageされるかが変わります。
delegate() / call()の動作確認
delegate()とcall()の引数は一つになります。
また、bool と bytes が返ってくることで、返り値を取得することができます。
<address>.delegatecall(bytes memory) returns (bool, bytes memory)
<address>.call(bytes memory) returns (bool, bytes memory)
さて、細かい説明はおいといて、まずはサンプルを動かしましょう。
動作を見たいときは、例えばRemixで、下記を貼り付けていただけたら確認できます。
pragma solidity ^0.5.0;
contract Callee {
uint256 public x;
uint256 public y;
function add(uint256 _x, uint256 _y) public returns(uint256, uint256) {
x = _x + 1;
y = _y + 1;
return (x, y);
}
}
contract Caller {
uint256 public x;
uint256 public y;
uint256 public v;
uint256 public w;
address public callee;
function setCallee(address _callee) public {
callee = _callee;
}
function addCall(uint256 _x, uint256 _y) public {
(bool success, bytes memory data) = callee.call(abi.encodeWithSignature("add(uint256,uint256)", _x, _y));
require(success);
(v, w) = bytesToUint256(data);
}
function addDelegateCall(uint256 _x, uint256 _y) public {
(bool success, bytes memory data) = callee.delegatecall(abi.encodeWithSignature("add(uint256,uint256)", _x, _y));
require(success);
(v, w) = bytesToUint256(data);
}
function bytesToUint256(bytes memory _b) internal pure returns(uint256, uint256) {
uint256 res1;
uint256 res2;
assembly {
res1 := mload(add(_b, 32))
res2 := mload(add(_b, 64))
}
return (res1, res2);
}
}
call()による呼び出し
上記のaddCall
で呼び出せます。
addCall()
を呼び出すと、Callee.x
Callee.y
の値が更新され、返り値としてCaller.v
Callar.w
の値が入ります。
例えば、addCall(1, 2)
と呼び出すと、
Callee : v=2
w=3
Caller : x=0
y=0
v=2
w=3
となります。Callerに値が保存されず、Calleeのx,yが更新されます。
つまり、call()
を呼び出すと、呼び出した先のコントラクトのStorageが更新されます。
delegatecall()による呼び出し
上記のaddDelegateCall
で呼び出せます。
addCall()
を呼び出すと、Caller.x
Caller.y
の値が更新され、返り値としてCaller.v
Callar.w
の値が入ります。
例えば、addDelegateCall(1, 2)
と呼び出すと、
Callee : x=0
y=0
Caller : x=2
y=3
v=2
w=3
となります。Calleeに値が保存されず、Callerのx,yが更新されます。
つまり、delegatecall()
を呼び出すと、呼び出した元のコントラクトのStorageが更新されます。
終わりに
delegatecall()とcall()のstorageの違いを、要約すると
-
call()
を呼び出すと、呼び出した先のコントラクトのStorageが更新されます。 -
delegatecall()
を呼び出すと、呼び出した元のコントラクトのStorageが更新されます。