目录
ERC-165 Standard Interface Detection ,即检测合约是否实现接囗规范,使用函数标识(.selecor)进行检测。参考:EIP-165: Standard Interface Detection
1、如何获取接囗标识符
pragma solidity ^0.4.20;
interface Solidity101 {
function hello() external pure;
function world(int) external pure;
}
contract Selector {
function calculateSelector() public pure returns (bytes4) {
Solidity101 i;
return i.hello.selector ^ i.world.selector;
}
}
通过两种方法可获取函数接囗标识符
(1)i.hello.selector
(2)bytes4(keccak256("hello()"))
增加测试函数进行验证以上(1)与(2)是等价的,代码如下:
pragma solidity ^0.6.0;
interface Solidity101 {
function hello() external pure;
function world(int) external pure;
}
contract Selector {
function calculateSelector() public pure returns (bytes4) {
Solidity101 i;
return i.hello.selector ^ i.world.selector;
}
function testSelector() public pure returns (bytes32,bytes4,bytes4) {
Solidity101 i;
return (keccak256("hello()"), bytes4(keccak256("hello()")), i.hello.selector);
}
}
验证结果如下图所示:
2、合约如何实现接囗
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);
}
- 当
interfaceID = 0x01ffc9a7
(EIP165接囗),返回true- bytes4(keccak256("supportsInterface(bytes4)")); //
0x01ffc9a7
当
interfaceID
=0xffffffff 返回false
合约中实现接囗的interfaceID
,返回true
合约中未实现接囗的
interfaceID,返回false
3、如何检测合约实现了ERC-165接囗
1、在合约地址上使用附加数据(input data)0x01ffc9a701ffc9a700000000000000000000000000000000000000000000000000000000 和 gas 30,000 进行STATICCALL调用,相当于contract.supportsInterface(0x01ffc9a7) ;
2、如果调用失败或返回false , 说明合约不兼容ERC-165标准;
3、如果返回true,则使用输入数据0x01ffc9a7ffffffff000000000000000000000000000000000000000000000000000000000000进行第二次调用;
4、如果第二次调用失败或返回true,则目标合约没有实现ERC-165;
5、否则它实现了ERC-165。
4、如何检测合约实现了指定的接囗
1、如果不确定合约是否实现ERC-165,请使用上面的方法进行确认;
2、如果没有实现ERC-165,那么不得不看看它采用哪种老式方法;
3、如果实现了ERC-165,那么只需调用 supportsInterface(interfaceID) 来确定它是否实现了对应的接口。
测试
pragma solidity ^0.6.0;
interface IERC165 {
/// @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);
}
contract ERC165Test is IERC165 {
//定义一个常量,记录ERC165接囗ID
bytes4 private constant INTERFACE_ID_ERC165 = 0x01ffc9a7;
// //记录接囗注册
mapping(bytes4=>bool) private _supportInterface;
//构造函数,初始化接囗注册
constructor() public {
registerInterface(INTERFACE_ID_ERC165);
}
// //实现接囗方法
function supportsInterface(bytes4 interfaceID) external view override returns (bool) {
return _supportInterface[interfaceID];
}
//注册接囗
function registerInterface(bytes4 interfaceID) public {
require(interfaceID != 0xffffffff, "无效的接囗ID");
_supportInterface[interfaceID] = true;
}
}