Library(库)是 Solidity 中一种特殊的代码复用机制,与 Contract(合约)有本质区别。正确使用 Library 可以节省 Gas、提高代码复用性。
1. Library 和 Contract 的核心区别
| 特性 | Library | Contract |
|---|---|---|
| 部署 | 可以独立部署或内联 | 必须独立部署 |
| 状态变量 | 不能有状态变量 | 可以有状态变量 |
| 继承 | 不能被继承 | 可以被继承 |
| ETH 接收 | 不能接收 ETH | 可以接收 ETH |
| 销毁 | 不能自销毁 | 可以自销毁 |
| 调用方式 | DELEGATECALL 或内联 | CALL |
| 使用场景 | 工具函数、复用代码 | 业务逻辑、状态管理 |
2. Library 的基本用法
独立函数库
solidity// 文件: MathLibrary.sol // 纯函数库,所有函数都是 pure library MathLibrary { // 计算最大公约数 function gcd(uint256 a, uint256 b) internal pure returns (uint256) { while (b != 0) { uint256 temp = b; b = a % b; a = temp; } return a; } // 计算平方根 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 pow(uint256 base, uint256 exponent) internal pure returns (uint256) { if (exponent == 0) return 1; if (exponent == 1) return base; uint256 result = 1; uint256 b = base; uint256 e = exponent; while (e > 0) { if (e & 1 == 1) { result *= b; } b *= b; e >>= 1; } return result; } } // 使用库 contract Calculator { // 使用 using for 语法 using MathLibrary for uint256; function calculate(uint256 a, uint256 b) external pure returns (uint256, uint256, uint256) { uint256 gcdResult = MathLibrary.gcd(a, b); uint256 sqrtResult = a.sqrt(); // 使用 using for 语法 uint256 powResult = a.pow(b); // 使用 using for 语法 return (gcdResult, sqrtResult, powResult); } }
结构体库
solidity// 文件: ArrayLibrary.sol library ArrayLibrary { // 查找元素索引 function find(uint256[] storage arr, uint256 value) internal view returns (int256) { for (uint i = 0; i < arr.length; i++) { if (arr[i] == value) { return int256(i); } } return -1; } // 删除元素(保持顺序) function removeAt(uint256[] storage arr, uint256 index) internal { require(index < arr.length, "Index out of bounds"); for (uint i = index; i < arr.length - 1; i++) { arr[i] = arr[i + 1]; } arr.pop(); } // 删除元素(不保持顺序,更高效) function quickRemove(uint256[] storage arr, uint256 index) internal { require(index < arr.length, "Index out of bounds"); if (index != arr.length - 1) { arr[index] = arr[arr.length - 1]; } arr.pop(); } // 插入排序 function sort(uint256[] storage arr) internal { for (uint i = 1; i < arr.length; i++) { uint256 key = arr[i]; int256 j = int256(i) - 1; while (j >= 0 && arr[uint256(j)] > key) { arr[uint256(j) + 1] = arr[uint256(j)]; j--; } arr[uint256(j) + 1] = key; } } // 检查是否包含 function contains(uint256[] storage arr, uint256 value) internal view returns (bool) { for (uint i = 0; i < arr.length; i++) { if (arr[i] == value) { return true; } } return false; } } // 使用结构体库 contract DataManager { using ArrayLibrary for uint256[]; uint256[] public data; function addData(uint256 value) external { data.push(value); } function removeData(uint256 index) external { data.quickRemove(index); // 调用库函数 } function findData(uint256 value) external view returns (int256) { return data.find(value); // 调用库函数 } function sortData() external { data.sort(); // 调用库函数 } }
3. Library 的高级用法
使用 storage 的库
solidity// 文件: MappingLibrary.sol library MappingLibrary { // 为 mapping 添加迭代功能 struct IterableMapping { mapping(address => uint256) data; address[] keys; mapping(address => uint256) keyIndex; } function set(IterableMapping storage self, address key, uint256 value) internal { if (self.keyIndex[key] == 0) { // 新键 self.keys.push(key); self.keyIndex[key] = self.keys.length; } self.data[key] = value; } function get(IterableMapping storage self, address key) internal view returns (uint256) { return self.data[key]; } function remove(IterableMapping storage self, address key) internal { require(self.keyIndex[key] != 0, "Key not found"); uint256 index = self.keyIndex[key] - 1; address lastKey = self.keys[self.keys.length - 1]; // 用最后一个元素替换要删除的元素 self.keys[index] = lastKey; self.keyIndex[lastKey] = index + 1; self.keys.pop(); delete self.keyIndex[key]; delete self.data[key]; } function contains(IterableMapping storage self, address key) internal view returns (bool) { return self.keyIndex[key] != 0; } function size(IterableMapping storage self) internal view returns (uint256) { return self.keys.length; } function getKeyAt(IterableMapping storage self, uint256 index) internal view returns (address) { require(index < self.keys.length, "Index out of bounds"); return self.keys[index]; } } // 使用可迭代映射 contract TokenHolder { using MappingLibrary for MappingLibrary.IterableMapping; MappingLibrary.IterableMapping private balances; function setBalance(address holder, uint256 amount) external { balances.set(holder, amount); } function getBalance(address holder) external view returns (uint256) { return balances.get(holder); } function removeHolder(address holder) external { balances.remove(holder); } function getAllHolders() external view returns (address[] memory) { address[] memory holders = new address[](balances.size()); for (uint i = 0; i < balances.size(); i++) { holders[i] = balances.getKeyAt(i); } return holders; } }
4. 常用的标准库
SafeMath(Solidity < 0.8.0)
solidity// 文件: SafeMath.sol library SafeMath { function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "SafeMath: subtraction underflow"); return a - b; } function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) return 0; uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } function div(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: division by zero"); return a / b; } function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0, "SafeMath: modulo by zero"); return a % b; } } // 使用 SafeMath contract SafeCalculator { using SafeMath for uint256; function safeAdd(uint256 a, uint256 b) external pure returns (uint256) { return a.add(b); // 使用库函数 } }
Address 库
solidity// OpenZeppelin 的 Address 库 library Address { function isContract(address account) internal view returns (bool) { return account.code.length > 0; } function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Transfer failed"); } function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.call(data); if (!success) { if (returndata.length > 0) { assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } return returndata; } } // 使用 Address 库 contract ContractInteractor { using Address for address; function checkIsContract(address account) external view returns (bool) { return account.isContract(); } function safeTransfer(address payable recipient, uint256 amount) external { recipient.sendValue(amount); } }
5. Library 的部署方式
内联库(Internal 函数)
solidity// 所有函数都是 internal,代码会被内联到调用合约中 library InlineLibrary { function internalFunction(uint256 x) internal pure returns (uint256) { return x * 2; } } // 使用时不需要部署库,代码直接内联 contract UsingInlineLibrary { using InlineLibrary for uint256; function double(uint256 x) external pure returns (uint256) { return x.internalFunction(); } }
外部库(External 函数)
solidity// 包含 external 函数,需要单独部署 library ExternalLibrary { function externalFunction(uint256 x) external pure returns (uint256) { return x * 2; } } // 使用时需要链接已部署的库地址 contract UsingExternalLibrary { function double(uint256 x) external pure returns (uint256) { return ExternalLibrary.externalFunction(x); } }
6. Library 的最佳实践
命名规范
solidity// 库名使用 PascalCase,以 Library 结尾(可选但推荐) library StringLibrary { // 函数名使用 camelCase function toUpperCase(string memory str) internal pure returns (string memory) { // 实现... } }
函数可见性
soliditylibrary VisibilityExample { // internal:代码内联,不需要部署库(推荐) function internalFunc() internal pure returns (uint256) { return 1; } // external:需要部署库,通过 DELEGATECALL 调用 function externalFunc() external pure returns (uint256) { return 2; } // public:可以内部调用也可以外部调用 function publicFunc() public pure returns (uint256) { return 3; } // private:只能在库内部使用 function privateFunc() private pure returns (uint256) { return 4; } }
使用 using for
soliditylibrary EnhancedMath { function square(uint256 x) internal pure returns (uint256) { return x * x; } function cube(uint256 x) internal pure returns (uint256) { return x * x * x; } } contract MathUser { // 方式 1:对特定类型使用 using for using EnhancedMath for uint256; function calculate(uint256 x) external pure returns (uint256, uint256) { return (x.square(), x.cube()); } // 方式 2:全局使用(所有 uint256) using EnhancedMath for *; }
7. Library 的实际应用案例
字符串处理库
soliditylibrary StringUtils { function length(string memory str) internal pure returns (uint256) { return bytes(str).length; } function concat(string memory a, string memory b) internal pure returns (string memory) { bytes memory ba = bytes(a); bytes memory bb = bytes(b); bytes memory result = new bytes(ba.length + bb.length); for (uint i = 0; i < ba.length; i++) { result[i] = ba[i]; } for (uint i = 0; i < bb.length; i++) { result[ba.length + i] = bb[i]; } return string(result); } function substring( string memory str, uint256 start, uint256 length ) internal pure returns (string memory) { bytes memory strBytes = bytes(str); require(start + length <= strBytes.length, "Out of bounds"); bytes memory result = new bytes(length); for (uint i = 0; i < length; i++) { result[i] = strBytes[start + i]; } return string(result); } function compare(string memory a, string memory b) internal pure returns (int256) { bytes memory ba = bytes(a); bytes memory bb = bytes(b); uint256 minLength = ba.length < bb.length ? ba.length : bb.length; for (uint i = 0; i < minLength; i++) { if (ba[i] < bb[i]) return -1; if (ba[i] > bb[i]) return 1; } if (ba.length < bb.length) return -1; if (ba.length > bb.length) return 1; return 0; } } // 使用字符串库 contract StringProcessor { using StringUtils for string; function processString(string memory input) external pure returns ( uint256 len, string memory sub, int256 cmp ) { len = input.length(); sub = input.substring(0, 5); cmp = input.compare("Hello"); } }
8. Library 的 Gas 优化
solidity// 优化前:多次重复代码 contract BeforeOptimization { function operation1(uint256 x) external pure returns (uint256) { // 复杂计算... return x * 2 + 1; } function operation2(uint256 x) external pure returns (uint256) { // 相同复杂计算... return x * 2 + 1; } } // 优化后:使用库复用代码 library OptimizedMath { function complexCalculation(uint256 x) internal pure returns (uint256) { return x * 2 + 1; } } contract AfterOptimization { using OptimizedMath for uint256; function operation1(uint256 x) external pure returns (uint256) { return x.complexCalculation(); } function operation2(uint256 x) external pure returns (uint256) { return x.complexCalculation(); } }
9. 总结
Library 是 Solidity 中强大的代码复用工具:
-
使用场景:工具函数、数据结构扩展、代码复用
-
部署方式:internal 函数内联,external 函数需要部署
-
最佳实践:
- 优先使用 internal 函数
- 使用 using for 语法提高可读性
- 遵循命名规范
- 利用 OpenZeppelin 等标准库
-
Gas 优化:减少代码重复,提高部署效率