WTF Solidity 17: Biblioteca (Library) — Solidity 库合约实战:站在巨人的肩膀上复用代码
WTF Solidity 17: Biblioteca (Library) — Solidity 库合约实战站在巨人的肩膀上复用代码【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity本教程是 WTF Solidity 极简入门系列第 17 讲以 OpenZeppelin 风格实现的Strings库合约为核心样例系统讲解 Solidity 库合约library的定义、特性、可见性与delegatecall的底层关系以及using ... for ...指令与按库名直接调用两种使用方式。读者学完后将能读懂 ERC721 等主流合约如何借助库合约减少重复代码、降低 gas并掌握在真实项目中选用常用库合约的能力。什么是库合约库合约library是 Solidity 中一种特殊的合约其存在是为了提升代码的复用性reusabilidad并减少gas消耗。本质上库合约是一系列可复用函数的合集通常由大牛或项目方创作——绝大多数普通开发者并不需要自己编写库合约只需要站在巨人的肩膀上知道在什么场景下选用哪一个库合约即可。在 Languages/es/17_Libreria_es/Library.sol 中可以看到完整的可运行样例文件包含library Strings与调用它的contract UseLibrary可以直接用 Remix 部署验证。与普通合约相比库合约有以下 4 点关键区别不能存在状态变量state variables库合约不存储持久化数据只能使用constant常量不能继承或被继承库合约不参与继承体系不能接收以太币ether库合约没有可用的fallback收款路径不可以被销毁不存在自毁selfdestruct入口。函数可见性与 delegatecall 的关系库合约函数的可见性设置会直接影响其执行方式此细节在中文原版第 17 讲中专门强调西语版同样适用若库函数可见性设置为public或external调用该函数时会触发一次delegatecall即库合约的代码在调用方合约的上下文中执行此时访问的存储是调用合约的存储若可见性设置为internal则不会触发外部调用函数代码会被直接内联inline到调用合约中这也是 OpenZeppelin 库函数默认采用internal pure的原因之一——既省 gas 又安全若可见性为private则该函数仅在库合约内部可见其他合约无法调用。Strings 库合约源码精解Strings库合约是一个将uint256类型转换为对应string类型十进制字符串或十六进制字符串的代码库。它正是ERC721合约所引用的库合约。以下是仓库中西语版教程对应的完整实现// SPDX-License-Identifier: MIT pragma solidity ^0.8.34; library Strings { bytes16 private constant _HEX_SYMBOLS 0123456789abcdef; /** * dev Convierte un uint256 a su representación decimal ASCII string. */ function toString(uint256 value) public 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); } /** * dev Convierte un uint256 a su representación hexadecimal ASCII string. */ function toHexString(uint256 value) public pure returns (string memory) { if (value 0) { return 0x00; } uint256 temp value; uint256 length 0; while (temp ! 0) { length; temp 8; } return toHexString(value, length); } /** * dev Convierte un uint256 a su representación hexadecimal ASCII string con longitud fija. */ function toHexString(uint256 value, uint256 length) public 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] _HEX_SYMBOLS[value 0xf]; value 4; } require(value 0, Strings: insuficiente longitud hexadecimal); return string(buffer); } }该库主要包含两个函数族toString(uint256 value)将uint256转换为10 进制的string。算法分两步先通过循环temp / 10统计十进制位数digits再分配bytes memory buffer new bytes(digits)随后用48 uint256(value % 10)把每一位数字映射为 ASCII 码字符0的 ASCII 码正是 48从低位到高位反向填充缓冲区最后return string(buffer)。toHexString(uint256 value)将uint256转换为16 进制带0x前缀的string。该函数通过temp 8按字节统计所需长度然后调用带长度的重载版本toHexString(value, length)。toHexString(uint256 value, uint256 length)重载按固定长度输出十六进制字符串。缓冲区长度为2 * length 20x占 2 字节从低位开始每次取value 0xf作为_HEX_SYMBOLS的索引得到对应十六进制字符再value 4移动 4 位循环结束后若value ! 0说明传入的length不足会触发require回滚并提示 Strings: insuficiente longitud hexadecimal。在 OpenZeppelin 的完整版本中该库还额外提供了一个toHexString(address addr)重载用于把address20 字节转换为地址形式的十六进制字符串其实现为toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH)其中_ADDRESS_LENGTH 20。仓库中的对应实现可参考 lib/openzeppelin-contracts/contracts/utils/Strings.sol而本教程精简版位于 Languages/es/17_Libreria_es/Library.sol。如何使用库合约两种调用方式教程以Strings库合约的toHexString()为例演示使用库合约函数的两种方法。对应可运行合约UseLibrary同样位于 Languages/es/17_Libreria_es/Library.sol。方式一使用using ... for ...指令通过using A for B;指令可以把库合约 A 中的函数附加到类型 B 的变量上之后即可像调用成员函数一样直接调用。注意调用时该变量会自动作为函数的第一个参数传入无需显式传递contract UseLibrary { // Usando la biblioteca con el comando using for using Strings for uint256; function getString1(uint256 _number) public pure returns(string memory){ // 库合约中的函数自动添加为 uint256 类型变量的成员 return _number.toHexString(); } }这里_number.toHexString()实际上等价于Strings.toHexString(_number)——_number被隐式绑定为第一个参数。方式二通过库合约名称直接调用不使用using指令时也可以像调用普通静态函数一样用库名.函数名的方式显式调用contract UseLibrary { // Llamado directamente por el nombre del contrato de biblioteca function getString2(uint256 _number) public pure returns(string memory){ return Strings.toHexString(_number); } }验证结果部署合约后向getString1和getString2分别输入170测试170的十六进制正是0xaa两种方法均能返回正确的十六进制字符串0xaa证明库合约调用成功。仓库源码佐证ERC721 如何实际引用 Strings库合约并非孤立的概念本教程正是以ERC721 所引用的Strings库为例展开的。仓库中的自制 ERC721 实现 34_ERC721/ERC721.sol 展示了库合约在真实 NFT 合约中的落地方式import ./String.sol; contract ERC721 is IERC721, IERC721Metadata{ using Strings for uint256; // 使用Strings库 ...在实现tokenURI时ERC721 通过tokenId.toString()把数值型的tokenId拼接进元数据 URI见 34_ERC721/ERC721.solreturn bytes(baseURI).length 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : ;这正是using Strings for uint256的典型生产级用法一行指令全合约范围内所有uint256变量即刻获得toString()、toHexString()能力。本仓库配套的 ERC721 专用Strings精简版位于 34_ERC721/String.sol其中还包含toHexString(address)重载用于在ownerOf等场景下把地址格式化为标准显示形式。常用库合约一览99% 的开发者不需要自己编写库合约关键是知道什么场景该用什么库。以下库合约在 OpenZeppelin 中均有成熟实现本仓库的 vendor 副本位于 lib/openzeppelin-contracts/contracts/utils/ 目录下可直接阅读源码学习库合约用途仓库路径Strings将uint256转换为String十进制 / 十六进制 / 地址lib/openzeppelin-contracts/contracts/utils/Strings.solAddress判断某个地址是否为合约地址安全调用任意合约函数functionCall等lib/openzeppelin-contracts/contracts/utils/Address.solCreate2更安全、可预测地址地使用CREATE2EVM 操作码lib/openzeppelin-contracts/contracts/utils/Create2.solArrays与数组操作相关的库函数如查找、防重复检查lib/openzeppelin-contracts/contracts/utils/Arrays.sol以Address为例其核心价值在于sendValue替代已被 EIP-1884 提高 gas 成本而失效的transfer2300 gas 上限functionCall系列则把低层call包装成带错误冒泡bubble revert的安全调用这些在 lib/openzeppelin-contracts/contracts/utils/Address.sol 中均有注释说明。而Create2与Arrays分别服务于确定性地址部署与数组安全操作都是生产合约中的高频需求。总结本讲以ERC721引用的Strings库合约为例介绍了 Solidity 中的库合约Library库合约的本质为提升代码复用、降低 gas 而存在的特殊合约不允许状态变量、不能继承、不能收款、不可销毁可见性决定执行方式public/external库函数触发delegatecall在调用方上下文执行internal直接内联两种调用方式using A for B把库函数附加为类型 B 的成员方法或直接用库名.函数名调用实战验证部署UseLibrary输入170两种方式均返回0xaa且可在 34_ERC721/ERC721.sol 的tokenURI中看到库函数在真实 NFT 合约里的生产级用法。绝大多数开发者无需自行编写库合约掌握Strings、Address、Create2、Arrays等常用库的适用场景就足以在项目开发中站在巨人的肩膀上写出更简洁、更省 gas、更安全的 Solidity 代码。【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考