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) 公众号名称:后现代泼痞浪漫主义奠基人

相关推荐
Blockchina3 天前
第 4 章 | Solidity安全 权限控制漏洞全解析
安全·web3·区块链·智能合约·solidity
Blockchina4 天前
第十四章 | DeFi / DAO / GameFi 项目高级实战
web3·区块链·智能合约·solidity
谭光志4 天前
如何估算和优化 Gas
web3·区块链·solidity
0x派大星6 天前
打造更安全的区块链资产管理:Solidity 多重签名机制详解
安全·web3·区块链·智能合约·solidity
Blockchina6 天前
第十五章 | Layer2、Rollup 与 ZK 技术实战解析
python·web3·区块链·智能合约·solidity
Blockchina7 天前
第 2 章 | 智能合约攻击图谱全景解析
web3·区块链·智能合约·solidity·区块链安全
Blockchina7 天前
第 1 章 | 开篇词:Dapp安全 区块链安全 Web3安全 区块链合约一旦部署,安全就是生死线
安全·web3·区块链·智能合约·solidity·合约审计
Blockchina9 天前
第八章 | 函数修饰符与访问控制模式
java·python·区块链·智能合约·solidity
Blockchina9 天前
第十二章 | Solidity 智能合约前后端集成实战
java·python·区块链·智能合约·solidity