// 旧时代的SafeMath写法
library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
}
这种方式不仅代码冗长,还容易遗漏。
1.2 v0.8.25的解决方案
Solidity v0.8.25默认启用算术溢出检查,告别SafeMath时代:
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
contract SecureCalculator {
// 安全的加法,自动溢出检查并回退
function safeAdd(uint8 a, uint8 b) public pure returns (uint8) {
return a + b; // 自动revert如果溢出
}
// 安全的减法,自动下溢检查
function safeSubtract(uint256 a, uint256 b) public pure returns (uint256) {
return a - b; // 自动revert如果b > a
}
// 安全的乘法
function safeMultiply(uint256 a, uint256 b) public pure returns (uint256) {
return a * b; // 自动检查溢出
}
}
# 1. 先创建父铭文
ord wallet inscribe --fee-rate 10 --file parent.json
# 2. 获取父铭文ID
ord wallet inscriptions
# 3. 创建子铭文,关联父铭文
ord wallet inscribe --fee-rate 10 --parent <PARENT_INSCRIPTION_ID> --file child.json
父子关系创建后不可修改,这保证了收藏关系的永久性。
6.2 自定义手续费策略
在网络拥堵时期,合理设置手续费可以节省大量成本:
bash
# 低优先级(适合时间不敏感的场景)
ord wallet inscribe --fee-rate 1 --file large_content.png
# 高优先级(网络繁忙时确保快速确认)
ord wallet inscribe --fee-rate 50 --file quick_inscription.txt
对于频繁操作,可以编写脚本批量处理:
bash
#!/bin/bash
# batch_inscribe.sh - 批量铭刻脚本
CONTENT_DIR="./images"
FEE_RATE=10
for file in "$CONTENT_DIR"/*; do
if [ -f "$file" ]; then
echo "铭刻: $file"
ord wallet inscribe --fee-rate $FEE_RATE --file "$file"
sleep 5 # 避免交易冲突
fi
done
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.24;
contract AssemblyExample {
function addWithAssembly(uint256 a, uint256 b) public pure returns (uint256 result) {
assembly {
result := add(a, b)
}
}
}
// 单槽存储多个小值
contract CompactStorage {
function packValues(uint128 a, uint128 b) public {
assembly {
// 将两个128位值打包到一个槽中
sstore(0, add(a, shl(128, b)))
}
}
function unpackValues() public view returns (uint128 a, uint128 b) {
assembly {
let packed := sload(0)
a := and(packed, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
b := shr(128, packed)
}
}
}
contract ArraySum {
function sumSolidity(uint256[] memory data) public pure returns (uint256 sum) {
for (uint256 i = 0; i < data.length; i++) {
sum += data[i];
}
}
function sumAssembly(uint256[] memory data) public pure returns (uint256 sum) {
assembly {
let len := mload(data)
let ptr := add(data, 0x20)
let end := add(ptr, mul(len, 0x20))
for { } lt(ptr, end) { ptr := add(ptr, 0x20) } {
sum := add(sum, mload(ptr))
}
}
}
}
在生产环境中使用优化版本时,务必确保调用者传入的数组是有效的,且代码逻辑保证不会越界访问。
位运算加速
某些数学运算可以用位运算替代,从而大幅提升性能:
solidity
contract BitOptimizations {
// 用位移替代乘法
function multiplyByPowerOfTwo(uint256 x, uint8 power) public pure returns (uint256 result) {
assembly {
result := shl(power, x)
}
}
// 用位移替代除法
function divideByPowerOfTwo(uint256 x, uint8 power) public pure returns (uint256 result) {
assembly {
result := shr(power, x)
}
}
// 快速取模(当divisor是2的幂时)
function modPowerOfTwo(uint256 x, uint256 divisor) public pure returns (uint256 result) {
require(divisor > 0 && (divisor & (divisor - 1)) == 0, "Not power of two");
assembly {
result := and(x, sub(divisor, 1))
}
}
}
零值检查优化
Solidity的零值检查在某些场景下可能不够高效。通过汇编可以实现更激进的检查模式:
solidity
contract ZeroChecks {
// 高效的非零检查
function requireNonZero(uint256 x) public pure {
assembly {
if iszero(x) {
mstore(0x00, 0x20)
mstore(0x20, 0x736d617274636f6e7472616374000000000000000000000000000000000000)
revert(0x1c, 0x04)
}
}
}
// 使用舍入方式计算最小值
function min(uint256 a, uint256 b) public pure returns (uint256 result) {
assembly {
result := sub(a, sub(a, b))
if slt(result, 0) {
result := b
}
}
}
}
合约代码获取
内联汇编的一个常见用途是检查其他合约的字节码。这在实现某些白名单机制或者合约验证时非常有用:
solidity
library CodeReader {
function getCodeSize(address target) internal view returns (uint256 size) {
assembly {
size := extcodesize(target)
}
}
function getCodeHash(address target) internal view returns (bytes32 hash) {
assembly {
hash := extcodehash(target)
}
}
}
contract CodeChecker {
using CodeReader for address;
function isContract(address target) public view returns (bool) {
return target.getCodeSize() > 0;
}
function verifyContractCode(address target, bytes32 expectedHash)
public
view
returns (bool)
{
return target.getCodeHash() == expectedHash;
}
}
实战:构建高效的数据验证器
让我们用一个完整例子来整合所有技术:实现一个高效的数据哈希验证器:
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
contract EfficientHasher {
// 使用内联汇编优化哈希计算
function hashData(bytes memory data) public pure returns (bytes32) {
bytes32 result;
assembly {
// 从内存加载数据并计算Keccak256
result := keccak256(add(data, 0x20), mload(data))
}
return result;
}
// 批量验证 - 优化版
function verifyBatch(
bytes[] memory dataArray,
bytes32[] memory expectedHashes
) public pure returns (bool) {
require(dataArray.length == expectedHashes.length, "Length mismatch");
assembly {
let len := dataArray.length
// 指向两个数组的数据区域
let dataPtr := add(dataArray, 0x20)
let hashPtr := add(expectedHashes, 0x20)
for { let i := 0 } lt(i, len) { i := add(i, 1) } {
// 计算当前数据的哈希
let dataLen := mload(dataPtr)
let actualHash := keccak256(add(dataPtr, 0x20), dataLen)
// 与预期值比较
if iszero(eq(actualHash, mload(hashPtr))) {
// 找到不匹配项,返回失败
mstore(0, 0)
return(0, 0x20)
}
// 移动到下一个元素
dataPtr := add(dataPtr, 0x20)
hashPtr := add(hashPtr, 0x20)
}
}
// 所有验证通过
return true;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "forge-std/Test.sol";
contract AssemblyBenchmark is Test {
uint256[] testData;
function setUp() public {
testData = new uint256[](1000);
for (uint256 i = 0; i < 1000; i++) {
testData[i] = i;
}
}
function testSoliditySum() public view {
uint256 sum;
for (uint256 i = 0; i < testData.length; i++) {
sum += testData[i];
}
}
function testAssemblySum() public pure {
uint256 sum;
assembly {
let len := calldataload(0x04)
let ptr := add(calldataload(0x24), 0x20)
let end := add(ptr, mul(len, 0x20))
for { } lt(ptr, end) { ptr := add(ptr, 0x20) } {
sum := add(sum, calldataload(sub(ptr, 0x20)))
}
}
}
}
contract UnrolledLoop {
uint256[8] public coefficients;
// 普通循环
function calculateNormal(uint256 x) public view returns (uint256) {
uint256 result = 0;
for (uint256 i = 0; i < coefficients.length; i++) {
result += coefficients[i] * x;
}
return result;
}
// 循环展开(适合固定迭代次数)
function calculateUnrolled(uint256 x) public view returns (uint256) {
uint256[8] memory c = coefficients; // 缓存到内存
return c[0] * x + c[1] * x + c[2] * x + c[3] * x +
c[4] * x + c[5] * x + c[6] * x + c[7] * x;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
// 定义库合约
library MathUtils {
// 库函数通常是internal或private
function sqrt(uint256 y) internal pure returns (uint256 z) {
if (y > 3) {
z = y;
uint256 x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
}
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
// 安全数学运算
function safeAdd(uint256 a, uint256 b) internal pure returns (uint256) {
require(a + b >= a, "Math: addition overflow");
return a + b;
}
function safeSub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "Math: subtraction overflow");
return a - b;
}
}
// 使用库
contract UseMathUtils {
using MathUtils for uint256;
function calculate(uint256 a, uint256 b) public pure returns (uint256) {
uint256 sum = MathUtils.safeAdd(a, b);
return MathUtils.sqrt(sum);
}
// 也可以用using for语法
function calculateAlternative(uint256 a, uint256 b)
public
pure
returns (uint256)
{
uint256 sum = a.safeAdd(b);
return sum.sqrt(); // 更自然的语法
}
}
3.2 数据结构库
库的一个常见用途是实现数据结构操作:
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
library AddressArray {
function indexOf(address[] storage arr, address target)
internal
view
returns (int256)
{
for (uint256 i = 0; i < arr.length; i++) {
if (arr[i] == target) {
return int256(i);
}
}
return -1;
}
function remove(address[] storage arr, address target)
internal
{
int256 index = indexOf(arr, target);
require(index >= 0, "Element not found");
removeAt(arr, uint256(index));
}
function removeAt(address[] storage arr, uint256 index)
internal
{
require(index < arr.length, "Index out of bounds");
for (uint256 i = index; i < arr.length - 1; i++) {
arr[i] = arr[i + 1];
}
arr.pop();
}
function contains(address[] storage arr, address target)
internal
view
returns (bool)
{
return indexOf(arr, target) >= 0;
}
}
// 使用数据结构库
contract AddressList {
address[] public whitelist;
using AddressArray for address[];
function addToWhitelist(address user) public {
require(!whitelist.contains(user), "Already whitelisted");
whitelist.push(user);
}
function removeFromWhitelist(address user) public {
whitelist.remove(user);
}
function isWhitelisted(address user) public view returns (bool) {
return whitelist.contains(user);
}
}
3.3 外部库调用
对于大型库合约,使用using ... for ...语法可能不高效,此时可以直接调用库函数:
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
// 字符串处理库
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
function toString(uint256 value) internal pure returns (string memory) {
// 特殊处理0
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);
}
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
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] = _HEX_SYMBOLS[value & 0xf];
value >>= 4;
}
return string(buffer);
}
}
contract StringUser {
function getTokenURI(uint256 tokenId, address owner)
public
pure
returns (string memory)
{
// 直接调用库函数
return string.concat(
"https://api.example.com/token/",
Strings.toString(tokenId),
"?owner=",
Strings.toHexString(uint256(uint160(owner)))
);
}
}
四、安全跨合约调用的最佳实践
4.1 验证目标合约
在调用任何外部合约之前,验证合约的存在性和正确性:
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
interface IVerifiedToken {
function balanceOf(address) external view returns (uint256);
function transfer(address, uint256) external returns (bool);
}
contract SecureTokenInteractor {
// 方式1:使用require检查代码大小
function isContract(address account) public view returns (bool) {
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
// 方式2:白名单机制
mapping(address => bool) public verifiedContracts;
function addVerifiedContract(address contract_) external {
// 应该在治理或owner控制下添加
require(isContract(contract_), "Not a contract");
verifiedContracts[contract_] = true;
}
function interactWithVerified(
address token,
address recipient,
uint256 amount
) public returns (bool) {
require(verifiedContracts[token], "Contract not verified");
IVerifiedToken(token).transfer(recipient, amount);
return true;
}
}
// 定义一个简单的数学库合约
library MathLib {
function sqrt(uint256 x) internal pure returns (uint256) {
if (x == 0) return 0;
uint256 z = (x + 1) / 2;
uint256 y = x;
while (z < y) {
y = z;
z = (x / z + z) / 2;
}
return y;
}
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
}
1.2 库合约与普通合约的核心区别
理解库合约与普通合约的区别,对于正确选择使用场景至关重要:
特性
库合约
普通合约
状态变量
受限或无
完全支持
继承
不支持
支持
payable
不支持
支持
fallback函数
不支持
支持
gas成本
调用更便宜
标准成本
this调用
不支持
支持
solidity
// 普通合约可以有自己的存储和状态
contract Bank {
mapping(address => uint256) public balances;
function deposit() external payable {
balances[msg.sender] += msg.value;
}
}
// 库合约只能包含逻辑,不应有自己的存储
library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
}
二、库合约的调用机制
2.1 内部调用(Internal Call)
最常用的库合约调用方式,函数调用在字节码级别被嵌入到调用合约中,不产生外部调用。
solidity
library ArrayLib {
function find(uint256[] storage arr, uint256 value)
internal
view
returns (uint256 index)
{
for (uint256 i = 0; i < arr.length; i++) {
if (arr[i] == value) {
return i;
}
}
revert("Value not found in array");
}
function pushUnique(
uint256[] storage arr,
uint256 value
) internal {
if (!contains(arr, value)) {
arr.push(value);
}
}
function contains(
uint256[] storage arr,
uint256 value
) internal view returns (bool) {
for (uint256 i = 0; i < arr.length; i++) {
if (arr[i] == value) return true;
}
return false;
}
}
使用示例:
solidity
contract UserList {
using ArrayLib for uint256[];
uint256[] private userIds;
function addUser(uint256 userId) external {
userIds.pushUnique(userId);
}
function findUser(uint256 userId) external view returns (uint256) {
return userIds.find(userId);
}
}