メインコンテンツまでスキップ

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になる

Reference