概述
Solidity 中用 call,callcode,delegatecall 这三个函数来进行跨合约的调用。给他们的三个的调用区别做个笔记。
call vs callcode
call
和 callcode
的区别在于:代码执行的上下文环境不同。具体来说,call
修改的是被调用者的storage,而 callcode
修改的是调用者的storage。换用另外一种说法是,callcode
通过调用其他的智能合约的函数,来修改自己的智能合约的变量。而 call
通过调用其他的智能合约的函数,来修改被调用智能合约的状态。如下图所示:
callcode vs delegatecall
callcode
已经被官方推荐使用 delegatecall
替代了。如果使用,编译器会提示 Warning: "callcode" has been deprecated in favour of "delegatecall".
。
callcode
与 delegatecall
都是通过调用其他的智能合约的函数,来修改自己的智能合约的变量。区别就是msg.sender不同。具体来说,delegatecall
会一直使用原始调用者的地址,而 callcode
不会。如下图所示:
测试用例
下面是一个调用例子:
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;
}
}
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" 数据对比上次调用均不变
其他
上面的 callcode
跟 delegatecall
都是通过调用其他的智能合约的函数,来修改自己的智能合约的变量。如果自己合约中不存在这个变量,Solidity是怎么处理的呢?Solidity 会智能地新建一个变量。但实际是在一个未定义的位置存储了这个新建的变量的值,智能合约根本没法访问到。