WTF Solidity: 23. Delegatecall
Twitter: @0xAA_Science | @WTFAcademy_
Community: Discord|Wechat|Website wtf.academy
Codes and tutorials are open source on GitHub: github.com/AmazingAng/WTFSolidity
delegatecall
delegatecall
is similar to call
, is a low level function in Solidity
. delegate
meas entrust/represent, so what does delegatecall
entrust?
When user A
call
contract C
via contract B
, the executed functions are from contract C
, the execution context
(the environment including state and variable) is in contract C
: msg.sender
is contract B
's address, and if state variables are changed due to function call, the affected state variables are in contract C
.
And when user A
delegatecall
contract C
via contract B
, the executed functions are from contract C
, the execution context
is in contract B
: msg.sender
is user A
's address, and if state variables are changed due to function call, the affected state variables are in contract B
.
You can understand it like this: a rich businessman
entrusts his asset (state variables
) to a VC
(functions of target contract) for management. The executed functions are from the VC
, but the state variables get changed is from the businessman
.
The syntax of delegatecall
is simimar to call
:
targetContractAddress.delegatecall(binary code);
the binary code
is generated by abi.encodeWithSignature
:
abi.encodeWithSignature("function signature", parameters separated by comma)
function signature
is "functionName(parameters separated by comma)"
. For example, abi.encodeWithSignature("f(uint256,address)", _x, _addr)
。
Unlike call
, delegatecall
can specify the value of gas
when calling smart contract, but the value of ETH
can't be specified.
Attention: using delegatecall could incur risk, make sure the storage layout of state variables of current contract and target cotnract is same, and target contract is safe, otherwise could cause loss of funds.
delegatecall
use cases?
Currently there are 2 major use cases for delegatecall:
Proxy Contract
: separating the storage part and logic part of smart contract:proxy contract
is used to store all related variables, and also store the address of logic contract; all functions are stored in thelogic contract
, and called via delegatecall. When upgrading, you only need to redirectproxy contract
to a newlogic contract
.- EIP-2535 Diamonds: Diamond is a standard that supports building modular smart contract systems that can scale in production. Diamond is a proxy contract with multiple implementation contracts. For more information, check: Introduction to EIP-2535 Diamonds.
delegatecall
example
Call mechanism: you (A
) call contract C
via contract B
.
Target Contract C
First we create a target contract C
with 2 public
variables: num
and sender
which are uint256
and address
respectively; and a function which sets num
based on _num
, and set sender
as msg.sender
.
// Target contract C
contract C {
uint public num;
address public sender;
function setVars(uint _num) public payable {
num = _num;
sender = msg.sender;
}
}
Call Initizalization Contract B
First, contract B
must have the same state variable layout as target contract C
, 2 variabels and the order is num
and sender
.
contract B {
uint public num;
address public sender;
Next, we use call
and delegatecall
respectively to call setVars
from contract C
, so we can understand the difference better.
Function callSetVars
calls setVars
via call
. callSetVars has 2 parameters, _addr
and _num
, which correspond to contract C
's address and the parameter of setVars
.
// Calling setVars() of contract C with call, the state variables of contract C will be changed
function callSetVars(address _addr, uint _num) external payable{
// call setVars()
(bool success, bytes memory data) = _addr.call(
abi.encodeWithSignature("setVars(uint256)", _num)
);
}
While function delegatecallSetVars
calls setVars
via delegatecall
. Similar to callSetVars
, delegatecallSetVars has 2 parameters, _addr
and _num
, which correspond to contract C
's address and the parameter of setVars
.
// Calling setVars() of contract C with delegatecall, the state variables of contract B will be changed
function delegatecallSetVars(address _addr, uint _num) external payable{
// delegatecall setVars()
(bool success, bytes memory data) = _addr.delegatecall(
abi.encodeWithSignature("setVars(uint256)", _num)
);
}
}
Verify on Remix
- First we deploy contract B and contract C
- After deployment, check the initial value of state variables in contract
C
, also the initial value of state variables in contractB
.
- Next, call
callSetVars
in contractB
with arguments of contractC
's address and10
- After execution, the state variables in contract
C
are changed:num
is changed to 10,sender
is changed to contract B's address
- Next, we call
delegatecallsetVars
in contractB
with arguments of contractC
's address and100
- Because of
delegatecall
, the execution context is contractB
. Afte execution, the state variables of contractB
are changed:num
is changed to 100,sender
is changed to your wallet's address. The state variables of contractC
are unchanged.
Summary
In this lecture we introduce another low level function in Solidity
, delegatecall
. Similar to call
, delegatecall
can be used to call another contract; the difference of delegatecall
and call
is execution context
, the execution context
is C
if B
call
C
; but the execution context
is B
if B
delegatecall
C
. The major use cases for delegatecall is proxy contract
and EIP-2535 Diamons
.