在以太坊中,若需要合约与合约之间的调用,则可以使用call、callcode和delegatecall方法,它们之间的联系如下:
调用方式 | 修改storage | 调用者的msg.sender | 被调用的msg.sender | 执行的上下文 | 推荐 |
---|---|---|---|---|---|
call | 修改被调用者的合约storage | 交易的发起者地址 | 调用者地址 | 在被调用者里 | 是 |
callcode | 修改调用者的合约storage | 调用者地址 | 调用者地址 | 在调用者里 | 否 |
delegatecall | 修改调用者的合约storage | 交易的发起者地址 | 调用者地址 | 在调用者里 | 是 |
这里以合约EthAlice调用合约EthBob为例,进行说明,其执行流程如下:
1、调用者EthAlice
//ehtAlice.sol
pragma solidity ^0.4.18;
contract EthAlice {
uint public num;
address public addr;
function callSetNum(address ads, uint value) public returns(uint) {
require(ads.call(bytes4(keccak256("SetNum(uint256)")), value));
return value;
}
function callcodeSetNum(address ads, uint value) public returns(uint) {
require(ads.callcode(bytes4(keccak256("SetNum(uint256)")), value));
return value;
}
function delegatecallSetNum(address ads, uint value) public returns(uint) {
require(ads.delegatecall(bytes4(keccak256("SetNum(uint256)")), value));
return value;
}
}
2、被调用者EthBob
//ethBob.sol
pragma solidity ^0.4.18;
contract EthBob {
uint public num;
address public addr;
event sendAddr(address);
function SetNum(uint _num) public {
num = _num;
addr = msg.sender;
emit sendAddr(msg.sender);
}
}
3、部署合约
在MetaMask里选择一个Rinkeby测试网账户,比如0x6026DfB9816D22F65F1011639B207B1c3A2C2e84
,然后在Remix上部署EthAlice和EthBob合约,得到如下:
EthAlice合约地址:0xeb0282Dcd268afCFeAFA84ea2260edC2c7747381
EthBob合约地址:0xe981880Ac36360A1bF4F8F2a369877dbE29Aa8d4
3.1 测试call()调用方式
在Remix里的部署页面里,点开ETHALICE --> callSetNum 输入如下2个参数:
ads:0xe981880Ac36360A1bF4F8F2a369877dbE29Aa8d4
value:32
如图(2) 所示:
Alice通过call()方式,修改的是Bob的num:0–>32,
而不是自身的num:0–>0;
即call()方式,修改的是被调用者的storage。
Alice的msg.sender = 交易的发起者地址
Bob的msg.sender = 调用者地址(Alice合约地址)
3.2 测试callcode()调用方式
在Remix里的部署页面里,点开ETHALICE --> callcodeSetNum 输入如下2个参数:
ads:0xe981880Ac36360A1bF4F8F2a369877dbE29Aa8d4
value:34
如图(3) 所示:
Alice通过callcode()方式,修改的是自身num:32–>34,
它不会修改Bob的num:32–>32;
即callcode()方式,修改的是调用者自身的storage。
Alice的msg.sender = 调用者地址(Alice合约地址)
Bob的msg.sender = 调用者地址(Alice合约地址)
3.3 测试delegatecall()调用方式
在Remix里的部署页面里,点开ETHALICE --> delegatecallSetNum 输入如下2个参数:
ads:0xe981880Ac36360A1bF4F8F2a369877dbE29Aa8d4
value:36
如图(4) 所示:
Alice通过delegatecall()方式,修改的是自身num:34–>36,
它不会修改Bob的num:32–>32;
即delegatecall()方式,修改的是调用者自身的storage。
Alice的msg.sender = 交易的发起者地址
Bob的msg.sender = 调用者地址(Alice合约地址)