基本类型
布尔类型
布尔类型取值只能为 true
或 false
。支持常见的逻辑操作符:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.24;
contract ValueTypeBoolean {
function boolType() private pure {
bool True = true;
bool False = false;
// ! 取反操作
assert(!False); // 断言成功
assert(!True); // 断言失败
// && 与操作
assert(True && False); // 断言失败
// || 或操作
assert(True || False); // 断言成功
// == 比较相等
assert(True == False); // 断言失败
// != 比较不等
assert(True != False); // 断言成功
}
}
&&
与 ||
两种操作支持提前短路:即 a && b
,如果 a 为假,则 b 不再处理。对 a || b
,如果 a
为真,则 b
不再处理。
整数类型
Solidity
中一个字为 256
位。因此,带符号整数 int
与无符号整数 uint
默认为 256 位,即 int
与 int256
,uint
和 uint256
是等价的。
除了上面四种类型,Solidity
还支持 intN
, uintN
,其中 ,且 N 为 8 的整数倍。
整数比较
支持常见的比较符:
- 不等:!=
- 相等:==
- 小于:<
- 大于:>
- 小于等于:<=
- 大于等于:>=
判断结果为一个布尔型。
四则运算
支持四则运算:
- 加法:+
- 减法:-
- 乘法:*
- 除法:/
需要注意,整数相除结果仍为整数,向0取整。
取值范围与溢出检查
要获取某一种类型的最大值或最小值,使用 type(T).min
或 type(T).max
。
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract ValueTypeIntegerRange {
function range() public pure returns (int8, int8) {
return (type(int8).min, type(int8).max);
}
}
以上 range
函数将返回:(-128, 127)
,分别为 8 位补码整数最小值及最大值。
当数值运算过程中出现溢出,Solidity
默认会进行检查,从而导致失败错误。这是因为在智能合约中,数值溢出,用户可能会蒙受极大损失。例如,一个 uint8
变量由 255 加 1 后变为了 0,这样可能会清空用户的数字资产。
如果允许溢出,则需要使用 unchecked {}
将代码显式包裹起来:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract ValueTypeUnchecked {
function defaultChecked() public pure returns (uint8) {
return uint8(0) - 1;
}
function uncheckedWrap() public pure returns (uint8) {
unchecked {
return uint8(0) - 1;
}
}
}
调用以上 defaultChecked
提示:
call to ValueTypeUnchecked.defaultChecked errored: Error occurred: revert.
revert
The transaction has been reverted to the initial state.
由于发生了错误,整个调用被撤销,revert
(恢复)到原来的状态。
调用 uncheckedWrap
,返回 255
。
由于有符号数使用补码,补码的负数取值范围比正数取值范围大1,如 uint8 取值范围为 。因此对 -type(uint8).min
也会导致溢出错误,因为 128 无法保存在 uint8
类型中:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract ValueTypeSignedRange {
function defaultChecked() public pure returns (int8) {
return -type(int8).min;
}
function uncheckedWrap() public pure returns (int8) {
unchecked {
return -type(int8).min;
}
}
}
执行上面的 defaultChecked
将导致溢出错误,交易被撤销;执行 uncheckedWrap
会发现,-type(int8).min
结果仍是原值 -128
。
type(int8).min / (-1)
也会导致以上情况,读者可自行验证。
求余操作
求余操作,如 a % b
,等价于 a - int(a / b) * b
。其中 int(a / b)
表示对 a / b
取整,根据上面提及的整数除法规则,向 0 取整:
int(5) % int(2) == int(1)
:int(5) % int(-2) == int(1)
:int(-5) % int(2) == int(-1)
:int(-5) % int(-2) == int(-1)
:
求幂
求x的n次幂:x**n
。定义 0 的 0 次幂为 1。
位操作
支持一般的位操作:
- 取反:~
- 位与:&
- 位或:|
- 异或:^
- 左移:<<
- 右移:>>
有符号数使用二进制补码表示,且左、右移不导致溢出:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract ValueTypeBitOps {
function bitOps() public pure returns (int8, uint8, uint8, uint8, uint8, uint8, uint8) {
return (
~int8(0), // ~(00000000) = 11111111 = -1
~uint8(0), // ~(00000000) = 11111111 = 255
uint8(0xff) & uint8(0), // 11111111 & 00000000 = 00000000 = 0
uint8(0xff) | uint8(0), // 11111111 | 00000000 = 11111111 = 255
uint8(0xf0) ^ uint8(0), // 11110000 ^ 00000000 = 11110000 = 240
uint8(1) << 1, // 00000001 << 1 = 00000010 = 2
uint8(1) >> 1 // 00000001 >> 1 = 00000000 = 0 ,不导致溢出
);
}
}
运行 bitOps
将返回:-1, 255, 0, 255, 240, 2, 0
。
定点小数
Solidity 定点小数关键字有 fixed/ufixed
,它们表示有符号定点小数和无符号定点小数。
fixed/ufixed
是 fixed128x18/ufixed128x18
的等价表示,指该定点小数占用128位,其中18位用于表示小数部分。
定点小数类型可以写为 fixedMxN/ufixedMxN
,其中 ,其中 必须为 8 的整数倍。
但是,Solidity 并没有完全支持定点小数。因此如果需要用到小数,需要寻找第三方库使用。
地址类型
使用关键字 address
用于声明一个地址。一个以太坊地址占用 20 字节。因此长度同样为 20 字节的 uint160/bytes20
可以转换为 address
类型。
如果是超过 20 字节的类型转换为 address
,则会发生截断。
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract ValueTypeAddress {
function addressConvert() public pure returns (address, address) {
uint a = 0x111122223333444455556666777788889999AAAABBBBCCCCDDDDEEEEFFFFCCCC;
bytes32 b = bytes32(0x111122223333444455556666777788889999AAAABBBBCCCCDDDDEEEEFFFFCCCC);
return (
address(uint160(a)), // 0x777788889999AaAAbBbbCcccddDdeeeEfFFfCcCc
address(bytes20(b)) // 0x111122223333444455556666777788889999aAaa
);
}
}
以上 uint256 -> uint160
截断为数字截断,保留数字低位部分。
对 bytes32 -> bytes20
截断为字节数组截断,保留数组低地址数据(数组左边数据)。
除了 uint160
以及 bytes20
,合约类型(contract) 也可以被显式转换为地址。转换后的地址即是合约部署的地址。
可支付地址(payable address)
由 payable
修饰的 address
是可支付地址。表明可以发送以太币到该地址。可支付地址具有 transfer
和 send
方法。您可以调用这两个方法来发送以太币。
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract ValueTypePayableAddress {
function sendCoin() public payable {
address payable addr = payable(
0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2
);
addr.transfer(msg.value);
}
}
如果一个合约中具有 receive
函数,或者具有一个 payable
修饰的 fallback
函数,那么该合约可以通过它的地址接受以太币。这类合约可以被显式转换为可支付地址。
实际上,即使合约没有实现 receive
和 fallback
函数,你也可以使用 payable
将合约强制转换为可支付地址,但向该合约发送以太币将触发错误。
可支付地址相比一般地址,是一种 “更严格”
的地址。可支付的地址可以隐式转换为可支付地址,但是一般地址要转换为可支付地址则必须使用 payable
显式转换。