solidity合约之间调用方式总结

450 阅读1分钟

solidity进行合约之间调用有四种方式:

  • call
  • callcode:官方不建议在使用
  • delegatecall:相当于callcode的bugfix版本
  • staticcall:目前不支持使用

call与callcode

call使用demo

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract Receiver {
    event Received(address caller, uint amount, string message);

    fallback() external payable {
        emit Received(msg.sender, msg.value, "Fallback was called");
    }

    function foo(string memory _message, uint _x) public payable returns (uint) {
        emit Received(msg.sender, msg.value, _message);

        return _x + 1;
    }
}

contract Caller {
    event Response(bool success, bytes data);

    // Let's imagine that contract Caller does not have the source code for the
    // contract Receiver, but we do know the address of contract Receiver and the function to call.
    function testCallFoo(address payable _addr) public payable {
        // You can send ether and specify a custom gas amount
        // 如果对方函数没有返回值,data=0x0 success=true
        (bool success, bytes memory data) = _addr.call{value: msg.value, gas: 5000}(
            abi.encodeWithSignature("foo(string,uint256)", "call foo", 123)
        );

        emit Response(success, data);
    }

}

调用结果:

image.png

delegatecall使用demo

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

// NOTE: Deploy this contract first
contract B {
    // NOTE: storage layout must be the same as contract A
    uint public num;
    address public sender;
    uint public value;

    function setVars(uint _num) public payable {
        num = _num;
        sender = msg.sender;
        value = msg.value;
    }
}

contract A {
    uint public num;
    address public sender;
    uint public value;

    function setVars(address _contract, uint _num) public payable {
        // A's storage is set, B is not modified.
        (bool success, bytes memory data) = _contract.delegatecall(
            abi.encodeWithSignature("setVars(uint256)", _num)
        );
    }
}

两个方式区别点(以合约A调用合约B场景为例):

  1. 如果使用call,在B执行时,storage是B,而使用delegatecall,在B执行时,storage是A
  2. 如果使用call,在B执行时,msg.sender获取的是调用A的地址,也即在A执行时的msg.sender

image.png

image.png