Solidity 值类型综合详解
本文详细讲解 Solidity 中的各种值类型,包含完整定义、特性、边界、常见操作及最佳实践,并提供丰富的实例代码。
目录
- 布尔型 (Boolean)
- 整型 (Integers)
- 定点数 (Fixed Point Numbers)
- 地址类型 (Address)
- 定长字节数组 (Fixed-size Byte Arrays)
- 枚举类型 (Enumerations)
- 函数类型 (Function Types)
- 值类型之间的类型转换
- 常量和不可变量
- 值类型的存储与内存操作
- 最佳实践与陷阱
1. 布尔型 (Boolean)
定义与特性
- 关键字:
bool
- 可能的值:
true
或false
- 默认值:
false
- 占用 1 个字节,但在 EVM 中作为 32 字节的字被处理
布尔操作符
!
(逻辑非)&&
(逻辑与)||
(逻辑或)==
(等于)!=
(不等于)
实例代码
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract BooleanExample {
bool public isActive;
bool public isFinished = true;
function logicalOperations(bool _a, bool _b) public pure returns (bool, bool, bool) {
bool and = _a && _b;
bool or = _a || _b;
bool not = !_a;
return (and, or, not);
}
function compareValues(bool _a, bool _b) public pure returns (bool, bool) {
bool isEqual = _a == _b;
bool isNotEqual = _a != _b;
return (isEqual, isNotEqual);
}
function shortCircuitEvaluation(bool _a) public pure returns (bool) {
// 短路求值示例:如果 _a 为 false,第二个条件不会被计算
bool result = _a && complexOperation();
return result;
}
function complexOperation() public pure returns (bool) {
// 模拟复杂操作
return true;
}
function toggle() public {
isActive = !isActive;
}
}
布尔型的优化使用
在存储结构体和数组时,多个布尔值可以通过打包来节省 gas:
solidity
// 高效打包多个布尔值
contract EfficientBooleanPacking {
struct UserFlags {
bool isActive;
bool isVerified;
bool hasSpecialPermission;
bool isAdmin;
// Solidity 自动将这些布尔值打包到一个存储槽中
}
UserFlags public userFlags;
function setFlags(
bool _isActive,
bool _isVerified,
bool _hasSpecialPermission,
bool _isAdmin
) public {
userFlags.isActive = _isActive;
userFlags.isVerified = _isVerified;
userFlags.hasSpecialPermission = _hasSpecialPermission;
userFlags.isAdmin = _isAdmin;
}
}
2. 整型 (Integers)
定义与特性
Solidity 提供有符号和无符号整数,从 8 位到 256 位不等。
- 无符号整数:
uint8
,uint16
,uint24
, ...,uint256
(或简写uint
) - 有符号整数:
int8
,int16
,int24
, ...,int256
(或简写int
) - 默认值:
0
uint
和int
分别是uint256
和int256
的别名
整数范围
uint8
: 0 到 2^8-1 (0 到 255)uint16
: 0 到 2^16-1 (0 到 65,535)uint256
: 0 到 2^256-1 (约 1.16 * 10^77)int8
: -2^7 到 2^7-1 (-128 到 127)int16
: -2^15 到 2^15-1 (-32,768 到 32,767)int256
: -2^255 到 2^255-1 (约 -5.8 * 10^76 到 5.8 * 10^76)
算术操作符
- 加法:
+
- 减法:
-
- 乘法:
*
- 除法:
/
- 取模:
%
- 幂运算:
**
位运算符
- 位与:
&
- 位或:
|
- 位异或:
^
- 位非:
~
- 左移:
<<
- 右移:
>>
比较运算符
- 大于:
>
- 小于:
<
- 大于等于:
>=
- 小于等于:
<=
- 等于:
==
- 不等于:
!=
实例代码
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract IntegerExample {
// 整数声明
uint8 public smallNumber;
uint public regularNumber = 123;
int public signedNumber = -456;
uint256 public largeNumber = 2**200; // 大约等于 1.6 * 10^60
// 数学运算
function performArithmetic(uint _a, uint _b) public pure returns (
uint addition,
uint subtraction,
uint multiplication,
uint division,
uint modulo,
uint exponentiation
) {
addition = _a + _b;
subtraction = _a - _b; // 如果 _b > _a,将在 0.8.0+ 版本中回滚
multiplication = _a * _b;
division = _a / _b; // 注意:整数除法会截断(向下取整),_b 不能为 0
modulo = _a % _b; // _b 不能为 0
exponentiation = _a ** 3; // 幂运算,小心溢出
return (addition, subtraction, multiplication, division, modulo, exponentiation);
}
// 位运算
function performBitwise(uint8 _a, uint8 _b) public pure returns (
uint8 bitwiseAnd,
uint8 bitwiseOr,
uint8 bitwiseXor,
uint8 bitwiseNot,
uint8 leftShift,
uint8 rightShift
) {
bitwiseAnd = _a & _b;
bitwiseOr = _a | _b;
bitwiseXor = _a ^ _b;
bitwiseNot = ~_a;
leftShift = _a << 2; // 左移 2 位,相当于乘以 4
rightShift = _a >> 2; // 右移 2 位,相当于除以 4
return (bitwiseAnd, bitwiseOr, bitwiseXor, bitwiseNot, leftShift, rightShift);
}
// 递增和递减
function incrementDecrement(uint _value) public pure returns (uint, uint) {
uint incremented = _value + 1;
uint decremented = _value - 1; // 如果 _value 为 0,将在 0.8.0+ 版本中回滚
return (incremented, decremented);
}
// 下溢和上溢检查(Solidity 0.8.0+ 会自动检查)
function overflowExample() public pure returns (uint8) {
uint8 max = 255;
// 在 0.8.0 之前,这会导致无声的溢出,结果为 0
// 在 0.8.0+ 中,这会导致交易回滚
return max + 1;
}
function underflowExample() public pure returns (uint8) {
uint8 min = 0;
// 在 0.8.0 之前,这会导致无声的下溢,结果为 255
// 在 0.8.0+ 中,这会导致交易回滚
return min - 1;
}
// 使用 unchecked 绕过溢出/下溢检查(仅 0.8.0+)
function uncheckedExample() public pure returns (uint8) {
uint8 max = 255;
unchecked {
return max + 1; // 这会导致溢出,结果为 0,但不会回滚
}
}
// 常见的整数使用模式
function calculateAverage(uint[] memory values) public pure returns (uint) {
uint sum = 0;
for (uint i = 0; i < values.length; i++) {
sum += values[i];
}
// 在 Solidity 中,整数除法会向下取整
return values.length > 0 ? sum / values.length : 0;
}
}
整数边界情况处理
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract IntegerEdgeCases {
// 安全地处理整数除法(防止除以零错误)
function safeDivision(uint _a, uint _b) public pure returns (uint, bool) {
if (_b == 0) {
return (0, false);
}
return (_a / _b, true);
}
// 检查加法是否会溢出(手动检查,适用于 0.8.0 之前的版本)
function safeAdd(uint256 _a, uint256 _b) public pure returns (uint256, bool) {
uint256 result = _a + _b;
bool overflowed = result < _a; // 如果结果小于其中一个操作数,说明发生了溢出
return (result, !overflowed);
}
// 检测最大最小值
function checkBoundaries() public pure returns (uint, uint, int, int) {
uint maxUint = type(uint).max; // 2^256 - 1
uint minUint = type(uint).min; // 0
int maxInt = type(int).max; // 2^255 - 1
int minInt = type(int).min; // -2^255
return (maxUint, minUint, maxInt, minInt);
}
// 检测特定位宽的最大最小值
function checkSpecificSizes() public pure returns (uint8, int16, uint32) {
uint8 maxUint8 = type(uint8).max; // 255
int16 minInt16 = type(int16).min; // -32,768
uint32 maxUint32 = type(uint32).max; // 4,294,967,295
return (maxUint8, minInt16, maxUint32);
}
// 整数舍入
function roundUp(uint _a, uint _b) public pure returns (uint) {
require(_b > 0, "Divisor cannot be zero");
// 向上取整除法: (a + b - 1) / b
return (_a + _b - 1) / _b;
}
// 计算百分比
function calculatePercentage(uint _value, uint _percentage) public pure returns (uint) {
require(_percentage <= 100, "Percentage must be <= 100");
// 避免小数点后的精度损失,先乘以再除以
return (_value * _percentage) / 100;
}
// 在合约中存储和使用大整数
uint public immutable MAX_SUPPLY = 21_000_000 * 10**18; // 2100万,带有18位小数(常用于代币)
// 整数排序和过滤的例子
function findMax(uint[] memory values) public pure returns (uint) {
require(values.length > 0, "Array must not be empty");
uint max = values[0];
for (uint i = 1; i < values.length; i++) {
if (values[i] > max) {
max = values[i];
}
}
return max;
}
}
3. 定点数 (Fixed Point Numbers)
定义与特性
Solidity 中的定点数类型目前还未完全实现,虽然保留了关键字,但无法在当前版本使用。开发者通常使用整数来模拟定点数。
- 未实现的定点数类型格式:
fixed
/ufixed
- 使用整数模拟:通常用 uint 乘以 10^n 来表示带小数点的数值
模拟定点数
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract FixedPointExample {
// 使用整数来模拟定点数
// 例如,用 uint256 表示带 18 位小数的数字
uint256 constant DECIMALS = 18;
uint256 constant DECIMAL_FACTOR = 10**DECIMALS; // 10^18
// 存储 5.25 (实际存储为 5.25 * 10^18 = 5,250,000,000,000,000,000)
uint256 public fixedPointValue = 5.25 * DECIMAL_FACTOR;
// 定点数加法
function add(uint256 _a, uint256 _b) public pure returns (uint256) {
return _a + _b;
}
// 定点数减法
function subtract(uint256 _a, uint256 _b) public pure returns (uint256) {
require(_a >= _b, "Result would be negative");
return _a - _b;
}
// 定点数乘法(需要注意溢出)
function multiply(uint256 _a, uint256 _b) public pure returns (uint256) {
// 先执行乘法,然后除以 10^18 以保持小数精度
return (_a * _b) / DECIMAL_FACTOR;
}
// 定点数除法
function divide(uint256 _a, uint256 _b) public pure returns (uint256) {
require(_b > 0, "Division by zero");
// 先乘以 10^18 以保持小数精度,然后执行除法
return (_a * DECIMAL_FACTOR) / _b;
}
// 示例:计算 50% 的值
function calculatePercentage(uint256 _value, uint256 _percentage) public pure returns (uint256) {
// _percentage 应该是一个带 18 位小数的数字
// 例如,50% 应表示为 0.5 * 10^18 = 500,000,000,000,000,000
return multiply(_value, _percentage);
}
// 将 2 位小数的数字转换为内部表示
function convertFromHuman(uint256 _value) public pure returns (uint256) {
// 例如,输入 525 表示 5.25
return _value * DECIMAL_FACTOR / 100;
}
// 将内部表示转换为 2 位小数的人类可读形式
function convertToHuman(uint256 _value) public pure returns (uint256) {
// 例如,输出 525 表示 5.25
return (_value * 100) / DECIMAL_FACTOR;
}
}
使用库处理定点数
可以使用第三方库如 PRBMath
或 OpenZeppelin 的 SafeMath
来处理定点数计算:
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
// 简单的定点数数学库
library FixedPointMath {
uint256 constant DECIMALS = 18;
uint256 constant DECIMAL_FACTOR = 10**DECIMALS; // 10^18
function add(uint256 a, uint256 b) internal pure returns (uint256) {
return a + b;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return a - b;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
return (a * b) / DECIMAL_FACTOR;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "Division by zero");
return (a * DECIMAL_FACTOR) / b;
}
// 将十进制数转换为固定点表示
// 例如,fromDecimals(5, 2) 将 5.00 转换为固定点表示
function fromDecimals(uint256 value, uint8 decimals) internal pure returns (uint256) {
require(decimals <= DECIMALS, "Too many decimals");
uint256 adjuster = 10**(DECIMALS - decimals);
return value * adjuster;
}
// 将固定点表示转换为具有特定小数位数的数字
// 例如,toDecimals(5 * DECIMAL_FACTOR, 2) 将固定点表示转换为 500(表示 5.00)
function toDecimals(uint256 value, uint8 decimals) internal pure returns (uint256) {
require(decimals <= DECIMALS, "Too many decimals");
uint256 adjuster = 10**(DECIMALS - decimals);
return value / adjuster;
}
}
contract FixedPointLibraryExample {
using FixedPointMath for uint256;
// 以 18 位小数表示的数量
uint256 public price = 5 * 10**18; // 5.0
uint256 public quantity = 2 * 10**18; // 2.0
function calculateTotal() public view returns (uint256) {
// 计算 price * quantity
return price.mul(quantity);
}
function calculateShare(uint256 total, uint256 percentage) public pure returns (uint256) {
// percentage 以 18 位小数表示,例如 50% = 0.5 * 10^18
uint256 sharePercentage = FixedPointMath.fromDecimals(percentage, 2);
return total.mul(sharePercentage);
}
function convertToReadablePrice(uint256 _price) public pure returns (uint256) {
// 将价格从内部表示(18 位小数)转换为 2 位小数的可读形式
return FixedPointMath.toDecimals(_price, 2);
}
}
4. 地址类型 (Address)
定义与特性
Solidity 提供地址类型用于存储以太坊地址。地址类型有两种变体:
address
:20 字节的以太坊地址address payable
:与address
相同,但有额外的transfer
和send
成员函数
地址成员
balance
: 查询地址的余额(以 wei 为单位)code
: 获取地址存储的代码codehash
: 获取地址代码的哈希值transfer()
: 向地址发送 ETH(会自动转发 2300 gas,失败时会回滚)send()
: 向地址发送 ETH(会自动转发 2300 gas,失败时返回 false)call()
: 低级函数调用,返回是否成功以及任何返回数据delegatecall()
: 低级函数调用,使用调用合约的存储、当前地址和余额staticcall()
: 低级函数调用,不允许状态修改
实例代码
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract AddressExample {
// 地址声明
address public owner;
address payable public treasury;
constructor() {
owner = msg.sender;
treasury = payable(msg.sender); // 将普通地址转换为 address payable
}
// 检查地址是否为合约
function isContract(address _addr) public view returns (bool) {
uint256 codeSize;
assembly {
codeSize := extcodesize(_addr)
}
return codeSize > 0;
}
// 地址余额查询
function getBalance(address _addr) public view returns (uint256) {
return _addr.balance;
}
// 比较地址
function compareAddresses(address _addr1, address _addr2) public pure returns (bool) {
return _addr1 == _addr2;
}
// 使用 transfer 发送 ETH(接收者有 2300 gas 限制)
function sendEtherWithTransfer(address payable _to, uint256 _amount) public {
// 会自动转发 2300 gas,失败时会回滚
_to.transfer(_amount);
}
// 使用 send 发送 ETH(接收者有 2300 gas 限制)
function sendEtherWithSend(address payable _to, uint256 _amount) public returns (bool) {
// 会自动转发 2300 gas,失败时返回 false
return _to.send(_amount);
}
// 使用 call 发送 ETH(灵活的 gas 限制)
function sendEtherWithCall(address payable _to, uint256 _amount) public returns (bool) {
// 可以传递自定义 gas 值,推荐使用的方式
(bool success, ) = _to.call{value: _amount}("");
return success;
}
// 调用其他合约的函数
function callExternalFunction(address _contract, bytes memory _data) public returns (bool, bytes memory) {
// 调用其他合约的函数(低级调用)
(bool success, bytes memory returnData) = _contract.call(_data);
return (success, returnData);
}
// 使用 staticcall 进行只读调用
function staticCallExternalFunction(address _contract, bytes memory _data) public view returns (bool, bytes memory) {
// 调用其他合约的函数(只读调用)
(bool success, bytes memory returnData) = _contract.staticcall(_data);
return (success, returnData);
}
// 使用 delegatecall 调用其他合约的函数(保留 msg.sender 和当前合约上下文)
function delegateCallFunction(address _contract, bytes memory _data) public returns (bool, bytes memory) {
// 使用其他合约的代码,但使用当前合约的存储
(bool success, bytes memory returnData) = _contract.delegatecall(_data);
return (success, returnData);
}
// 接收 ETH
receive() external payable {}
// 从合约中提取 ETH
function withdrawEther(uint256 _amount) public {
require(msg.sender == owner, "Only owner can withdraw");
treasury.transfer(_amount);
}
// 将普通地址转换为 address payable
function convertToPayable(address _addr) public pure returns (address payable) {
return payable(_addr);
}
// 将合约地址转换为接口类型(Type Casting)
function castToInterface(address _contractAddress) public view returns (bool) {
// 假设 _contractAddress 实现了 IERC20 接口
IERC20 token = IERC20(_contractAddress);
// 调用接口方法
uint256 totalSupply = token.totalSupply();
return totalSupply > 0;
}
}
// ERC20 接口定义(用于上面的例子)
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
}
安全使用地址的最佳实践
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract AddressSafetyExample {
address public immutable owner;
constructor() {
owner = msg.sender;
}
// 零地址检查
function setRecipient(address _recipient) public pure returns (address) {
require(_recipient != address(0), "Cannot use zero address");
return _recipient;
}
// 安全转账模式
function safeTransferETH(address payable _to, uint256 _amount) public returns (bool) {
require(_to != address(0), "Cannot transfer to zero address");
require(address(this).balance >= _amount, "Insufficient balance");
(bool success, ) = _to.call{value: _amount}("");
require(success, "ETH transfer failed");
return true;
}
// 检查钱包地址格式
function isValidAddress(address _addr) public pure returns (bool) {
// 地址不能为零,且长度必须为 20 字节(160 位)
// Solidity 类型系统已经确保了长度为 20 字节,所以我们只需检查非零
return _addr != address(0);
}
// 检查 EOA vs 合约地址
function isExternallyOwnedAccount(address _addr) public view returns (bool) {
uint256 codeSize;
assembly {
codeSize := extcodesize(_addr)
}
// EOA 的代码大小应该为 0
return codeSize == 0;
}
// 安全调用合约
function safeContractCall(address _contract, bytes memory _data) public returns (bool, bytes memory) {
require(_contract != address(0), "Invalid contract address");
require(isContract(_contract), "Target is not a contract");
(bool success, bytes memory returnData) = _contract.call(_data);
if (!success) {
// 如果调用失败并返回错误信息(错误信息通常是以 revert 原因字符串格式编码的)
if (returnData.length > 0) {
// 提取错误信息并重新抛出
assembly {
let returnDataSize := mload(returnData)
revert(add(32, returnData), returnDataSize)
}
} else {
revert("Contract call failed without error");
}
}
return (success, returnData);
}
// 辅助函数:检查地址是否为合约
function isContract(address _addr) public view returns (bool) {
uint256 codeSize;
assembly {
codeSize := extcodesize(_addr)
}
return codeSize > 0;
}
// 防止可重入攻击的 ETH 发送
bool private locked;
modifier nonReentrant() {
require(!locked, "Reentrant call");
locked = true;
_;
locked = false;
}
function sendEtherSafely(address payable _recipient, uint256 _amount) public nonReentrant {
require(_recipient != address(0), "Invalid recipient");
(bool success, ) = _recipient.call{value: _amount}("");
require(success, "ETH transfer failed");
}
// 接收 ETH
receive() external payable {}
}
5. 定长字节数组 (Fixed-size Byte Arrays)
定义与特性
Solidity 提供两种处理字节数据的类型:
- 固定大小的字节数组:
bytes1
,bytes2
,bytes3
, ...,bytes32
- 可变大小的字节数组:
bytes
和string
(这些是引用类型,不是值类型)
本节关注固定大小的字节数组(值类型)。
操作与方法
- 比较运算符:
==
,!=
- 位运算符:
&
,|
,^
,~
,<<
,>>
- 索引访问(只读):
bytesN[k]
返回第 k 个字节(0 索引)
实例代码
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract FixedBytesExample {
// 固定大小字节数组声明
bytes1 public singleByte = 0xa1; // 1 字节,存储 0xa1
bytes2 public twoBytes = 0xa1b2; // 2 字节,存储 0xa1b2
bytes16 public sixteenBytes = 0xa1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6; // 16 字节
bytes32 public hash = keccak256("Hello"); // 32 字节,存储哈希值
// 将地址转换为 bytes20
function addressToBytes20(address _addr) public pure returns (bytes20) {
return bytes20(_addr);
}
// 将 bytes32 转换为地址
function bytes32ToAddress(bytes32 _bytes) public pure returns (address) {
return address(uint160(uint256(_bytes)));
}
// 比较两个 bytes32 值
function compareBytes(bytes32 _a, bytes32 _b) public pure returns (bool) {
return _a == _b;
}
// 位运算示例
function bitwiseOperations(bytes2 _a, bytes2 _b) public pure returns (
bytes2 bitwiseAnd,
bytes2 bitwiseOr,
bytes2 bitwiseXor,
bytes2 bitwiseNot,
bytes2 leftShift,
bytes2 rightShift
) {
bitwiseAnd = _a & _b;
bitwiseOr = _a | _b;
bitwiseXor = _a ^ _b;
bitwiseNot = ~_a;
leftShift = _a << 4; // 左移 4 位
rightShift = _a >> 4; // 右移 4 位
return (bitwiseAnd, bitwiseOr, bitwiseXor, bitwiseNot, leftShift, rightShift);
}
// 访问 bytes32 中的单个字节
function getByteAt(bytes32 _value, uint8 _index) public pure returns (bytes1) {
require(_index < 32, "Index out of bounds");
return _value[_index];
}
// 将 bytes32 转换为 uint256
function bytes32ToUint(bytes32 _value) public pure returns (uint256) {
return uint256(_value);
}
// 将字节数组拼接在一起
function concatenateBytes() public pure returns (bytes memory) {
bytes1 a = 0xa1;
bytes1 b = 0xb2;
bytes1 c = 0xc3;
// 将单个字节拼接到动态字节数组中
bytes memory result = new bytes(3);
result[0] = a;
result[1] = b;
result[2] = c;
return result;
}
// 使用字节作为标志或状态标识符
bytes1 private flags = 0x00; // 每一位表示一个标志
// 设置标志位
function setFlag(uint8 _position, bool _value) public {
require(_position < 8, "Position out of bounds");
if (_value) {
// 设置指定位为 1
flags |= bytes1(uint8(1 << _position));
} else {
// 设置指定位为 0
flags &= ~bytes1(uint8(1 << _position));
}
}
// 获取标志位
function getFlag(uint8 _position) public view returns (bool) {
require(_position < 8, "Position out of bounds");
return (uint8(flags) & (1 << _position)) != 0;
}
// 获取当前所有标志
function getFlags() public view returns (bytes1) {
return flags;
}
}
高级字节操作
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract AdvancedBytesExample {
// 构建字节数组 - 按字节构建 bytes32
function buildBytes32() public pure returns (bytes32) {
bytes32 result;
assembly {
// 存储 "Hello, World!" 字符串到 bytes32
// 注意:字符串以右对齐方式存储
result := 0x48656c6c6f2c20576f726c642100000000000000000000000000000000000000
}
return result;
}
// 从字节数组提取部分内容
function extractString(bytes32 _input) public pure returns (string memory) {
// 找到结束零的位置(如果有)
uint8 length = 32;
for (uint8 i = 0; i < 32; i++) {
if (_input[i] == 0) {
length = i;
break;
}
}
// 创建字符串并复制非零字节
bytes memory result = new bytes(length);
for (uint8 i = 0; i < length; i++) {
result[i] = _input[i];
}
return string(result);
}
// 使用 bytes32 存储最多 32 个字符的字符串
function stringToBytes32(string memory _input) public pure returns (bytes32) {
bytes32 result;
assembly {
// 从内存加载字符串
result := mload(add(_input, 32))
}
return result;
}
// 两个 bytes32 值的位操作合并
function mergeData(bytes32 _a, bytes32 _b, uint8 _bitsFromA) public pure returns (bytes32) {
require(_bitsFromA <= 256, "Invalid bit count");
uint256 mask = (1 << _bitsFromA) - 1;
mask = mask << (256 - _bitsFromA);
uint256 aVal = uint256(_a) & mask;
uint256 bVal = uint256(_b) & ~mask;
return bytes32(aVal | bVal);
}
// 将数字压缩到单个 bytes32 中
// 例如,存储 4 个 uint64 值到一个 bytes32 中
function packUints(uint64 a, uint64 b, uint64 c, uint64 d) public pure returns (bytes32) {
return bytes32(
(uint256(a) << 192) |
(uint256(b) << 128) |
(uint256(c) << 64) |
uint256(d)
);
}
// 从 bytes32 解包 uint64 值
function unpackUint64(bytes32 packed, uint8 index) public pure returns (uint64) {
require(index < 4, "Index out of bounds");
return uint64(uint256(packed) >> (64 * (3 - index)));
}
// 使用 bytes32 作为映射键
mapping(bytes32 => uint256) private dataStore;
// 存储数据
function storeData(bytes32 key, uint256 value) public {
dataStore[key] = value;
}
// 检索数据
function getData(bytes32 key) public view returns (uint256) {
return dataStore[key];
}
// 从 String 创建哈希作为唯一标识符
function getKeyFromString(string memory input) public pure returns (bytes32) {
return keccak256(abi.encodePacked(input));
}
}
6. 枚举类型 (Enumerations)
定义与特性
枚举类型用于创建用户定义类型,包含一组命名常量。
- 枚举是值类型,在内部表示为整数(从 0 开始)
- 可以显式设置枚举常量的值
- 默认值是第一个定义的枚举常量(索引为 0)
操作和限制
- 可以将枚举类型转换为整数,反之亦然
- 无法在运行时动态添加枚举值
实例代码
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract EnumExample {
// 定义一个状态枚举
enum Status {
Pending, // 0
Active, // 1
Inactive, // 2
Cancelled // 3
}
// 状态变量
Status public currentStatus;
// 另一个示例:支付方式
enum PaymentMethod {
CreditCard, // 0
BankTransfer, // 1
Crypto // 2
}
// 带有自定义值的枚举(通过类型转换处理)
enum Priority {
Low, // 0
Medium, // 1
High, // 2
Critical // 3
}
// 映射不同状态到权重
mapping(Status => uint256) public statusWeights;
// 构造函数
constructor() {
currentStatus = Status.Pending; // 设置初始状态
// 初始化状态权重
statusWeights[Status.Pending] = 1;
statusWeights[Status.Active] = 2;
statusWeights[Status.Inactive] = 1;
statusWeights[Status.Cancelled] = 0;
}
// 设置状态
function setStatus(Status _newStatus) public {
currentStatus = _newStatus;
}
// 获取当前状态
function getStatus() public view returns (Status) {
return currentStatus;
}
// 获取状态名称
function getStatusName() public view returns (string memory) {
if (currentStatus == Status.Pending) return "Pending";
if (currentStatus == Status.Active) return "Active";
if (currentStatus == Status.Inactive) return "Inactive";
if (currentStatus == Status.Cancelled) return "Cancelled";
return "Unknown";
}
// 枚举转换为 uint
function getStatusAsUint() public view returns (uint) {
return uint(currentStatus);
}
// uint 转换为枚举
function setStatusFromUint(uint _status) public {
require(_status <= uint(type(Status).max), "Invalid status");
currentStatus = Status(_status);
}
// 比较枚举值
function isActive() public view returns (bool) {
return currentStatus == Status.Active;
}
// 使用枚举作为参数
function processPayment(PaymentMethod _method, uint256 _amount) public pure returns (string memory) {
if (_method == PaymentMethod.CreditCard) {
return string(abi.encodePacked("Processing credit card payment of ", _uintToString(_amount)));
} else if (_method == PaymentMethod.BankTransfer) {
return string(abi.encodePacked("Processing bank transfer of ", _uintToString(_amount)));
} else if (_method == PaymentMethod.Crypto) {
return string(abi.encodePacked("Processing crypto payment of ", _uintToString(_amount)));
} else {
return "Unknown payment method";
}
}
// 遍历所有可能的枚举值
function getAllStatusWeights() public view returns (uint256[] memory) {
uint256 count = uint(type(Status).max) + 1;
uint256[] memory weights = new uint256[](count);
for (uint i = 0; i < count; i++) {
weights[i] = statusWeights[Status(i)];
}
return weights;
}
// 获取枚举成员数量
function getStatusCount() public pure returns (uint256) {
return uint(type(Status).max) + 1;
}
// 辅助函数:将 uint 转换为字符串(简化版)
function _uintToString(uint256 value) private pure returns (string memory) {
if (value == 0) return "0";
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
}
枚举的高级使用案例
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract AdvancedEnumExample {
// 表示状态机的枚举
enum OrderState {
Created,
Paid,
Shipped,
Delivered,
Cancelled,
Refunded
}
// 投票选择的枚举
enum Vote {
Abstain, // 0
Yes, // 1
No // 2
}
// 订单结构体
struct Order {
OrderState state;
uint256 amount;
address customer;
uint256 timestamp;
}
// 状态转换规则
mapping(OrderState => OrderState[]) private allowedTransitions;
// 订单存储
mapping(uint256 => Order) public orders;
uint256 public orderCount;
// 用户投票记录
mapping(address => mapping(uint256 => Vote)) public userVotes;
constructor() {
// 初始化状态转换规则
allowedTransitions[OrderState.Created] = [OrderState.Paid, OrderState.Cancelled];
allowedTransitions[OrderState.Paid] = [OrderState.Shipped, OrderState.Cancelled];
allowedTransitions[OrderState.Shipped] = [OrderState.Delivered, OrderState.Cancelled];
allowedTransitions[OrderState.Delivered] = [OrderState.Refunded];
allowedTransitions[OrderState.Cancelled] = [OrderState.Refunded];
// Refunded 是终态,没有可能的转换
}
// 创建订单
function createOrder(uint256 _amount) public returns (uint256) {
uint256 orderId = orderCount++;
orders[orderId] = Order({
state: OrderState.Created,
amount: _amount,
customer: msg.sender,
timestamp: block.timestamp
});
return orderId;
}
// 更新订单状态
function updateOrderState(uint256 _orderId, OrderState _newState) public {
Order storage order = orders[_orderId];
require(order.customer != address(0), "Order does not exist");
// 检查状态转换是否有效
bool validTransition = false;
OrderState[] memory allowed = allowedTransitions[order.state];
for (uint i = 0; i < allowed.length; i++) {
if (allowed[i] == _newState) {
validTransition = true;
break;
}
}
require(validTransition, "Invalid state transition");
// 更新状态
order.state = _newState;
order.timestamp = block.timestamp;
}
// 检查订单是否已完成
function isOrderComplete(uint256 _orderId) public view returns (bool) {
OrderState state = orders[_orderId].state;
return state == OrderState.Delivered || state == OrderState.Refunded;
}
// 使用枚举进行投票
function vote(uint256 _proposalId, Vote _vote) public {
require(_vote == Vote.Abstain || _vote == Vote.Yes || _vote == Vote.No, "Invalid vote");
userVotes[msg.sender][_proposalId] = _vote;
}
// 统计投票数
function countVotes(uint256 _proposalId, address[] memory _voters) public view returns (
uint256 abstainCount,
uint256 yesCount,
uint256 noCount
) {
for (uint256 i = 0; i < _voters.length; i++) {
Vote userVote = userVotes[_voters[i]][_proposalId];
if (userVote == Vote.Abstain) {
abstainCount++;
} else if (userVote == Vote.Yes) {
yesCount++;
} else if (userVote == Vote.No) {
noCount++;
}
}
return (abstainCount, yesCount, noCount);
}
// 将枚举转换为字符串
function getOrderStateString(uint256 _orderId) public view returns (string memory) {
OrderState state = orders[_orderId].state;
if (state == OrderState.Created) return "Created";
if (state == OrderState.Paid) return "Paid";
if (state == OrderState.Shipped) return "Shipped";
if (state == OrderState.Delivered) return "Delivered";
if (state == OrderState.Cancelled) return "Cancelled";
if (state == OrderState.Refunded) return "Refunded";
return "Unknown";
}
}
7. 函数类型 (Function Types)
定义与特性
Solidity 中的函数类型表示对函数的引用。函数类型是值类型,但与数组和结构体一样,作为参数传递时存储位置很重要。
- 语法:
function (<parameter types>) {internal|external} [pure|view|payable] [returns (<return types>)]
- 可以是内部 (
internal
) 或外部 (external
) 类型 - 函数可以有
pure
、view
或payable
修饰符 - 函数类型变量初始值为
null
函数类型的使用
- 可以将函数分配给变量
- 可以将函数作为参数传递
- 可以从函数返回函数
- 可以调用存储在变量中的函数
实例代码
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract FunctionTypeExample {
// 定义一个内部函数类型的变量
function(uint256, uint256) internal pure returns (uint256) mathOperation;
// 声明一个事件,跟踪操作
event OperationPerformed(string operationName, uint256 a, uint256 b, uint256 result);
// 初始化函数类型变量
constructor() {
mathOperation = add; // 默认为加法
}
// 加法函数
function add(uint256 a, uint256 b) internal pure returns (uint256) {
return a + b;
}
// 减法函数
function subtract(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a - b : 0;
}
// 乘法函数
function multiply(uint256 a, uint256 b) internal pure returns (uint256) {
return a * b;
}
// 除法函数
function divide(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "Cannot divide by zero");
return a / b;
}
// 设置当前操作为加法
function setOperationToAdd() public {
mathOperation = add;
}
// 设置当前操作为减法
function setOperationToSubtract() public {
mathOperation = subtract;
}
// 设置当前操作为乘法
function setOperationToMultiply() public {
mathOperation = multiply;
}
// 设置当前操作为除法
function setOperationToDivide() public {
mathOperation = divide;
}
// 执行当前设置的操作
function execute(uint256 a, uint256 b) public returns (uint256) {
uint256 result = mathOperation(a, b);
string memory operationName;
if (mathOperation == add) operationName = "Addition";
else if (mathOperation == subtract) operationName = "Subtraction";
else if (mathOperation == multiply) operationName = "Multiplication";
else if (mathOperation == divide) operationName = "Division";
else operationName = "Unknown";
emit OperationPerformed(operationName, a, b, result);
return result;
}
// 将函数作为参数传递
function computeWithFunction(
uint256 a,
uint256 b,
function(uint256, uint256) internal pure returns (uint256) operation
) public pure returns (uint256) {
return operation(a, b);
}
// 返回函数
function getOperation(string memory opName) public pure returns (
function(uint256, uint256) internal pure returns (uint256)
) {
bytes32 opHash = keccak256(abi.encodePacked(opName));
if (opHash == keccak256(abi.encodePacked("add"))) return add;
if (opHash == keccak256(abi.encodePacked("subtract"))) return subtract;
if (opHash == keccak256(abi.encodePacked("multiply"))) return multiply;
if (opHash == keccak256(abi.encodePacked("divide"))) return divide;
revert("Unknown operation");
}
// 使用外部函数类型
function getAddFunction() public view returns (function(uint256, uint256) external pure returns (uint256)) {
return this.addExternal;
}
// 外部加法函数
function addExternal(uint256 a, uint256 b) external pure returns (uint256) {
return a + b;
}
// 使用外部函数类型调用
function callExternalFunction(
function(uint256, uint256) external pure returns (uint256) externalFunc,
uint256 a,
uint256 b
) public pure returns (uint256) {
return externalFunc(a, b);
}
// 函数选择器示例
function getAddSelector() public pure returns (bytes4) {
return this.addExternal.selector;
}
}
高级函数类型用例:策略模式与回调
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract AdvancedFunctionTypes {
// 定义一个函数类型的数组
function(uint256) internal returns (uint256)[] public processors;
// 回调函数定义
function(uint256, bool) internal callback;
// 事件
event Processing(uint256 input, uint256 output);
event CallbackTriggered(uint256 value, bool success);
// 一些示例处理函数
function doubleValue(uint256 input) internal pure returns (uint256) {
return input * 2;
}
function addTen(uint256 input) internal pure returns (uint256) {
return input + 10;
}
function square(uint256 input) internal pure returns (uint256) {
return input * input;
}
// 初始化处理器数组
function initializeProcessors() public {
// 清除现有处理器
delete processors;
// 添加处理器函数
processors.push(doubleValue);
processors.push(addTen);
processors.push(square);
}
// 执行所有处理器
function processValue(uint256 input) public returns (uint256) {
uint256 result = input;
for (uint256 i = 0; i < processors.length; i++) {
result = processors[i](result);
emit Processing(input, result);
}
return result;
}
// 设置回调函数
function setCallbackToSuccess() public {
callback = successCallback;
}
function setCallbackToFailure() public {
callback = failureCallback;
}
// 回调函数实现
function successCallback(uint256 value, bool success) internal {
emit CallbackTriggered(value, success);
// 可以在这里添加更多逻辑
}
function failureCallback(uint256 value, bool success) internal {
emit CallbackTriggered(value, success);
// 处理失败情况的逻辑
revert("Operation failed");
}
// 执行带回调的异步操作模拟
function executeWithCallback(uint256 value, bool shouldSucceed) public {
require(address(callback) != address(0), "Callback not set");
// 执行一些操作,然后调用回调
uint256 result = value * 2;
// 仅在成功时调用回调
if (shouldSucceed) {
callback(result, true);
} else {
callback(result, false);
}
}
// 使用函数类型实现策略模式
enum Strategy { Conservative, Moderate, Aggressive }
function(uint256) internal pure returns (uint256) investmentStrategy;
function setStrategy(Strategy _strategy) public {
if (_strategy == Strategy.Conservative) {
investmentStrategy = conservativeStrategy;
} else if (_strategy == Strategy.Moderate) {
investmentStrategy = moderateStrategy;
} else if (_strategy == Strategy.Aggressive) {
investmentStrategy = aggressiveStrategy;
} else {
revert("Unknown strategy");
}
}
function conservativeStrategy(uint256 amount) internal pure returns (uint256) {
// 保守策略:返回 5% 收益
return amount + (amount * 5 / 100);
}
function moderateStrategy(uint256 amount) internal pure returns (uint256) {
// 中等策略:返回 10% 收益
return amount + (amount * 10 / 100);
}
function aggressiveStrategy(uint256 amount) internal pure returns (uint256) {
// 激进策略:返回 20% 收益,但有 10% 的亏损风险
if (amount % 10 == 0) { // 简单的模拟亏损情况
return amount - (amount * 10 / 100);
} else {
return amount + (amount * 20 / 100);
}
}
// 执行投资
function invest(uint256 amount) public view returns (uint256) {
require(address(investmentStrategy) != address(0), "Strategy not set");
return investmentStrategy(amount);
}
}
8. 值类型之间的类型转换
隐式类型转换
Solidity 在满足以下条件时执行隐式类型转换:
- 类型之间的大小关系明确(如
uint8
到uint16
) - 类型之间不会出现信息丢失
显式类型转换
对于不能隐式转换的情况,需要使用显式类型转换:
- 较大类型到较小类型(如
uint256
到uint8
) - 有符号类型与无符号类型之间(如
int256
到uint256
) - 地址类型与整数类型之间
实例代码
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract TypeConversionExample {
// 隐式类型转换
function implicitConversions() public pure returns (uint256, int256) {
uint8 smallUint = 100;
uint256 largeUint = smallUint; // uint8 隐式转换为 uint256
int16 smallInt = 200;
int256 largeInt = smallInt; // int16 隐式转换为 int256
return (largeUint, largeInt);
}
// 显式类型转换
function explicitConversions() public pure returns (
uint8,
int8,
uint256,
bytes32,
address
) {
uint256 largeUint = 100;
uint8 smallUint = uint8(largeUint); // 显式缩小转换
int256 largeInt = -100;
int8 smallInt = int8(largeInt); // 显式缩小转换
int256 signedInt = 0x64;
uint256 unsignedInt = uint256(signedInt); // 显式转换 int 到 uint
uint256 uintValue = 0xaabbccdd;
bytes32 bytes32Value = bytes32(uintValue); // uint 到 bytes32
bytes20 bytes20Value = bytes20(0x1234567890123456789012345678901234567890);
address addressValue = address(bytes20Value); // bytes20 到 address
return (smallUint, smallInt, unsignedInt, bytes32Value, addressValue);
}
// 数据丢失的风险
function truncationRisks() public pure returns (uint8, int8) {
uint256 largeUint = 300; // 超出 uint8 范围 (0-255)
uint8 truncatedUint = uint8(largeUint); // 结果为 44 (300 - 256 = 44)
int256 largeInt = 130; // 超出 int8 范围 (-128 到 127)
int8 truncatedInt = int8(largeInt); // 结果为 -126 (130 - 256 = -126)
return (truncatedUint, truncatedInt);
}
// 从 bytes1, bytes2, etc. 到数字的转换
function bytesToIntegers() public pure returns (uint8, uint16, uint256) {
bytes1 b1 = 0xA9;
uint8 u8 = uint8(b1); // 结果为 169
bytes2 b2 = 0xA9F0;
uint16 u16 = uint16(b2); // 结果为 43504
bytes32 b32 = bytes32(uint256(0x1234567890));
uint256 u256 = uint256(b32); // 结果为 0x1234567890
return (u8, u16, u256);
}
// 布尔转换
function booleanConversions() public pure returns (uint8, bool, bool) {
bool trueBool = true;
uint8 fromTrue = uint8(trueBool); // 1
uint256 nonZero = 123;
uint256 zero = 0;
bool fromNonZero = (nonZero != 0); // true
bool fromZero = (zero != 0); // false
return (fromTrue, fromNonZero, fromZero);
}
// 地址与整数/字节之间的转换
function addressConversions() public pure returns (
address,
uint160,
bytes20
) {
address addr = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
uint160 addrUint = uint160(addr); // 地址转为 uint160
bytes20 addrBytes = bytes20(addr); // 地址转为 bytes20
return (addr, addrUint, addrBytes);
}
// 安全类型转换
function safeTypeConversion(uint256 value) public pure returns (uint8, bool) {
// 检查值是否在 uint8 范围内
if (value > type(uint8).max) {
return (0, false);
}
return (uint8(value), true);
}
// 处理有符号和无符号整数之间的转换
function signedUnsignedConversion(int256 signedValue) public pure returns (uint256, bool) {
// 检查是否为负数
if (signedValue < 0) {
return (0, false);
}
return (uint256(signedValue), true);
}
// 类型转换在实际应用中的用例:货币单位转换
function convertEtherToWei(uint256 etherAmount) public pure returns (uint256) {
// 1 Ether = 10^18 Wei
return etherAmount * 1 ether;
}
function convertWeiToGwei(uint256 weiAmount) public pure returns (uint256) {
// 1 Gwei = 10^9 Wei
return weiAmount / 1 gwei;
}
}
更复杂的类型转换示例
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract AdvancedTypeConversions {
// 字节数组和字符串之间的转换
function bytesToString(bytes memory data) public pure returns (string memory) {
return string(data);
}
function stringToBytes(string memory str) public pure returns (bytes memory) {
return bytes(str);
}
// 十六进制字符串/数字之间的转换
function addressToHexString(address addr) public pure returns (string memory) {
return _toHexString(uint256(uint160(addr)), 20);
}
function uint256ToHexString(uint256 value) public pure returns (string memory) {
return _toHexString(value, 32);
}
// 辅助函数:将 uint256 转换为十六进制字符串
function _toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _toHexChar(uint8(value & 0xf));
value >>= 4;
}
return string(buffer);
}
// 辅助函数:将 4 位转换为十六进制字符
function _toHexChar(uint8 value) internal pure returns (bytes1) {
if (value < 10) {
return bytes1(uint8(bytes1("0")) + value);
} else {
return bytes1(uint8(bytes1("a")) + value - 10);
}
}
// 有符号整数和无符号整数之间类型转换的边界情况
function signedToUnsignedEdgeCases() public pure returns (
uint256, // 从最大 int256 转换
uint256, // 从最小 int256 转换(会失败)
int256, // 从最大 uint256 转换(会失败)
int256 // 从 uint256(0) 转换
) {
int256 maxInt256 = type(int256).max;
int256 minInt256 = type(int256).min;
uint256 maxUint256 = type(uint256).max;
uint256 minUint256 = 0;
uint256 maxIntToUint;
uint256 minIntToUint;
int256 maxUintToInt;
int256 minUintToInt;
// 安全转换
try this.unsafeIntToUint(maxInt256) returns (uint256 result) {
maxIntToUint = result;
} catch {
maxIntToUint = 0; // 转换失败
}
// 这个会失败(负数不能转换为 uint)
try this.unsafeIntToUint(minInt256) returns (uint256 result) {
minIntToUint = result;
} catch {
minIntToUint = 0; // 转换失败
}
// 这个会失败(maxUint256 超出 int256 范围)
try this.unsafeUintToInt(maxUint256) returns (int256 result) {
maxUintToInt = result;
} catch {
maxUintToInt = 0; // 转换失败
}
// 安全转换
try this.unsafeUintToInt(minUint256) returns (int256 result) {
minUintToInt = result;
} catch {
minUintToInt = 0; // 转换失败
}
return (maxIntToUint, minIntToUint, maxUintToInt, minUintToInt);
}
// 用于测试转换边界的不安全函数
function unsafeIntToUint(int256 value) external pure returns (uint256) {
require(value >= 0, "Negative value cannot be converted to uint");
return uint256(value);
}
function unsafeUintToInt(uint256 value) external pure returns (int256) {
require(value <= uint256(type(int256).max), "Value too large for int256");
return int256(value);
}
// 使用位操作符进行类型转换
function bitOperationConversions() public pure returns (
uint32,
uint32,
bytes4
) {
uint256 fullValue = 0x12345678ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF;
// 提取低 32 位
uint32 low32Bits = uint32(fullValue);
// 提取高 32 位
uint32 high32Bits = uint32(fullValue >> 224);
// 创建函数选择器(4 字节)
bytes4 selector = bytes4(keccak256("transfer(address,uint256)"));
return (low32Bits, high32Bits, selector);
}
// 安全地将 uint 数组转换为 int 数组
function uintArrayToIntArray(uint256[] memory uintArray) public pure returns (int256[] memory) {
int256[] memory intArray = new int256[](uintArray.length);
for (uint256 i = 0; i < uintArray.length; i++) {
require(uintArray[i] <= uint256(type(int256).max), "Value too large for int256");
intArray[i] = int256(uintArray[i]);
}
return intArray;
}
}
9. 常量和不可变量
定义与特性
Solidity 提供两种声明固定值的方法:
constant
:在编译时必须确定的值,不占用存储空间immutable
:可以在构造函数中分配的值,在部署后不可更改
常量
- 使用关键字
constant
声明 - 必须在声明时赋值
- 可以包含复杂表达式,但仅限于编译时可确定的计算
- 每次使用都复制其实际值,不占用存储空间
不可变量
- 使用关键字
immutable
声明 - 可以在构造函数中分配
- 部署后不可修改
- 比存储变量更节省 gas