十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

WTF-Solidity 合约工程化指南:Solidity 抽象合约(abstract)与接口(interface)实战解析

WTF-Solidity 合约工程化指南:Solidity 抽象合约(abstract)与接口(interface)实战解析 WTF-Solidity 合约工程化指南Solidity 抽象合约abstract与接口interface实战解析【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity导读本教程是 WTF-Solidity 极简入门系列的第 14 讲聚焦 Solidity 中两大合约抽象工具abstract抽象合约与interface接口。它们用于编写合约模板、强制约定实现规范并大幅减少代码冗余是理解 ERC 系列标准ERC20 / ERC721 / ERC1155以及实现合约间无源码交互的必备基础。读完本讲你将掌握抽象合约与接口的语法规则、IERC721接口的完整结构3 个事件 9 个函数、bytes4函数选择器与接口 ID 的来源并学会仅凭接口与合约地址调用链上任意标准合约如 BAYC的方法。为什么需要抽象合约与接口在 Solidity 开发中不同项目往往共享同一套业务骨架例如所有 ERC721 NFT 都拥有balanceOf、ownerOf、transferFrom、approve等函数。如果每个项目都从零实现一遍既容易出错也无法形成统一的调用规范。抽象合约与接口正是为此而生——它们负责“定义形状”把具体实现交给子合约。这样开发者可以编写模板合约先确定函数签名实现细节后续再补Dapp / 智能合约之间可以不依赖源码地互操作只要知道对方实现了某个接口就能按接口签名安全调用项目间通过统一的接口约束如 ERC20、ERC721形成可组合的生态。本讲的配套示例代码位于 Languages/en/14_Interface_en/ 目录含 AbstractDemo.sol、Interface.sol、InterfaceDemo.sol 三个可编译文件主教程文档为 Languages/en/14_Interface_en/readme.md。抽象合约abstract contract语法规则未实现函数必须配合 abstract如果一个合约中至少包含一个未实现的函数即函数体{}为空或缺省该合约就必须被标记为abstract否则编译会直接报错。同时未实现的函数必须加上virtual关键字以便子合约后续重写override。以一个还没想好排序算法怎么写的插入排序合约为例abstract contract InsertionSort{ function insertionSort(uint[] memory a) public pure virtual returns(uint[] memory); }注意这里insertionSort以分号;结尾而非函数体{ }这正是未实现的语法标志。后续由他人继承并补全实现即可。源码佐证Base / BaseImpl 完整示例仓库中 Languages/en/14_Interface_en/AbstractDemo.sol 给出了一个更完整的抽象合约示例——抽象合约可以同时拥有已实现的状态变量与未实现的纯虚函数// SPDX-License-Identifier: MIT pragma solidity ^0.8.34; abstract contract Base{ string public name Base; function getAlias() public pure virtual returns(string memory); } contract BaseImpl is Base{ function getAlias() public pure override returns(string memory){ return BaseImpl; } }要点Base中的name是普通状态变量getAlias()是未实现的纯虚函数子合约BaseImpl用override关键字补全了getAlias()的实现注意函数可见性、状态可变性pure必须与父抽象合约保持一致否则编译不通过。抽象合约的适用场景业务骨架未定型先固化函数签名如本讲示例中的InsertionSort多个子合约共享部分逻辑可把公共实现放在抽象合约里各自只重写差异部分作为库或框架提供给他人继承扩展。接口interface接口的定义与五条铁律接口与抽象合约类似但更加严格它不实现任何功能只声明。接口必须遵守以下规则不能包含状态变量不能包含构造函数不能继承除接口外的其他合约即只能is其他接口所有函数都必须是external且不能有函数体继承接口的非抽象合约必须实现接口中定义的所有功能。从源码看Languages/en/14_Interface_en/InterfaceDemo.sol 演示了最小接口与实现// SPDX-License-Identifier: MIT pragma solidity ^0.8.34; interface Base { function getFirstName() external pure returns(string memory); function getLastName() external pure returns(string memory); } contract BaseImpl is Base{ function getFirstName() external pure override returns(string memory){ return Amazing; } function getLastName() external pure override returns(string memory){ return Ang; } }接口为什么如此重要虽然接口不实现任何功能但它定义了合约的骨架合约能做什么以及外部如何触发。如果某个合约实现了IERC721接口其他 Dapp 和智能合约无需阅读其源码即可通过接口与它交互。接口对外提供两类关键信息函数签名与bytes4选择器每个函数的函数签名形如函数名(每个参数类型)对其做 Keccak-256 哈希并取前 4 字节即得到函数选择器bytes4。EVM 正是靠它路由交易调用。接口 IDinterface id由接口内全部函数选择器异或XOR得到用于 EIP-165 标准的能力自检supportsInterface。接口与 ABI 等价接口在形态上等价于合约的ABIApplication Binary Interface二者可以相互转换编译接口合约即可得到对应的合约 ABI JSON反过来也可以利用 abi-to-sol 工具将 ABI JSON 还原成接口 Solidity 文件。这意味着拿到一个已部署合约的 ABI就等于拿到了它的接口拿到接口也就知道了它与链上交互的全部签名信息。IERC721以 ERC721 标准接口为范例接口总览本讲以 ERC721 标准接口IERC721为例。仓库中该接口有两处实现教程目录中的 Languages/en/14_Interface_en/Interface.sol 与 ERC721 专题目录 34_ERC721/IERC721.sol两者内容一致。接口继承自IERC165见 34_ERC721/IERC165.sol包含3 个事件和 9 个函数所有 ERC721 标准 NFT 都必须实现它们interface IERC721 is IERC165 { event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); event ApprovalForAll(address indexed owner, address indexed operator, bool approved); function balanceOf(address owner) external view returns (uint256 balance); function ownerOf(uint256 tokenId) external view returns (address owner); function safeTransferFrom(address from, address to, uint256 tokenId) external; function transferFrom(address from, address to, uint256 tokenId) external; function approve(address to, uint256 tokenId) external; function getApproved(uint256 tokenId) external view returns (address operator); function setApprovalForAll(address operator, bool _approved) external; function isApprovedForAll(address owner, address operator) external view returns (bool); function safeTransferFrom( address from, address to, uint256 tokenId, bytes calldata data) external; }与常规合约的显著区别接口中每个函数都以;代替函数体{ }结尾。此外接口中的函数默认就是virtual的无需显式标注子合约仍需写override。IERC721 的三个事件事件触发时机记录内容Transfer转账时发出地址from、接收地址to、tokenIdApproval单币授权时授权方owner、被授权地址approved、tokenIdApprovalForAll批量授权时批量授权发出方owner、被授权地址operator、是否启用approved三个事件均将关键参数标记为indexed便于链下索引与过滤。其中Transfer与Approval事件在 ERC20 标准中同样存在是代币生态最通用的事件原语。IERC721 的九个函数函数作用balanceOf(owner)返回某地址持有的 NFT 数量ownerOf(tokenId)返回某tokenId的持有人transferFrom(from, to, tokenId)普通转账从from转给tosafeTransferFrom(from, to, tokenId)安全转账若接收方是合约地址要求其实现ERC721Receiver接口见下方说明approve(to, tokenId)授权另一个地址使用你指定的某个 NFTgetApproved(tokenId)查询某个tokenId被授权给了哪个地址setApprovalForAll(operator, approved)将自己持有的该系列 NFT 批量授权给operatorisApprovedForAll(owner, operator)查询owner是否把 NFT 批量授权给了operatorsafeTransferFrom(from, to, tokenId, data)安全转账的重载版本参数中额外携带datasafeTransferFrom的安全体现在接收方检查当接收方是合约地址时对方必须实现 34_ERC721/IERC721Receiver.sol 中定义的onERC721Received回调并返回正确的bytes4魔数否则转账回滚。这可以防止 NFT 被转入无法操作它的合约而永久锁死。从接口到实现ERC721 源码印证接口只是骨架真正落地需要具体实现。仓库 34_ERC721/ERC721.sol 是一个完整的ERC721 is IERC721, IERC721Metadata实现可以用它对照接口验证五条规则如何被执行状态变量承载数据_owners、_balances、_tokenApprovals、_operatorApprovals四个映射34_ERC721/ERC721.sol接口函数逐一override实现例如balanceOf读取_balances[owner]34_ERC721/ERC721.sol、setApprovalForAll写映射并释放ApprovalForAll事件34_ERC721/ERC721.solsupportsInterface利用type(IERC721).interfaceId等编译期常量完成 EIP-165 能力自检34_ERC721/ERC721.sol。而 34_ERC721/WTFApe.sol 则是在ERC721基类之上再派生只需补充MAX_APES、_baseURI()与mint()就构成了一个功能完整的 NFT 项目。这一层层叠加的结构正是抽象合约 / 接口定义骨架、具体合约填充实现工程思想的直接体现。什么时候使用接口无源码交互 BAYC接口的最大实战价值只要知道合约地址且确认它实现了某接口就能安全交互无需阅读其源码。Bored Ape Yacht ClubBAYC是典型的 ERC721 代币实现了IERC721接口的全部函数。我们只需要它的合约地址ETH 主网0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D就能通过IERC721接口完成查询与转账contract interactBAYC { // 利用 BAYC 地址创建接口合约变量ETH 主网 IERC721 BAYC IERC721(0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D); // 通过接口调用 BAYC 的 balanceOf() 查询持仓量 function balanceOfBAYC(address owner) external view returns (uint256 balance){ return BAYC.balanceOf(owner); } // 通过接口调用 BAYC 的 safeTransferFrom() 安全转账 function safeTransferFromBAYC(address from, address to, uint256 tokenId) external{ BAYC.safeTransferFrom(from, to, tokenId); } }这段代码与接口声明一同收录于 Languages/en/14_Interface_en/Interface.sol。核心手法是将地址显式转换为接口类型IERC721(0xBC4C...)之后所有调用都遵循IERC721的函数签名走 EVM 的bytes4选择器路由与 BAYC 合约内部实现细节完全解耦。在 Remix 上验证抽象合约示例将抽象合约Base与实现合约BaseImpl见 Languages/en/14_Interface_en/AbstractDemo.sol一并粘贴到 Remix编译部署BaseImpl后调用getAlias()返回BaseImpl直接尝试编译只有未实现函数的abstract contract则会看到强制 abstract 的编译错误。接口示例将接口Base与实现合约BaseImpl见 Languages/en/14_Interface_en/InterfaceDemo.sol粘贴到 Remix编译部署后调用getFirstName()与getLastName()分别返回Amazing与Ang若实现合约遗漏任一接口函数编译会因未实现接口全部功能而失败。总结本讲介绍了 Solidity 中的两大抽象工具抽象合约abstract允许保留未实现函数需标virtual用于编写模板、固化骨架接口interface只声明不实现规则更严格无状态变量、无构造函数、函数必须external且无函数体用于定义标准与实现无源码交互。两者都能写模板并减少代码冗余。同时我们以IERC721为例剖析了 ERC721 标准接口的 3 个事件与 9 个函数并演示了如何仅凭接口与合约地址与链上 BAYC 合约交互。掌握了本讲内容你就具备了解读任何 ERC 标准ERC20、ERC721、ERC1155、ERC4626 等并与其链上实例安全交互的底层能力——仓库中 31_ERC20/IERC20.sol、34_ERC721/IERC721.sol、40_ERC1155/IERC1155.sol 等接口文件都是进一步练习的最佳素材。【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表