了解过 NFT 合约的同学应该注意到,每个 NFT 合约都实现了 supportsInterface 方法,但是每个合约中的具体方法实现又不尽相同,那么在实际开发过程中,究竟应该怎么实现这个方法呢,这篇文章就来讲讲这个内容
ERC165的具体内容这里就不再多说了,不了解的同学可以 Google 一下
首先我们需要知道计算一个接口的 interfaceId 都有几种方法。
假设对于如下接口:
interface IExample {
function foo() external view returns (uint);
function bar() external view returns (bool);
function baz() external view returns (bytes32);
}
那么可以这样计算它的 interfaceId
function calcInterfaceId1() public view returns (bytes4) {
return bytes4(keccak256("foo()"))
^ bytes4(keccak256("bar()"))
^ bytes4(keccak256("baz()"));
}
function calcInterfaceId2() public view returns (bytes4) {
IExample i;
return i.foo.selector ^ i.bar.selector ^ i.baz.selector;
}
function calcInterfaceId3() public view returns (bytes4) {
return type(IExample).interfaceId;
}
这三种方法都可以用来计算 interfaceId,结果相同,都是 0x9bb235aa。
接下来,我们看看实际开发中都是怎么实现 supportsInterface 方法的。
来看几个例子:
// 合约继承了 ERC721,实现了 IERC2981接口
contract InvisibleFriends is ERC721, IERC2981, Ownable, ReentrancyGuard {
}
// override 后面包括 ERC721 和 IERC2981 的最上层基类 IERC165
function supportsInterface(bytes4 interfaceId)
public
view
override(ERC721, IERC165)
returns (bool)
{
// 需要显式声明 IERC2981
return
interfaceId == type(IERC2981).interfaceId ||
super.supportsInterface(interfaceId);
}
合约实现的基类中,ERC721 和 IERC2981 中都包括 IERC165,因此需要显式重写 supportsInterface 方法。override后面的括号需要包含 ERC721,由于 IERC2981 是接口,因此要包含 IERC2981 的最上层基类 IERC165,同时需要在方法实现中显示声明 IERC2981 接口本身。
// 合约继承了 ERC721,ERC721Enumerable
contract mfers is ERC721, ERC721Enumerable, Ownable {
}
// override 后面包括 ERC721 和 ERC721Enumerable
function supportsInterface(bytes4 interfaceId)
public
view
virtual
override(ERC721, ERC721Enumerable)
returns (bool)
{
// 无需显式声明接口
return super.supportsInterface(interfaceId);
}
合约实现的基类中,ERC721 和 ERC721Enumerable 中都包括 IERC165。因此需要显式重写 supportsInterface 方法。而由于两者均非接口,因此直接在 override 后面声明。在方法实现中无需显式声明。
// 合约继承了 ERC721
contract TastyBones is Ownable, ERC721A, ReentrancyGuard {
}
// 没有显式实现 supportsInterface 方法
合约实现的基类中,只有 ERC721A 一个合约包括 IERC165 接口,因此合约的最终实现无需显式实现 supportsInterface 方法。
这三个合约基本上代表了几种具体实现方式,总结一下: