Call,DelegateCall
Call,DelegateCallについて学習します
これはコントラクトからコントラクトの関数を呼び出すための抽象的な命令です
呼び出し
以下のような形で関数を呼び出せる
ADDRESS.call(bytes memory calldata)
ADDRESS.delegatecall(bytes memory calldata)
calldata
call,delegatecallに用いるcalldataはSelectorとその引数をABIでパックしたものになる
// deployed address "0x123..."
contract Target {
uint number;
// selector is 0x3fb5c1cb
function setNumber(uint num) external {
number = num;
}
}
contract Sample {
uint number;
function callSample() {
address(0x123...).call(abi.encodeWithSignature("setNumber(uint)",123));
// same as above
address(0x123...).call(abi.encodeWithSelector(0x3fb5c1cb,123));
}
}
delegatecall
callは「呼び出されたコントラクト(Target)が、呼び出されたコントラクトのストレージ(Target)を、呼び出された関数のロジックで干渉する」というものである
つまり、呼び出し元は単なるトリガーである
それに対してdelegatecallは「呼び出されたコントラクト(Target)が、呼び出したコントラクトのストレージ(Sample)を、呼び出された関数のロジックで干渉する」というものである
delegate(委譲)の意味する通り、実装のみをcallしているが干渉する実体は呼び出し元というのが特徴である
contract Target {
uint number;
function setNumber(uint num) external {
number = num;
}
}
contract Sample {
uint number;
function delegatecallSample() {
address(0x123...).delegatecall(abi.encodeWithSignature("setNumber(uint)",123));
}
}
上記を実行したとき、TargetではなくSampleのnumberが123にセットされる
Storage layout
詳しくはStorage Layoutにて解説するが、delegatecallする際のストレージの干渉先は変数名ではなくストレージの場所に対応する
contract Target {
uint number;
function setNumber(uint num) external {
number = num;
}
}
contract Sample {
uint firstNumber;
uint number;
function delegatecallSample() {
address(0x123...).delegatecall(abi.encodeWithSignature("setNumber(uint)",123));
}
}
上記を実行したとき、Targetのnumberは先頭にあるので、Sampleにおける書き込みも先頭、つまりfirstNumberになる