1. ERC165简介
简单来说该协议是用来检测智能合约是否实现了某个接口。
2. ERC165的工作原理 ERC165中只定义了一个函数,即supportsInterface()
,如下:
1 2 3 4 5 6 7 8 9 10 11 pragma solidity ^0.4.20; interface ERC165 { /// @notice Query if a contract implements an interface /// @param interfaceID The interface identifier, as specified in ERC-165 /// @dev Interface identification is specified in ERC-165. This function /// uses less than 30,000 gas. /// @return `true` if the contract implements `interfaceID` and /// `interfaceID` is not 0xffffffff, `false` otherwise function supportsInterface(bytes4 interfaceID) external view returns (bool); }
简单来说,如果要判断A是否实现了SUPER接口,则A需要实现ERC165中的函数,判断原理则是,调用A中的supportsInterface
函数,该函数会判等传入的参数,如果A实现了SUPER的接口,那么函数体的内容应该是:
1 2 3 function supportsInterface(bytes4 interfaceID) external view returns (bool) { return type(SUPER).interfaceId == interfaceID; }
这样就可以初步判断该合约实现了某一接口,但是仅仅初步,因为你可以自己编写函数体的返回值,比如我没有实现B接口,我可以自己设置返回值为:
1 2 3 function supportsInterface(bytes4 interfaceID) external view returns (bool) { return type(B).interfaceId == interfaceID; }
而合约中并没有实现B接口中函数。比如,别人问你有钱吗,你甩出一张银行卡说里面有一个“小目标”,当然因为你看不见银行卡里面有多少钱,你暂且可以相信你有钱。就好比调用supportsInterface
函数来初步判断。具体是否实现这些函数,还得具体调用某些功能来检测才行。
3. ERC165接口的计算原理 - 一个接口的的interfaceID是通过,计算接口中所有函数选择器的异或值,即Dog的interfaceID计算如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // SPDX-License-Identifier: MIT pragma solidity 0.8.17; interface Dog { function eat() external; function shout() external; } contract Test { function calDogInterfaceID() external pure returns(bytes4, bytes4) { bytes4 result = Dog.eat.selector ^ Dog.shout.selector; return (type(Dog).interfaceId, result); } }
当然也会有接口继承的时候,假如Cat接口继承了Animals接口,那么判断是否实现Cat接口该如何计算interfaceId,只需要计算在Cat接口中定义的函数即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 // SPDX-License-Identifier: MIT pragma solidity 0.8.17; interface Animals { function run() external; function breath() external; } interface Dog { function eat() external; function shout() external; } contract Test { function calDogInterfaceID() external pure returns(bytes4, bytes4) { bytes4 result = Dog.eat.selector ^ Dog.shout.selector; return (type(Dog).interfaceId, result); } }
根据运行结果可验证: