文章目录

单位和全局可用变量(Units and Globally Available Variables)
特殊变量和函数
1. 区块和交易属性
在 Solidity 中,有一些特殊的全局变量和函数,用于提供区块链相关的信息,或作为常用工具函数。这些函数和变量在智能合约中始终可用,帮助开发者获取区块链的状态或交易的相关数据。
-
blockhash(uint blockNumber) returns (bytes32):返回指定区块的哈希值,仅适用于最近的 256 个区块。超出此范围的区块返回 0。
-
blobhash(uint index) returns (bytes32):返回当前交易中第 index 个 blob 的版本化哈希值。版本化哈希由一个字节的版本号(目前为 0x01)和 KZG 承诺的 SHA256 哈希的后 31 个字节组成(EIP-4844)。如果该索引的 blob 不存在,返回 0。
-
block.basefee (uint):当前区块的基础费用(EIP-3198 和 EIP-1559)。
-
block.blobbasefee (uint):当前区块的 blob 基础费用(EIP-7516 和 EIP-4844)。
-
block.chainid (uint):当前链的 Chain ID。
-
block.coinbase (address payable):当前区块矿工的地址。
-
block.difficulty (uint):当前区块的难度(仅适用于 Paris 之前的 EVM 版本)。在其他 EVM 版本中,该字段是 block.prevrandao 的废弃别名(EIP-4399)。
-
block.gaslimit (uint):当前区块的 gas 限制。
-
block.number (uint):当前区块的编号。
-
block.prevrandao (uint):由信标链提供的随机数(适用于 EVM >= Paris)。
-
block.timestamp (uint):当前区块的时间戳(自 Unix 纪元以来的秒数)。
-
gasleft() returns (uint256):返回当前交易中剩余的 gas 数量。
-
msg.data (bytes calldata):完整的 calldata(调用数据)。
-
msg.sender (address):当前调用消息的发送者地址。
-
msg.sig (bytes4):calldata 的前 4 个字节(即函数标识符)。
-
msg.value (uint):随消息发送的 Wei 数量。
-
tx.gasprice (uint):交易的 gas 价格。
-
tx.origin (address):交易的原始发送者地址,即完整调用链中的最初发起者。
注意
1.msg 变量的动态性
msg.sender、msg.value 等 msg 成员的值会在每次外部函数调用时发生变化,包括调用库函数时。
2.区块和交易属性的限制
当合约在链下执行(如本地测试或模拟环境)时,不要假设 block. 和 tx. 具有特定的值,因为这些值依赖于 EVM 实现。
3.不要依赖 block.timestamp 或 blockhash 作为随机数源
block.timestamp 和 blockhash 可能会受到矿工的操控。在某些应用场景下,恶意矿工可能会反复计算直到获得有利的哈希值。虽然当前区块的时间戳必须严格大于上一个区块的时间戳,但唯一的保证是它位于两个连续区块的时间戳之间。
4.blockhash 的可用性
出于可扩展性的考虑,并非所有区块的哈希值都可以访问,只有最近 256 个区块的哈希值可用,超过该范围的哈希值将返回 0。
5.废弃的函数和别名
-
block.blockhash 在 Solidity 0.4.22 版本中被弃用,并在 0.5.0 版本中移除。
-
msg.gas 在 Solidity 0.4.21 版本中被弃用,并在 0.5.0 版本中移除(被 gasleft() 替代)。
-
now(block.timestamp 的别名)在 Solidity 0.7.0 版本中被移除。
2. ABI 编码和解码函数
1、abi.decode(bytes memory encodedData, (…)) returns (…)
对给定的 encodedData 进行 ABI 解码,第二个参数括号内指定解码后的数据类型。示例:
(uint a, uint[2] memory b, bytes memory c) = abi.decode(data, (uint, uint[2], bytes));
2、abi.encode(…) returns (bytes memory)
对给定的参数进行 ABI 编码。
3、abi.encodePacked(…) returns (bytes memory)
对给定的参数进行紧凑编码(packed encoding)。注意:紧凑编码可能会导致数据歧义。
4、abi.encodeWithSelector(bytes4 selector, …) returns (bytes memory)
以 selector 作为前缀,对后续参数进行 ABI 编码。
5、abi.encodeWithSignature(string memory signature, …) returns (bytes memory)
等效于:
abi.encodeWithSelector(bytes4(keccak256(bytes(signature))), ...)
6、abi.encodeCall(function functionPointer, (…)) returns (bytes memory)
对函数指针 functionPointer 及其参数进行 ABI 编码,同时进行完整的类型检查,确保参数类型与函数签名匹配。结果等价于:
abi.encodeWithSelector(functionPointer.selector, (...))
注意:
1.这些编码函数可用于构造外部函数调用的数据,而不实际调用该函数。
2.keccak256(abi.encodePacked(a, b)) 可用于计算结构化数据的哈希值。警告:不同的参数类型可能会导致哈希碰撞,应谨慎使用。
3.详细的 ABI 编码规则和紧凑编码(tightly packed encoding)请参考 Solidity 官方文档。
3. bytes 成员函数
bytes.concat(…) returns (bytes memory)
将多个 bytes 和 bytes1 到 bytes32 类型的参数连接成一个字节数组。
4. string 成员函数
string.concat(…) returns (string memory)
将多个字符串参数连接成一个字符串。
5. 错误处理
1.assert(bool condition)
如果条件不成立,将触发 Panic 错误并回滚状态更改,通常用于内部错误。
2.require(bool condition)
如果条件不成立,回滚并撤销交易,常用于输入错误或外部组件错误。
3.require(bool condition, string memory message)
如果条件不成立,回滚并撤销交易,同时提供错误消息,常用于输入错误或外部组件错误。
4.revert()
中止执行并回滚状态更改。
5.revert(string memory reason)
中止执行并回滚状态更改,同时提供一个解释字符串。
6. 数学和加密函数
1、addmod(uint x, uint y, uint k) returns (uint)
计算 (x + y) % k,加法以任意精度执行,不会在 2^256 上溢出。从版本 0.5.0 起,确保 k != 0。
2、mulmod(uint x, uint y, uint k) returns (uint)
计算 (x * y) % k,乘法以任意精度执行,不会在 2^256 上溢出。从版本 0.5.0 起,确保 k != 0。
3、keccak256(bytes memory) returns (bytes32)
计算输入的 Keccak-256 哈希值。
注意:keccak256 之前有一个别名叫 sha3,在版本 0.5.0 中已被移除。
4、sha256(bytes memory) returns (bytes32)
计算输入的 SHA-256 哈希值。
5、ripemd160(bytes memory) returns (bytes20)
计算输入的 RIPEMD-160 哈希值。
6、ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)
从椭圆曲线签名中恢复与公钥相关联的地址,如果发生错误则返回零。该函数参数对应于 ECDSA 签名的值:
-
r = 签名的前 32 字节
-
s = 签名的第二个 32 字节
-
v = 签名的最后 1 字节
ecrecover 返回一个地址,而不是一个可支付地址。如果需要将资金转移到恢复的地址,可以通过 address payable 进行转换。
进一步说明:
在使用 ecrecover 时需要注意,一个有效的签名可以被转换成另一个有效的签名,而无需了解对应的私钥。在 Homestead 硬分叉中,针对交易签名的问题(参见 EIP-2)已被修复,但 ecrecover 函数未做更改。通常,这不会造成问题,除非你要求签名是唯一的,或者使用签名来识别项目。你可以使用 OpenZeppelin 的 ECDSA 辅助库,它是 ecrecover 的封装,避免了这个问题。
注意:
在私有区块链上运行 sha256、ripemd160 或 ecrecover 时,可能会遇到 “Out-of-Gas” 错误。这是因为这些函数作为“预编译合约”实现,只有在接收到第一个消息后才真正存在(尽管它们的合约代码是硬编码的)。发送到不存在的合约的消息更昂贵,因此执行可能会出现 “Out-of-Gas” 错误。解决该问题的方法是,先向每个合约发送 Wei(例如 1),然后再在实际合约中使用它们。这个问题在主网或测试网上并不存在。
7. 地址类型成员函数
1.<address>.balance (uint256) 返回地址的余额,以 Wei 为单位。
2.<address>.code (bytes memory) 返回地址处的代码(可能为空)。
3.<address>.codehash (bytes32) 返回地址的代码哈希。
4.<address payable>.transfer(uint256 amount) 向地址发送指定的 Wei 数量,失败时回滚,转发 2300 gas 补助,不可调节。
5.<address payable>.send(uint256 amount) returns (bool) 向地址发送指定的 Wei 数量,失败时返回 false,转发 2300 gas 补助,不可调节。
6.<address>.call(bytes memory) returns (bool, bytes memory) 进行低级 CALL,带有给定的有效载荷,返回成功状态和返回数据,转发所有可用的 gas,可以调节。
7.<address>.delegatecall(bytes memory) returns (bool, bytes memory) 进行低级 DELEGATECALL,带有给定的有效载荷,返回成功状态和返回数据,转发所有可用的 gas,可以调节。
8.<address>.staticcall(bytes memory) returns (bool, bytes memory) 进行低级 STATICCALL,带有给定的有效载荷,返回成功状态和返回数据,转发所有可用的 gas,可以调节。
注意:
1.尽量避免使用 .call(),因为它绕过了类型检查、函数存在性检查和参数打包。这可能导致不必要的风险,特别是在处理外部调用时。
2.使用 send 时存在一些潜在危险:当调用栈深度达到 1024 时,转账会失败(此问题可以由调用者强制触发),如果接收者没有足够的 gas,也会导致转账失败。因此,为了确保以太币转账的安全性,始终检查 send 的返回值,或者使用 transfer。更好的做法是采用一种模式,让接收者主动提取以太币。
3.由于 EVM 将对不存在的合约的调用视为始终成功,因此 Solidity 在执行外部调用时会使用 extcodesize 操作码进行额外检查。这可以确保即将调用的合约实际存在(即包含代码),否则将抛出异常。对地址而非合约实例的低级调用不进行此检查(例如 .call()、.delegatecall()、.staticcall()、.send() 和 .transfer()),因此它们在 gas 消耗上更便宜,但也更不安全。
8. 与合约相关
1.this (当前合约的类型):代表当前合约,并可以显式转换为 address 类型。
2.super:在继承层次结构中,指向当前合约的直接父类合约。
3.selfdestruct(address payable recipient):销毁当前合约,并将其余额转账到指定地址,结束合约的执行。selfdestruct 继承自 EVM,并具有以下特性:
-
接收合约的 receive 函数不会被调用。
-
合约在交易结束时被实际销毁,并且回滚操作可能会撤销销毁。
-
当前合约的所有函数(包括 selfdestruct 本身)仍然可以直接调用。
注意:
1.从 EVM >= Cancun 开始,selfdestruct 将只会将合约中的所有以太币发送到指定的接收地址,而不再销毁合约本身。然而,当 selfdestruct 在同一交易中被调用,并且创建了调用它的合约时,合约会依旧销毁,包括存储键、代码和合约本身,这一行为与 EVM <= Shanghai 时的行为一致。详情请参见 EIP-6780。
2.新的行为是全网范围的变化,影响所有部署在以太坊主网和测试网的合约。需要注意的是,这一变化取决于合约部署时的 EVM 版本,而编译时使用的 --evm-version 设置不会影响此行为。
3.此外,selfdestruct 操作码在 Solidity 版本 0.8.18 中已被弃用,依据 EIP-6049 的建议,编译器将发出警告。在新的合约中强烈不建议使用 selfdestruct,即便考虑到新的行为,未来的 EVM 更改可能会进一步减少其功能。
4.在 Solidity 0.5.0 版本之前,存在一个名为 suicide 的函数,其语义与 selfdestruct 相同。
9. 类型信息
type(X) 表达式可用于检索与类型 X 相关的信息。当前,支持此功能的类型有限(例如,X 可以是合约类型或整数类型),但未来可能会扩展。
对于合约类型 C,可用的属性如下:
-
type©.name:返回合约的名称。
-
type©.creationCode:返回包含合约创建字节码的内存字节数组。该字节码可以用于在内联汇编中构建自定义创建逻辑,尤其是在使用 create2 操作码时。需要注意的是,该属性不能在合约本身或其任何派生合约中访问,因为这会导致字节码被包含在调用站点的字节码中,从而无法处理循环引用。
-
type©.runtimeCode:返回包含合约运行时字节码的内存字节数组。这通常是由合约 C 的构造函数部署的代码。如果构造函数使用了内联汇编,则这可能与实际部署的字节码不同。另需注意,库合约在部署时会修改其运行时字节码,以防止常规调用。与 creationCode 相同,runtimeCode 也受相同的限制。
对于接口类型 I,可用的属性为 type(I).interfaceId:返回一个 bytes4 类型的值,表示接口 I 的 EIP-165 接口标识符。该标识符是接口中所有函数选择器的 XOR 值,排除了所有继承的函数。
对于整数类型 T,可用的属性如下:
- type(T).min:返回类型 T 可表示的最小值。
- type(T).max:返回类型 T 可表示的最大值。
2396

被折叠的 条评论
为什么被折叠?



