阅读(117) (10)

地址

2022-05-11 14:18:40 更新

地址类型有两种风格,它们基本相同:

  • 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

地址成员

有关地址的所有成员的快速参考,请参阅地址类型的成员

  • balancetransfer

可以使用该属性查询地址的余额,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,delegatecallstaticcall

为了与不遵守 ABI 的合约进行交互,或者为了更直接地控制编码,提供了calldelegatecall功能staticcall。它们都采用单个参数并返回成功条件(作为 a )和返回的数据()。函数、和可用于对结构化数据进行编码 。bytes memoryboolbytes memoryabi.encodeabi.encodePackedabi.encodeWithSelectorabi.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.sendermsg.value值的访问。此功能已在 0.5.0 版中删除。

因为staticcall也可以使用拜占庭。这与 基本相同call,但如果被调用的函数以任何方式修改状态,它将恢复。

这三个函数call和都是非常低级的函数delegatecallstaticcall只能作为最后的手段使用,因为它们破坏了 Solidity 的类型安全。

gas选项适用于所有三种方法,而该value选项仅适用于call.

笔记

最好避免在智能合约代码中依赖硬编码的 gas 值,无论状态是读取还是写入,因为这可能有很多陷阱。此外,未来天然气的获取可能会发生变化。

  • codecodehash

您可以查询任何智能合约的部署代码。用于.code将 EVM 字节码获取为 ,它可能为空。使用获取该代码的 Keccak-256 哈希(作为)。请注意,这比使用便宜。bytes memory.codehashbytes32addr.codehashkeccak256(addr.code)

笔记

所有合约都可以转换为address类型,因此可以使用 查询当前合约的余额address(this).balance