Solidity 中 call,callcode,delegatecall 的区别

概述

Solidity 中用 call,callcode,delegatecall 这三个函数来进行跨合约的调用。给他们的三个的调用区别做个笔记。

call vs callcode

callcallcode 的区别在于:代码执行的上下文环境不同。具体来说,call 修改的是被调用者的storage,而 callcode 修改的是调用者的storage。换用另外一种说法是,callcode 通过调用其他的智能合约的函数,来修改自己的智能合约的变量。而 call 通过调用其他的智能合约的函数,来修改被调用智能合约的状态。如下图所示:
call vs callcode

callcode vs delegatecall

callcode 已经被官方推荐使用 delegatecall 替代了。如果使用,编译器会提示 Warning: "callcode" has been deprecated in favour of "delegatecall".
callcodedelegatecall 都是通过调用其他的智能合约的函数,来修改自己的智能合约的变量。区别就是msg.sender不同。具体来说,delegatecall 会一直使用原始调用者的地址,而 callcode 不会。如下图所示:
call vs delegatecall

测试用例

下面是一个调用例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
pragma solidity ^0.4.25;
contract Demo {
int public num;
address public caller;

function Demo() public {
num = 100;
caller = tx.origin;
}
function increase() public {
num++;
caller = msg.sender;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
pragma solidity ^0.4.25;

contract DemoCall {
int public num;
address public caller;

function DemoCall() public {
num = 200;
caller = msg.sender;
}

function callIncrease(address addr) public {
addr.call(bytes4(keccak256("increase()")));
}

function callcodeIncrease(address addr) public {
addr.callcode(bytes4(keccak256("increase()")));
}

function delegatecallIncrease(address addr) public {
addr.delegatecall(bytes4(keccak256("increase()")));
}
}

假设有外部账号地址为 0x5f169EB2430D2aB55E2EEF07b6838e8E506fc502。
使用该外部账号部署Demo合约得到的地址为 0x53E356e76c93fdA06693762cC289329E52286650
使用该外部账号部署DemoCall合约得到的地址为 0x3a46Ed47a453Fee8D6984FEB4534afe670DE74cB
此时,则Demo的caller为外部账号的地址 0x5f169EB2430D2aB55E2EEF07b6838e8E506fc502,num为 100。
DemoCall的caller也是为外部账号的地址 0x5f169EB2430D2aB55E2EEF07b6838e8E506fc502,num为 200。

调用callIncrease

  • 外部账号:0xF9703aeAb705E9a48DDf1622f64c54eBD1bC4429
  • DemoCall结果:num = 200,caller = “0x5f169EB2430D2aB55E2EEF07b6838e8E506fc502” 数据均不变
  • Demo结果:num = 101(相比原来增加1),caller = “0x3a46Ed47a453Fee8D6984FEB4534afe670DE74cB”(即 DemoCall 合约地址)。

调用callcodeIncrease

  • 外部账号:0xF9703aeAb705E9a48DDf1622f64c54eBD1bC4429
  • DemoCall结果:num = 201(相比原来增加1),caller = “0x3a46Ed47a453Fee8D6984FEB4534afe670DE74cB”(即 DemoCall 合约地址)
  • Demo结果:num = 101,caller = “0x3a46Ed47a453Fee8D6984FEB4534afe670DE74cB” 数据对比上次调用均不变

调用delegatecallIncrease

  • 外部账号:0x5f169EB2430D2aB55E2EEF07b6838e8E506fc502。
  • DemoCall结果:num = 202(相比原来增加1),caller = “0x5f169EB2430D2aB55E2EEF07b6838e8E506fc502。”(即调用DemoCall合约的外部账号地址)
  • Demo结果:num = 101,caller = “0x3a46Ed47a453Fee8D6984FEB4534afe670DE74cB” 数据对比上次调用均不变

其他

上面的 callcodedelegatecall 都是通过调用其他的智能合约的函数,来修改自己的智能合约的变量。如果自己合约中不存在这个变量,Solidity是怎么处理的呢?Solidity 会智能地新建一个变量。但实际是在一个未定义的位置存储了这个新建的变量的值,智能合约根本没法访问到。

您的支持将鼓励我继续创作!
0%