地址
地址类型有两种风格,它们基本相同:
- address:保存一个 20 字节的值(以太坊地址的大小)。
- address payable:与 相同address,但具有附加成员transfer和send。
这种区别背后的想法是,您可以将 Ether 发送到一个地址,而您不应该将 Ether 发送到 plain ,例如,因为它可能是不是为接受 Ether 而构建的智能合约。address payableaddress
类型转换:
允许从to的隐式转换,而从to的转换 必须通过.address payableaddressaddressaddress payablepayable(<address>)
address允许对uint160、整数文字 bytes20和合约类型进行显式转换。
只有 typeaddress和 contract-type 的表达式可以通过显式转换转换为类型。对于合约类型,只有当合约可以接收以太币时才允许这种转换,即合约要么具有接收或应付回退功能。请注意,这是有效的,并且是此规则的一个例外。address payablepayable(...) payable(0)
笔记
如果您需要一个类型的变量address并计划向其发送 Ether,则声明其类型以使此要求可见。另外,请尝试尽早进行这种区分或转换。address payable
运营商:
- <=, <, ==, !=,>=和>
警告
例如,如果将使用较大字节大小的类型转换address为bytes32,则 将address被截断。为了减少转换歧义,版本 0.4.24 及更高版本的编译器强制您在转换中显式截断。以 32 字节的值为例0x111122223333444455556666777788889999AAAABBBBCCCCDDDDEEEEFFFFCCCC。
您可以使用address(uint160(bytes20(b))), 导致0x111122223333444455556666777788889999aAaa, 或者您可以使用address(uint160(uint256(b))), 导致0x777788889999AaAAbBbbCcccddDdeeeEfFFfCcCc.
笔记
address和之间的区别是在 0.5.0 版本中引入的。同样从该版本开始,合约不是从地址类型派生的,但 如果它们具有接收或应付回退功能,仍可以显式转换为or 。address payableaddressaddress payable
地址成员
有关地址的所有成员的快速参考,请参阅地址类型的成员。
-
balance
和transfer
可以使用该属性查询地址的余额,balance
并使用以下函数将 Ether(以 wei 为单位)发送到应付地址transfer
:
address payable x = payable(0x123); address myAddress = address(this); if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10);
transfer
如果当前合约的余额不够大或以太币转账被接收账户拒绝,则该功能失败。该transfer
功能在失败时恢复。
笔记
Ifx
是一个合约地址,它的代码(更具体地说:它的Receive Ether Function,如果存在,或者它的Fallback Function,如果存在)将与调用一起执行transfer
(这是
EVM 的一个特性,无法阻止)。如果该执行用完 gas 或以任何方式失败,则 Ether 转移将被恢复,当前合约将异常停止。
-
send
Send 是 的低级对应物transfer
。如果执行失败,当前合约不会异常停止,而是send
会返回false
。
警告
使用 有一些危险send
:如果调用堆栈深度为 1024,则传输失败(这总是由调用者强制执行的),如果接收者用完 gas,它也会失败。因此,为了进行安全的 Ether 转账,请始终检查 的返回值send
,使用transfer
甚至更好:使用收款人取款的模式。
-
call
,delegatecall
和staticcall
为了与不遵守 ABI 的合约进行交互,或者为了更直接地控制编码,提供了call
和delegatecall
功能staticcall
。它们都采用单个参数并返回成功条件(作为 a )和返回的数据()。函数、和可用于对结构化数据进行编码 。bytes memory
bool
bytes memory
abi.encode
abi.encodePacked
abi.encodeWithSelector
abi.encodeWithSignature
例子:
bytes memory payload = abi.encodeWithSignature("register(string)", "MyName"); (bool success, bytes memory returnData) = address(nameReg).call(payload); require(success);
警告
所有这些函数都是低级函数,应该小心使用。具体来说,任何未知的合约都可能是恶意的,如果您调用它,您会将控制权移交给该合约,该合约可能会反过来回调您的合约,因此请准备好在调用返回时更改您的状态变量。与其他合约交互的常规方式是调用合约对象 ( x.f()
) 上的函数。
笔记
以前版本的 Solidity 允许这些函数接收任意参数,并且还会以不同的方式处理第一个类型的参数 bytes4
。这些边缘情况在 0.5.0 版中被删除。
可以使用调节器调整供应的气体gas
:
address(nameReg).call{gas: 1000000}(abi.encodeWithSignature("register(string)", "MyName"));
同样,也可以控制提供的 Ether 值:
address(nameReg).call{value: 1 ether}(abi.encodeWithSignature("register(string)", "MyName"));
最后,可以组合这些修饰符。他们的顺序无关紧要:
address(nameReg).call{gas: 1000000, value: 1 ether}(abi.encodeWithSignature("register(string)", "MyName"));
以类似的方式,delegatecall
可以使用该功能:不同之处在于仅使用给定地址的代码,所有其他方面(存储,余额,...)均取自当前合约。的目的delegatecall
是使用存储在另一个合约中的库代码。用户必须确保两个合约中的存储布局都适合使用委托调用。
笔记
在宅基地之前,只有一个名为的有限变体callcode
可用,它不提供对原始值msg.sender
和msg.value
值的访问。此功能已在 0.5.0 版中删除。
因为staticcall
也可以使用拜占庭。这与 基本相同call
,但如果被调用的函数以任何方式修改状态,它将恢复。
这三个函数call
和都是非常低级的函数delegatecall
,staticcall
只能作为最后的手段使用,因为它们破坏了 Solidity 的类型安全。
该gas
选项适用于所有三种方法,而该value
选项仅适用于call
.
笔记
最好避免在智能合约代码中依赖硬编码的 gas 值,无论状态是读取还是写入,因为这可能有很多陷阱。此外,未来天然气的获取可能会发生变化。
-
code
和codehash
您可以查询任何智能合约的部署代码。用于.code
将 EVM 字节码获取为 ,它可能为空。使用获取该代码的 Keccak-256 哈希(作为)。请注意,这比使用便宜。bytes memory
.codehash
bytes32
addr.codehash
keccak256(addr.code)
笔记
所有合约都可以转换为address
类型,因此可以使用 查询当前合约的余额address(this).balance
。