Michael.W基于Foundry精读Openzeppelin第26期——ERC1820Implementer.sol

  • Michael.W
  • 更新于 2023-08-14 22:14
  • 阅读 1208

ERC1820Implementer合约是对IERC1820Implementer interface的实现。该合约往往与ERC1820Registry合约配合使用。如果想要合约成为ERC1820Registry记录在案的implementer,需要目标合约继承ERC1820Implementer。

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神。 以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。 同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下! 如果需要转发,麻烦注明作者。十分感谢!

1.jpeg

公众号名称:后现代泼痞浪漫主义奠基人

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Michael.W
Michael.W
0x93E7...0000
狂热的区块链爱好者