天天看点

以太坊合约调用call、callcode和delegatecall的联系与区别

    在以太坊中,若需要合约与合约之间的调用,则可以使用call、callcode和delegatecall方法,它们之间的联系如下:

调用方式 修改storage 调用者的msg.sender 被调用的msg.sender 执行的上下文 推荐
call 修改被调用者的合约storage 交易的发起者地址 调用者地址 在被调用者里
callcode 修改调用者的合约storage 调用者地址 调用者地址 在调用者里
delegatecall 修改调用者的合约storage 交易的发起者地址 调用者地址 在调用者里

    这里以合约EthAlice调用合约EthBob为例,进行说明,其执行流程如下:

以太坊合约调用call、callcode和delegatecall的联系与区别

图(1) 合约间的调用流程(Alice调用Bob)

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) 所示:

以太坊合约调用call、callcode和delegatecall的联系与区别

图(2) call()调用方式

    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) 所示:

以太坊合约调用call、callcode和delegatecall的联系与区别

图(3) callcode()调用方式

    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) 所示:

以太坊合约调用call、callcode和delegatecall的联系与区别

图(4) delegatecode()调用方式

    Alice通过delegatecall()方式,修改的是自身num:34–>36,

它不会修改Bob的num:32–>32;

    即delegatecall()方式,修改的是调用者自身的storage。

    Alice的msg.sender = 交易的发起者地址

    Bob的msg.sender = 调用者地址(Alice合约地址)