Solidity有三种方法向其他合约发送ETH,他们是:transfer(),send()和call(),其中call()是被鼓励的用法。
用法: 用法是接收方地址.transfer(发送ETH数额)。
transfer()的gas限制是2300,足够用于转账,但对方合约的fallback()或receive()函数不能实现太复杂的逻辑。
transfer()如果转账失败,会自动revert(回滚交易)。
// 用transfer()发送ETH to地址是接受eth的地址,amount是转账的数量
function transferETH(address payable _to, uint256 amount) external payable{
_to.transfer(amount);
}
用法是接收方地址.send(发送ETH数额)。
send()的gas限制是2300,足够用于转账,但对方合约的fallback()或receive()函数不能实现太复杂的逻辑。
send()如果转账失败,不会revert。
send()的返回值是bool,代表着转账成功或失败,需要额外代码处理一下。
error SendFailed(); // 用send发送ETH失败error
// send()发送ETH
function sendETH(address payable _to, uint256 amount) external payable{
// 处理下send的返回值,如果失败,revert交易并发送error
bool success = _to.send(amount);
if(!success){
revert SendFailed();
}
}
用法是接收方地址.call{value: 发送ETH数额}("")。
call()没有gas限制,可以支持对方合约fallback()或receive()函数实现复杂逻辑。
call()如果转账失败,不会revert。
call()的返回值是(bool, bytes),其中bool代表着转账成功或失败,需要额外代码处理一下。
error CallFailed(); // 用call发送ETH失败error
// call()发送ETH
function callETH(address payable _to, uint256 amount) external payable{
// 处理下call的返回值,如果失败,revert交易并发送error
(bool success,) = _to.call{value: amount}("");
if(!success){
revert CallFailed();
}
}
一个合约可以调用另一个合约的函数,这在构建复杂的DApps时非常有用。 调用其他合约的方式
以标准的erc20的transfer方法为例:
-
在知道调用的函数方法名称和参数的情况下:
- 创建接口合约调用
interface ITest { function transfer(address to, uint256 value) external returns (bool); } function functionCall(address erc20, address to, uint256 value) public { // 调用地址为erc20的合约的transfer方法,发送数量为value的token给到to地址。 ITest(erc20).transfer(to, value); }
- 引用原合约调用
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; function functionCall(address erc20, address to, uint256 value) public { // 调用地址为erc20的合约的transfer方法,发送数量为value的token给到to地址。 IERC20(erc20).transfer(to, value); }
- 直接使用call方法调用,用abi.encodeWithSignature(string memory signature, ...) returns (bytes memory)进行abi编码获取调用数据。 signature是函数声明
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; function functionCall(address erc20, address to, uint256 value) public { // 调用地址为erc20的合约的transfer方法,发送数量为value的token给到to地址。 erc20.call(abi.encodeWithSignature(bytes4(keccak256(bytes("transfer(address, uint256)"))), to, value)); }
-
在不知道调用的函数方法名称的情况下
- 使用call方法调用,用abi.encodeWithSelector(bytes4 selector, ...) returns (bytes memory)进行编码获取调用数据。 selector是函数选择器,通过将函数声明进行hash,然后获取的前八个十六进制字符。
function functionCall(address erc20, address to, uint256 value) public { // 调用地址为erc20的合约的transfer方法,发送数量为value的token给到to地址。 erc20.call(abi.encodeWithSelector(hex"a9059cbb", to, value)); }