在Solidity中,合约间的调用非常普遍且重要。这里就做个总结。以前的文章有些零散,归纳下,且做些参考。
A合约调用B合约有四种方法,如下: 1.合约对象,2.继承,3.接口, 4.低级调用,call和delegatecall
这些调用方法可以增强合约的灵活性,使其能够执行跨合约操作,比如获取数据、转移资金或触发其他合约的功能。具体的应用场景需具体实现。
1.合约对象
直接生成合约对象来调用。这和很多语言的对象化编程是相通的。
contract FuncAdd {
uint x = 263;
function add(uint x, uint y) external returns(uint) {
return x;
};
}
contract TestAdd {
//用合约地址来实例化接口
FuncAdd t = FuncAdd(0x3dA5048CE9384a35fF4F3AAF0B4804114e584039);
function test(uint x, uint y) public returns(uint){
return t.add(x, y);
}
}
2.继承
继承A合约中的方法,可以直接调用。
contract A {
uint public x;
function setValue(uint _x) public {
x = _x;
}
}
contract B is A {
function modify(uint _y) public {
setValue(_y); //可以直接调用A合约中的方法
}
}
3.接口
接口有点类似抽象合约的功能,可用于两个合约间的调用。是非侵入式接口,也就是不用显式的调用接口。在ERC20合约中比较常见。 注意:B中引用A的接口,A的函数仍会在A的环境中执行。
一个合约想调用另一个合约的方法时,尽量用接口来调用,不要用call, delegatecall。
interface IFuncAdd {
function add(uint x, uint y) external returns(uint);
}
contract TestAdd {
//用合约地址来实例化接口
IFuncAdd t = IFuncAdd(0x3dA5048CE9384a35fF4F3AAF0B4804114e584039);
function test(uint x, uint y) public returns(uint){
return t.add(x, y);
}
}
4. call和delegatecall
call 会切换到被调合约中去执行方法,会切换上下文。msg.sender是主调合约地址。
delegatecall 则是将被调合约中的方法调到本合约中执行,也就是不会切换上下文。msg.sender是发起者本人。
另外,还有staticcall是 call 的只读版本,适用于调用不会修改状态的函数。在调用期间,合约的状态无法被改变。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract SetNum {
uint256 public n = 20;
address public sender;
function setN(uint256 _n) public {
n = _n;
sender = msg.sender;
}
}
contract CallSet {
uint256 public n = 15;
address public sender;
function CallTest(address _addr, uint256 _n) public {
_addr.call(abi.encodeWithSignature("setN(uint256)", _n)); //改变SetNum中的n值
sender = msg.sender; //是调用者本人,同时会改变SetNum的sender地址
//(bool success, bytes memory returnData) = _addr.staticcall(data);
}
function delegatecallTest(address _addr, uint256 _n) public {
_addr.delegatecall(abi.encodeWithSignature("setN(uint256)", _n)); //改变自身的n值
//(bool success, bytes memory data) = _contract.delegatecall(
// abi.encodeWithSignature("setVars(uint256)", _num)
// );
sender = msg.sender; //是调用者本人,不会改变SetNum的sender地址
}
}
// 注意: delegatecall是相当于将被调合约的函数引入自身中执行,所以,这两个合约中的状态变量要一致!否则容易出错。
// 传入的参数(address _addr)是合约地址。
Solidity提供了这四种主要的合约间的调用方法,包括合约对象调用、合约继承,低级调用和接口调用。每种方法都有其适用的场景和特点。开发时要根据具体需求选择合适的调用方式。在合约调用过程中,安全性问题要特别重视,尤其是低级调用中会有重入攻击等危险性,要做好合约的安全工作。