Michael.W基于Foundry精读Openzeppelin第21期——ERC165.sol

Michael.W基于Foundry精读Openzeppelin第21期------ERC165.sol

      • [0. 版本](#0. 版本)
        • [0.1 ERC165.sol](#0.1 ERC165.sol)
      • [1. 目标合约](#1. 目标合约)
      • [2. 代码精读](#2. 代码精读)
        • [2.1 IERC165](#2.1 IERC165)
          • [2.1.1 interfaceId的计算方式](#2.1.1 interfaceId的计算方式)
        • [2.2 ERC165](#2.2 ERC165)
          • [2.2.1 supportsInterface(bytes4 interfaceId)](#2.2.1 supportsInterface(bytes4 interfaceId))
          • [2.2.2 foundry代码验证](#2.2.2 foundry代码验证)

0. 版本

openzeppelin\]:v4.8.3,\[forge-std\]:v1.5.6 #### 0.1 ERC165.sol Github: ERC165合约是IERC165的标准实现。ERC165提供了本合约是否实现了IERC165接口的查询。如果需要额外支持其他interface,可在目标合约内重写`supportsInterface(bytes4)`方法。 ### 1. 目标合约 继承ERC165成为一个可调用合约: Github: ```js // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; import "openzeppelin-contracts/contracts/utils/introspection/ERC165.sol"; interface ICustomizedInterface { error CustomizedError(); event CustomizedEvent(uint n); function viewFunction(address addr) external view returns (address); function pureFunction(uint n) external pure returns (uint); function externalFunction(uint[] calldata nums, address[] calldata addrs) external; } contract MockERC165 is ERC165, ICustomizedInterface { uint _totalSum; uint _totalNumberOfAddress; // implementation of ICustomizedInterface function viewFunction(address addr) external view returns (address){ if (addr == msg.sender) { return addr; } return address(1024); } function pureFunction(uint n) external pure returns (uint){ return n + 1; } function externalFunction(uint[] calldata nums, address[] calldata addrs) external { for (uint i; i < nums.length; ++i) { _totalSum += nums[i]; } _totalNumberOfAddress = addrs.length; } // override supportsInterface(bytes4) of IERC165 function supportsInterface(bytes4 interfaceId) public view override returns (bool) { return interfaceId == type(ICustomizedInterface).interfaceId || super.supportsInterface(interfaceId); } } ``` 同时该合约实现了一个自定义interface------ICustomizedInterface。 全部foundry测试合约: Github: ### 2. 代码精读 #### 2.1 IERC165 通过定义interfaceId作为interface的唯一标记。IERC165主要解决了目标合约已实现接口的检测问题------IERC165的实现合约提供了自身已实现interface的查询功能并且可以被其他继承`ERC165Checker`的合约识别。 具体详情参见:https://eips.ethereum.org/EIPS/eip-165 注:IERC165中要求方法`supportsInterface(bytes4)`的gas消耗必须小于30000 gas。 ##### 2.1.1 interfaceId的计算方式 interfaceId为interface的标识,有3种方法可以求得一个interface的interfaceId: 1. solidity内置的`type(interface).interfaceId`。以ICustomizedInterface为例: ```js bytes4 interfaceIdICustomizedInterface = type(ICustomizedInterface).interfaceId; ``` 2. interface内包含的所有函数的selector的异或结果。以ICustomizedInterface为例: ```js bytes4 interfaceIdICustomizedInterface = ICustomizedInterface.viewFunction.selector ^ ICustomizedInterface.pureFunction.selector ^ ICustomizedInterface.externalFunction.selector; ``` 3. interface内包含的所有函数的函数签名的异或结果。以ICustomizedInterface为例: ```js bytes4 interfaceIdICustomizedInterface = bytes4(keccak256('viewFunction(address)')) ^ bytes4(keccak256('pureFunction(uint256)')) ^ bytes4(keccak256('externalFunction(uint256[],address[])')); ``` #### 2.2 ERC165 ##### 2.2.1 supportsInterface(bytes4 interfaceId) 对外提供本合约是否实现了传入interfaceId标识的interface的查询功能。 ```js function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { // 如果传入的interfaceId为IERC165的interfaceId返回true,否则返回false return interfaceId == type(IERC165).interfaceId; } ``` 注:ERC165只支持对interface IERC165的确认查询,如果需要额外支持其他interface,可在目标合约内重写该方法。具体例子可见后面的foundry测试代码。 ##### 2.2.2 foundry代码验证 ```js contract ERC165Test is Test { MockERC165 me = new MockERC165(); function test_SupportsInterface_IERC165() external { // way 1: from function selector bytes4 interfaceIdIERC165 = IERC165.supportsInterface.selector; assertTrue(me.supportsInterface(interfaceIdIERC165)); // way 2: from hash result of function signature interfaceIdIERC165 = bytes4(keccak256('supportsInterface(bytes4)')); assertTrue(me.supportsInterface(interfaceIdIERC165)); // way 3: from type(interface).interfaceId interfaceIdIERC165 = type(IERC165).interfaceId; assertTrue(me.supportsInterface(interfaceIdIERC165)); // not support bytes4 interfaceIdNotSupport = 0xffffffff; assertFalse(me.supportsInterface(interfaceIdNotSupport)); } function test_SupportsInterface_ICustomizedInterface() external { // way 1: from XOR of all function selectors in the interface bytes4 interfaceIdICustomizedInterface = ICustomizedInterface.viewFunction.selector ^ ICustomizedInterface.pureFunction.selector ^ ICustomizedInterface.externalFunction.selector; assertTrue(me.supportsInterface(interfaceIdICustomizedInterface)); // way 2: from XOR of all hash result of function signature interfaceIdICustomizedInterface = bytes4(keccak256('viewFunction(address)')) ^ bytes4(keccak256('pureFunction(uint256)')) ^ bytes4(keccak256('externalFunction(uint256[],address[])')); assertTrue(me.supportsInterface(interfaceIdICustomizedInterface)); // way 3: from type(interface).interfaceId interfaceIdICustomizedInterface = type(ICustomizedInterface).interfaceId; assertTrue(me.supportsInterface(interfaceIdICustomizedInterface)); // not support bytes4 interfaceIdNotSupport = ICustomizedInterface.pureFunction.selector ^ ICustomizedInterface.externalFunction.selector; assertFalse(me.supportsInterface(interfaceIdNotSupport)); } function test_SupportsInterface_Gas() external { // for type(IERC165).interfaceId uint startGas = gasleft(); me.supportsInterface(type(IERC165).interfaceId); uint endGas = gasleft(); assertLt(startGas - endGas, 30000); // for type(ICustomizedInterface).interfaceId startGas = gasleft(); me.supportsInterface(type(ICustomizedInterface).interfaceId); endGas = gasleft(); assertLt(startGas - endGas, 30000); } } ``` ps: 本人热爱图灵,热爱中本聪,热爱V神。 以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。 同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下! 如果需要转发,麻烦注明作者。十分感谢! ![在这里插入图片描述](https://file.jishuzhan.net/article/1688386736633155586/a5a50c1511224f55adc44f1626bb3f1a.jpeg) 公众号名称:后现代泼痞浪漫主义奠基人

相关推荐
天涯学馆1 天前
Solidity自毁合约:让你的区块链代码优雅谢幕
智能合约·solidity·以太坊
小攻城狮长成ing2 天前
从0开始学区块链第10天—— 写第二个智能合约 FundMe
web3·区块链·智能合约·solidity
野老杂谈2 天前
【Solidity 从入门到精通】第3章 Solidity 基础语法详解
web3·solidity
野老杂谈2 天前
【Solidity 从入门到精通】第2章 Solidity 语言概览与环境搭建
web3·区块链·智能合约·solidity·remix ide
野老杂谈3 天前
【Solidity 从入门到精通】前言
web3·智能合约·solidity·以太坊·dapp
许强0xq4 天前
Solidity 的十年与重生:从 Classic 到 Core
web3·区块链·智能合约·solidity·以太坊
小攻城狮长成ing5 天前
从0开始学区块链第12天—如何使用可见性标识符
web3·区块链·智能合约·solidity·以太坊
野老杂谈8 天前
如何快速学习智能合约开发语言 Solidity
开发语言·学习·智能合约·solidity·以太坊·区块链开发
本郡主是喵14 天前
Truffle 合约编译与部署:从.sol 文件到上链全流程
区块链·solidity·truffle
许强0xq16 天前
Gas优化大师目录
web3·区块链·智能合约·solidity·foundry·ethernaut·gas优化