调用已部署合约
开发者写智能合约来调用其他合约,这让以太坊网络上的程序可以复用,从而建立繁荣的生态。很多web3项目依赖于调用其他合约,比如收益农场(yield farming)。这一讲,我们介绍如何在已知合约代码(或接口)和地址情况下调用目标合约的函数。
目标合约
我们先写一个简单的合约OtherContract来调用,并定义了接口
这个合约包含一个状态变量_x,一个事件Log在收到ETH时触发,三个函数:
- getBalance(): 返回合约ETH余额。
- setX(): external payable函数,可以设置_x的值,并向合约发送ETH。
- getX(): 读取_x的值。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
interface IOtherContract {
function getBalance() external view returns (uint256);
function setX(uint256 x) external payable;
function getX() external view returns (uint256 x);
}
contract OtherContract is IOtherContract {
uint256 private _x = 0; // 状态变量_x
// 收到eth的事件,记录amount和gas
event Log(uint256 amount, uint256 gas);
// 返回合约ETH余额
function getBalance() public view returns (uint256) {
return address(this).balance;
}
// 可以调整状态变量_x的函数,并且可以往合约转ETH (payable)
function setX(uint256 x) external payable {
_x = x;
// 如果转入ETH,则释放Log事件
if (msg.value > 0) {
emit Log(msg.value, gasleft());
}
}
// 读取_x
function getX() external view returns (uint256 x) {
x = _x;
}
}
调用OtherContract合约
我们可以利用合约的地址和合约代码(或接口)来创建合约的引用:_Name(_Address)
,其中_Name
是合约名,_Address
是合约地址。然后用合约的引用来调用它的函数:_Name(_Address).f()
,其中f()是要调用的函数。
下面我们介绍4个调用合约的例子,在remix中编译合约后,分别部署OtherContract和CallContract:
传入合约地址
我们可以在函数里传入目标合约地址,通过接口生成目标合约的引用,然后调用目标函数。以调用OtherContract合约的setX函数为例,我们在新合约中写一个callSetX函数,传入已部署好的OtherContract合约地址_Address和setX的参数x:
这里说明一下,不通过接口,直接通过目标合约代码也是可以生成目标合约的引用,但是一般都是通过接口。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import {IOtherContract} from "./OtherContract.sol";
contract CallContract {
function callSetX(address _Address, uint256 x) external {
IOtherContract(_Address).setX(x);
}
}
传入合约变量
我们可以直接在函数里传入合约的引用,只需要把上面参数的address类型改为目标合约名,比如OtherContract。下面例子实现了调用目标合约的getX()函数。
注意该函数参数IOtherContract _Address
底层类型仍然是address,生成的ABI中、调用callSetX时传入的参数都是address类型
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import {IOtherContract} from "./OtherContract.sol";
contract CallContract {
function callSetX(IOtherContract _Address, uint256 x) external {
_Address.setX(x);
}
}
创建合约变量
我们可以创建合约变量,然后通过它来调用目标函数。下面例子,我们给变量oc存储了OtherContract合约的引用:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import {IOtherContract} from "./OtherContract.sol";
contract CallContract {
function callSetX(address _Address, uint256 x) external {
IOtherContract oc = IOtherContract(_Address);
oc.setX(x);
}
}
调用合约并发送ETH
如果目标合约的函数是payable的,那么我们可以通过调用它来给合约转账:_Name(_Address).f{value: _Value}(),其中_Name是合约名,_Address是合约地址,f是目标函数名,_Value是要转的ETH数额(以wei为单位)。
OtherContract合约的setX函数是payable的,在下面这个例子中我们通过调用setX来往目标合约转账。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import {IOtherContract} from "./OtherContract.sol";
contract CallContract {
function callSetX(address _Address, uint256 x) external payable {
IOtherContract oc = IOtherContract(_Address);
oc.setX{value: msg.value}(x);
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 289211569@qq.com