Michael.W基于Foundry精读Openzeppelin第26期——ERC1820Implementer.sol
- 0. 版本
- 0.1 ERC1820Implementer.sol
- 1. 目标合约
- 2. 代码精读
- 2.1 _registerInterfaceForAddress(bytes32 interfaceHash, address account) internal
- 2.2 canImplementInterfaceForAddress(bytes32 interfaceHash, address account)
- 2.3 foundry代码验证
0. 版本
[openzeppelin]:v4.8.3,[forge-std]:v1.5.6
0.1 ERC1820Implementer.sol
Github: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.3/contracts/utils/introspection/ERC1820Implementer.sol
ERC1820Implementer合约是对IERC1820Implementer interface的实现。该合约往往是与ERC1820Registry合约配合使用。
如果想要合约成为ERC1820Registry记录在案的implementer,需要目标合约继承ERC1820Implementer并且调用_registerInterfaceForAddress()向外公布自己的implementer意愿。之后,ERC1820Registry中的对应account的管理员再调用ERC1820Registry.setInterfaceImplementer()进行注册确认。
1. 目标合约
继承ERC1820Implementer成为一个可调用合约:
Github: https://github.com/RevelationOfTuring/foundry-openzeppelin-contracts/blob/master/src/utils/introspection/MockERC1820Implementer.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "openzeppelin-contracts/contracts/utils/introspection/ERC1820Implementer.sol";
import "openzeppelin-contracts/contracts/token/ERC721/ERC721.sol";
contract MockERC1820Implementer is ERC1820Implementer, ERC721("", "") {
function registerInterfaceForAddress(bytes32 interfaceHash, address account) external {
_registerInterfaceForAddress(interfaceHash, account);
}
}
全部foundry测试合约:
Github: https://github.com/RevelationOfTuring/foundry-openzeppelin-contracts/blob/master/test/utils/introspection/ERC1820Implementer.t.sol
2. 代码精读
2.1 _registerInterfaceForAddress(bytes32 interfaceHash, address account) internal
设置本合约成为account名下的interfaceHash的implementer。
// magic值(常量)
bytes32 private constant _ERC1820_ACCEPT_MAGIC = keccak256("ERC1820_ACCEPT_MAGIC");
// 用于记录本implementer合约在ERC1820 register中的映射关系的mapping
// ERC1820 register中记录的映射关系:address名下,interface hash => implementer address
// 由于一个implementer可以由多个address共享,所以:
// - 第一个key为interface hash;
// - 第二个key为所属名下的address;
// - 第二个key对应的value表示本合约地址是否是address(第二个key)名下的interface hash(第一个key)的implementer
mapping(bytes32 => mapping(address => bool)) private _supportedInterfaces;
function _registerInterfaceForAddress(bytes32 interfaceHash, address account) internal virtual {
// interfaceHash作为第一个key,account作为第二个key,对应的value设置为true。这样,本合约在面对ERC1820 register的查询时可以二次确认自己与account和interfaceHash之间的关系
_supportedInterfaces[interfaceHash][account] = true;
}
2.2 canImplementInterfaceForAddress(bytes32 interfaceHash, address account)
对外提供的查询方法。如果本合约承认自己是account名下的interfaceHash的implementer,返回magic值。否则返回0值。
function canImplementInterfaceForAddress(bytes32 interfaceHash, address account)
public
view
virtual
override
returns (bytes32)
{
// 如果mapping _supportedInterfaces中记录的第二个key的value为true,返回magic值,否则返回bytes32(0x00)
return _supportedInterfaces[interfaceHash][account] ? _ERC1820_ACCEPT_MAGIC : bytes32(0x00);
}
2.3 foundry代码验证
contract ERC1820ImplementerTest is Test {
MockERC1820Implementer mei = new MockERC1820Implementer();
ERC1820Registry er = new ERC1820Registry();
bytes32 constant ERC1820_ACCEPT_MAGIC = keccak256("ERC1820_ACCEPT_MAGIC");
function test_ERC1820Implementer() external {
address account = address(1024);
bytes32 interfaceHashERC721 = er.interfaceHash("ERC721Token");
// no registered interface
assertEq(0, mei.canImplementInterfaceForAddress(interfaceHashERC721, account));
// revert when set implementer in ERC1820Registry before register operation in ERC1820 implementer
vm.prank(account);
er.setManager(account, address(this));
vm.expectRevert("Does not implement the interface");
er.setInterfaceImplementer(account, interfaceHashERC721, address(mei));
// register interface between ERC1820Registry and ERC1820Implementer
// 1. register willing in ERC1820Implementer
mei.registerInterfaceForAddress(interfaceHashERC721, account);
assertEq(ERC1820_ACCEPT_MAGIC, mei.canImplementInterfaceForAddress(interfaceHashERC721, account));
// query for interface hash not registered
bytes32 interfaceHashOther = er.interfaceHash("ERC20Token");
assertNotEq(ERC1820_ACCEPT_MAGIC, mei.canImplementInterfaceForAddress(interfaceHashOther, account));
// query for account not registered
assertNotEq(ERC1820_ACCEPT_MAGIC, mei.canImplementInterfaceForAddress(interfaceHashERC721, address(1024 + 1)));
// 2. set implementer in ERC1820Registry
er.setInterfaceImplementer(account, interfaceHashERC721, address(mei));
// 3. check from ERC1820Registry
assertEq(address(mei), er.getInterfaceImplementer(account, interfaceHashERC721));
}
}
ps:
本人热爱图灵,热爱中本聪,热爱V神。
以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。
同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下!
如果需要转发,麻烦注明作者。十分感谢!
公众号名称:后现代泼痞浪漫主义奠基人