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);
}
}
调用结果:
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场景为例):
- 如果使用call,在B执行时,storage是B,而使用delegatecall,在B执行时,storage是A
- 如果使用call,在B执行时,msg.sender获取的是调用A的地址,也即在A执行时的msg.sender