call、delegatecall 和 callcode 的区别
2021-08-30 本文已影响0人
张亚伦
汇总:
call: 调用后内置变量 msg 的值会修改为调用者,执行环境为被调用者的运行环境(合约的 storage),最常用的调用方式。
delegatecall: 调用后内置变量 msg 的值不会修改为调用者(注:会改为以太坊账户的地址),但执行环境为调用者的运行环境。
callcode【v0.5.0时已禁用】: 调用后内置变量 msg 的值会修改为调用者,但执行环境为调用者的运行环境。
- 准备环境
示例合约代码:
pragma solidity ^0.4.0;
contract A {
address public temp1;
uint256 public temp2;
function three_call(address addr) public {
// 1
addr.call(bytes4(keccak256("test()")));
// 2
//addr.delegatecall(bytes4(keccak256("test()")));
// 3
//addr.callcode(bytes4(keccak256("test()")));
} }
contract B {
address public temp1;
uint256 public temp2;
function test() public {
temp1 = msg.sender;
temp2 = 100;
}
}
-
部署
部署合约A,B。 -
测试
- 方案1: addr.call(bytes4(keccak256("test()")));
结果如下:
合约A:
temp1 = 0
temp2 = 0
合约B:
temp1 = 合约A的地址
temp2 = 100
注意:call调用后内置变量 msg 的值会修改为调用者(合约A的地址),执行环境为被调用者(合约B)的运行环境(运行环境指合约的 storage)
- 方案2: addr.delegatecall(bytes4(keccak256("test()")));
结果如下:
合约A:
temp1 = 调用合约A的以太坊账号地址
temp2 = 100
合约B:
temp1 = 0
temp2 = 0
说明:delegatecall调用后内置变量 msg 的值不会修改为调用者(合约A的地址),而是caller的账户地址,但执行环境为调用者(合约A)的运行环境
- 方案3: addr.callcode(bytes4(keccak256("test()")));
结果如下:
callcode
合约A:
temp1 =合约A的地址
temp2 = 100
合约B:
temp1 = 0
temp2 = 0
说明:callcode调用后内置变量 msg 的值会修改为调用者(合约A的地址),但执行环境为调用者(合约A)的运行环境。
注意
上述的示例合约为v0.4.0,从v0.5.0之后有了一些变化,如下:
-
x.call(bytes4(keccak256("f(uint256)")), a, b)
tox.call(abi.encodeWithSignature("f(uint256)", a, b))
- Function
callcode
is now disallowed (in favor ofdelegatecall
). It is still possible to use it via inline assembly.
示例合约代码:
pragma solidity ^0.5.0;
contract A {
address public temp1;
uint256 public temp2;
function three_call(address addr) public {
// 1
// addr.call(bytes4(keccak256("test()")));
addr.call(abi.encodeWithSignature("test()"));
// 2
// addr.delegatecall(abi.encodeWithSignature("test()"));
// 3 depreated
// addr.callcode(abi.encodeWithSignature("test()"));
} }
contract B {
address public temp1;
uint256 public temp2;
function test() public {
temp1 = msg.sender;
temp2 = 100;
}
}