(五)字面量和内置单位、函数
字面常量
地址
地址的字面常量有 EIP-55 的标准(区分大写字母和小写字母来验证),只有经过校验后才能作为 address 变量。
有理数和整数
十进制的小数字面常量都会带一个.比如1. .1 1.2。。.5*8的结果是整型的4。
也支持科学计数法,但是指数部分需要是整数(防止无理数出现)。2e-10
为了提高可读性,数字之间可以添加下划线,编译器会自动忽略。但是下划线不允许连续出现,而且只能出现在数字之间。1_2e345_678
数值字面常量支持任意精度和长度,也支持对应类型的所有运算,字面常量的运算结果还是字面常量。但是如果出现在变量表达式就会发生隐式转化,并且不同类型的字面常量和变量运算不能通过编译。也就是说2**800+1-2**800在字面常量中是允许的
(在0.4.0之前,整数的字面常量会被截断即5/2=2,但是之后是2.5)
字面类型的运算还是字面常量,和非字面常量运算,就会隐式转化成普通类型
字符串
字符串字面常量("foo"或者’bar’这样),可以分段写("foo""bar"等效为"foobar")
字符串"foo"相当于 3 个字节,而不是 4 个字节,它不像 C 语言里以\0结尾。
字符串字面常量可以隐式的转换成bytes1,…bytes32。在合适的情况下,可以转换成bytes以及string
字符串字面常量只包含可打印的 ASCII 字符和下面的转义字符:
\<newline>(转义实际换行)\\(反斜杠)\'(单引号)\"(双引号)\b(退格)\f(换页)\n(换行符)\r(回车)\t(标签 tab)\v(垂直标签)\xNN(十六进制转义,表示一个十六进制的值,)\uNNNN(unicode 转义,转换成 UTF-8 的序列)
十六进制
十六进制字面常量以关键字 hex 打头,后面紧跟着用单引号或双引号引起来的字符串(例如,hex"001122FF" )。 字符串的内容必须是一个十六进制的字符串,它们的值将使用二进制表示。
用空格分隔的多个十六进制字面常量被合并为一个字面常量: hex"00112233" hex"44556677" 等同于 hex"0011223344556677"
内置函数和变量
单位
- 币的单位默认是
wei,也可以添加单位。
1 | 1 wei == 1; |
- 时间单位,默认是秒。但是需要注意闰秒和闰年的影响,这里的统计的时间并不是完全和日历上的时间相同。
1 | 1 == 1 seconds` |
区块和交易的属性
括号内表示返回值类型
blockhash(uint blockNumber) returns (bytes32):指定区块的区块哈希,但是仅可用于最新的 256 个区块且不包括当前区块,否则返回 0.block.chainid(uint): 当前链 idblock.coinbase(address): 挖出当前区块的矿工地址block.difficulty(uint): 当前区块难度block.gaslimit(uint): 当前区块 gas 限额block.number(uint): 当前区块号block.timestamp(uint): 自 unix epoch 起始当前区块以秒计的时间戳gasleft() returns (uint256):剩余的 gasmsg.data(bytes): 完整的 calldatamsg.sender(address): 消息发送者(当前调用)msg.sig(bytes4): calldata 的前 4 字节(也就是函数标识符)msg.value(uint): 随消息发送的 wei 的数量tx.gasprice(uint): 交易的 gas 价格tx.origin(address payable): 交易发起者(完全的调用链)
注意几大变化:
gasleft原来是msg.gasblock.timestamp原来是nowblockhash原来是block.blockhash
delete
delete a不是常规意义上的删除,而是给a赋默认值(即返回不带参数的声明的状态),比如a是整数,那么等同于a=0。对用动态数组是将数组长度变为 0;对于静态数组是将每一个元素初始化;对于结构体就把每一个成员初始化;对于映射在原理上无效(不会影响映射关系),但是会删除其他的成员,如值。
1 | // SPDX-License-Identifier: GPL-3.0 |
ABI 编码及解码函数
详细原理和应用见下一节 应用二进制接口,需要明白 ABI 编码的含义才懂这些函数的用法。
abi.decode(bytes memory encodedData, (...)) returns (...): 对给定的数据进行 ABI 解码,而数据的类型在括号中第二个参数给出 。 例如:(uint a, uint[2] memory b, bytes memory c) = abi.decode(data, (uint, uint[2], bytes))abi.encode(...) returns (bytes): 对给定参数进行编码
例如:
1 | // SPDX-License-Identifier: GPL-3.0 |
abi.encodePacked(...) returns (bytes):对给定参数执行 紧打包编码 (即编码时不够 32 字节,不用补 0 了。abi.encodeWithSelector(bytes4 selector, ...) returns (bytes): ABI - 对给定第二个开始的参数进行编码,并以给定的函数选择器作为起始的 4 字节数据一起返回abi.encodeWithSignature(string signature, ...) returns (bytes):等价于abi.encodeWithSelector(bytes4(keccak256(signature), ...)
用法如下:
1 | // SPDX-License-Identifier: GPL-3.0 |
错误处理
assert(bool condition),require(bool condition),require(bool condition, string memory message)均是条件为假然后回滚。
revert(),revert(string memory reason) 立即回滚。
数学和密码学函数
addmod(uint x, uint y, uint k) returns (uint): 计算(x + y) % k,加法会在任意精度下执行,并且加法的结果即使超过2**256也不会被截取。从 0.5.0 版本的编译器开始会加入对k != 0的校验(assert)。mulmod(uint x, uint y, uint k) returns (uint): 计算(x * y) % k,乘法会在任意精度下执行,并且乘法的结果即使超过2**256也不会被截取。从 0.5.0 版本的编译器开始会加入对k != 0的校验(assert)。keccak256((bytes memory) returns (bytes32): 计算 Keccak-256 哈希。0.5.0 以前有别名sha3.它一般用于:生成输入信息的独一无二的标识。
例如:函数选择器即函数签名
1 | pragma solidity >=0.5.0 <0.9.0; |
sha256(bytes memory) returns (bytes32): 计算参数的 SHA-256 哈希。ripemd160(bytes memory) returns (bytes20): 计算参数的 RIPEMD-160 哈希。ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)利用椭圆曲线签名恢复与公钥相关的地址。
函数参数对应于 ECDSA 签名的值:
r = 签名的前 32 字节
s = 签名的第 2 个 32 字节
v = 签名的最后一个字节
(以后还需要补充许多密码学知识)
合约相关
this:表示当前合约的实例。
selfdestruct(address payable recipient) :在交易成功结束后,销毁合约,并且把余额转到指定地址。接受的合约不会运行。
类型信息
type(X) 返回X的类型,目前只支持整型和合约类型,未来计划会拓展。
用于合约类型 C 支持以下属性:
-
type(C).name:获得合约名
-
type(C).creationCode:获得包含创建合约的字节码的
memory byte[]数组。只能在内联汇编中使用。 -
type(C).runtimeCode获得合同的运行时字节码的内存字节数组,通常在构造函数的内联汇编中使用。
在接口类型I下可使用:
-
type(I).interfaceId:返回接口
I的bytes4类型的接口 ID,接口 ID 参考: EIP-165 定义的, 接口 ID 被定义为 接口内所有的函数的函数选择器(除继承的函数)的XOR(异或)。
对于整型 T 有下面的属性可访问:
-
type(T).minT的最小值。 -
type(T).maxT的最大值。






