ERC-1820: 伪自省注册表合约
    
    
      
        
       
    
    
      
        
       
    
  
  
  | Authors | Jordi Baylina <jordi@baylina.cat>, Jacques Dafflon <mail@0xjac.com> | 
|---|---|
| Created | 2019-03-04 | 
| Requires | EIP-165, EIP-214 | 
Table of Contents
:information_source: ERC-1820 已经取代了 ERC-820。 :information_source: ERC-1820 修复了 Solidity 0.5 更新引入的 ERC-165 逻辑中的不兼容性。 请查看官方公告,以及关于错误和修复的评论。 除了这个修复,ERC-1820 在功能上等同于 ERC-820。
概要
本标准定义了一个通用的注册表智能合约,任何地址(合约或常规账户)都可以在其中注册它支持哪个接口,以及哪个智能合约负责它的实现。
本标准保持了与 ERC-165 的向后兼容性。
摘要
本标准定义了一个注册表,智能合约和常规账户可以在其中发布它们实现的哪些功能——无论是直接实现还是通过代理合约实现。
任何人都可以查询此注册表,以询问特定地址是否实现了给定的接口,以及哪个智能合约处理它的实现。
这个注册表可以部署在任何链上,并在所有链上共享相同的地址。
最后 28 个字节为零 (0) 的接口被认为是 ERC-165 接口,
并且此注册表应将调用转发到合约,以查看它是否实现了该接口。
此合约还充当 ERC-165 缓存,以减少 gas 消耗。
动机
在 Ethereum 中已经有不同的方法来定义伪自省。 第一种是 ERC-165,它的局限性在于它不能被常规账户使用。 第二次尝试是 ERC-672,它使用反向 ENS。 使用反向 ENS 有两个问题。 首先,它不必要地复杂,其次,ENS 仍然是由多重签名控制的中心化合约。 理论上,这个多重签名能够修改系统。
这个标准比 ERC-672 简单得多,并且是完全去中心化的。
这个标准还为所有链提供了一个唯一的地址。 从而解决了为不同的链解析正确的注册表地址的问题。
规范
ERC-1820 注册表智能合约
这是 [ERC1820 注册表智能合约] 代码的精确副本。
/* ERC1820 伪自省注册表合约
 * 本标准定义了一个通用的注册表智能合约,任何地址(合约或常规帐户)都可以在其中
 * 注册它支持的接口以及哪个智能合约负责它的实现。
 *
 * 由 Jordi Baylina 和 Jacques Dafflon 于 2019 年编写
 *
 * 在法律允许的范围内,作者已将本软件的所有版权和相关及邻近权利
 * 贡献给全世界的公共领域。 本软件不提供任何保证地分发。
 *
 * 您应该已收到随本软件一起提供的 CC0 公共领域贡献副本。 如果没有,请参见
 * <http://creativecommons.org/publicdomain/zero/1.0/>.
 *
 *    ███████╗██████╗  ██████╗ ██╗ █████╗ ██████╗  ██████╗
 *    ██╔════╝██╔══██╗██╔════╝███║██╔══██╗╚════██╗██╔═████╗
 *    █████╗  ██████╔╝██║     ╚██║╚█████╔╝ █████╔╝██║██╔██║
 *    ██╔══╝  ██╔══██╗██║      ██║██╔══██╗██╔═══╝ ████╔╝██║
 *    ███████╗██║  ██║╚██████╗ ██║╚█████╔╝███████╗╚██████╔╝
 *    ╚══════╝╚═╝  ╚═╝ ╚═════╝ ╚═╝ ╚════╝ ╚══════╝ ╚═════╝
 *
 *    ██████╗ ███████╗ ██████╗ ██╗███████╗████████╗██████╗ ██╗   ██╗
 *    ██╔══██╗██╔════╝██╔════╝ ██║██╔════╝╚══██╔══╝██╔══██╗╚██╗ ██╔╝
 *    ██████╔╝█████╗  ██║  ███╗██║███████╗   ██║   ██████╔╝ ╚████╔╝
 *    ██╔══██╗██╔══╝  ██║   ██║██║╚════██║   ██║   ██╔══██╗  ╚██╔╝
 *    ██║  ██║███████╗╚██████╔╝██║███████║   ██║   ██║  ██║   ██║
 *    ╚═╝  ╚═╝╚══════╝ ╚═════╝ ╚═╝╚══════╝   ╚═╝   ╚═╝  ╚═╝   ╚═╝
 *
 */
pragma solidity 0.5.3;
// IV 是需要具有以 '0x1820' 开头的 vanity 地址的值。
// IV: 53759
/// @dev 如果合约是任何地址(除了自身)的某个(其他)接口的实现者,则合约必须实现的接口。
interface ERC1820ImplementerInterface {
    /// @notice 指示合约是否为地址“addr”实现了接口“interfaceHash”。
    /// @param interfaceHash 接口名称的 keccak256 哈希值
    /// @param addr 合约将为其实现接口的地址
    /// @return 仅当合约为地址“addr”实现“interfaceHash”时才返回 ERC1820_ACCEPT_MAGIC。
    function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view returns(bytes32);
}
/// @title ERC1820 伪自省注册表合约
/// @author Jordi Baylina 和 Jacques Dafflon
/// @notice 此合约是 ERC1820 注册表的官方实现。
/// @notice 更多详情,请参见 https://eips.ethereum.org/EIPS/eip-1820
contract ERC1820Registry {
    /// @notice ERC165 无效 ID。
    bytes4 constant internal INVALID_ID = 0xffffffff;
    /// @notice ERC165 supportsInterface 方法的 Method ID (= `bytes4(keccak256('supportsInterface(bytes4)'))`)。
    bytes4 constant internal ERC165ID = 0x01ffc9a7;
    /// @notice 如果合约代表其他地址实现接口,则返回的 Magic value。
    bytes32 constant internal ERC1820_ACCEPT_MAGIC = keccak256(abi.encodePacked("ERC1820_ACCEPT_MAGIC"));
    /// @notice 从地址和接口哈希到它们的实现者的映射。
    mapping(address => mapping(bytes32 => address)) internal interfaces;
    /// @notice 从地址到其管理器的映射。
    mapping(address => address) internal managers;
    /// @notice 用于指示是否对每个地址和 erc165 接口进行缓存的标志。
    mapping(address => mapping(bytes4 => bool)) internal erc165Cached;
    /// @notice 指示合约是“addr”的“interfaceHash”的“implementer”。
    event InterfaceImplementerSet(address indexed addr, bytes32 indexed interfaceHash, address indexed implementer);
    /// @notice 指示“newManager”是“addr”的新管理器的地址。
    event ManagerChanged(address indexed addr, address indexed newManager);
    /// @notice 查询地址是否实现了接口以及通过哪个合约实现。
    /// @param _addr 查询接口实现者的地址。
    /// (如果“_addr”为零地址,则假定为“msg.sender”。)
    /// @param _interfaceHash 接口名称的 Keccak256 哈希值,以字符串形式表示。
    /// 例如,“web3.utils.keccak256("ERC777TokensRecipient")”表示“ERC777TokensRecipient”接口。
    /// @return 实现“_addr”的接口“_interfaceHash”的合约地址
    /// 或者,如果“_addr”未为此接口注册实现者,则返回“0”。
    function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address) {
        address addr = _addr == address(0) ? msg.sender : _addr;
        if (isERC165Interface(_interfaceHash)) {
            bytes4 erc165InterfaceHash = bytes4(_interfaceHash);
            return implementsERC165Interface(addr, erc165InterfaceHash) ? addr : address(0);
        }
        return interfaces[addr][_interfaceHash];
    }
    /// @notice 设置为地址实现特定接口的合约。
    /// 只有为该地址定义的管理器才能设置它。
    /// (每个地址都是其自身的管理器,直到它设置新的管理器。)
    /// @param _addr 要为其设置接口的地址。
    /// (如果“_addr”为零地址,则假定为“msg.sender”。)
    /// @param _interfaceHash 接口名称的 Keccak256 哈希值,以字符串形式表示。
    /// 例如,“web3.utils.keccak256("ERC777TokensRecipient")”表示“ERC777TokensRecipient”接口。
    /// @param _implementer 实现“_addr”的“_interfaceHash”的合约地址。
    function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external {
        address addr = _addr == address(0) ? msg.sender : _addr;
        require(getManager(addr) == msg.sender, "Not the manager"); // 不是管理器
        require(!isERC165Interface(_interfaceHash), "Must not be an ERC165 hash"); // 必须不是 ERC165 哈希
        if (_implementer != address(0) && _implementer != msg.sender) {
            require(
                ERC1820ImplementerInterface(_implementer)
                    .canImplementInterfaceForAddress(_interfaceHash, addr) == ERC1820_ACCEPT_MAGIC,
                "Does not implement the interface" // 没有实现接口
            );
        }
        interfaces[addr][_interfaceHash] = _implementer;
        emit InterfaceImplementerSet(addr, _interfaceHash, _implementer);
    }
    /// @notice 设置“_newManager”作为“_addr”的管理器。
    /// 新的管理器将能够为“_addr”调用“setInterfaceImplementer”。
    /// @param _addr 要为其设置新管理器的地址。
    /// @param _newManager 'addr'的新管理器的地址。(传递“0x0”以将管理器重置为“_addr”。)
    function setManager(address _addr, address _newManager) external {
        require(getManager(_addr) == msg.sender, "Not the manager"); // 不是管理器
        managers[_addr] = _newManager == _addr ? address(0) : _newManager;
        emit ManagerChanged(_addr, _newManager);
    }
    /// @notice 获取地址的管理器。
    /// @param _addr 要为其返回管理器的地址。
    /// @return 给定地址的管理器的地址。
    function getManager(address _addr) public view returns(address) {
        // 默认情况下,地址的管理器是同一地址
        if (managers[_addr] == address(0)) {
            return _addr;
        } else {
            return managers[_addr];
        }
    }
    /// @notice 计算给定接口名称的 keccak256 哈希。
    /// @param _interfaceName 接口的名称。
    /// @return 接口名称的 keccak256 哈希。
    function interfaceHash(string calldata _interfaceName) external pure returns(bytes32) {
        return keccak256(abi.encodePacked(_interfaceName));
    }
    /* --- ERC165 相关函数 --- */
    /* --- 与 William Entriken 合作开发。 --- */
    /// @notice 使用合约是否实现 ERC165 接口来更新缓存。
    /// @param _contract 要为其更新缓存的合约地址。
    /// @param _interfaceId 要为其更新缓存的 ERC165 接口。
    function updateERC165Cache(address _contract, bytes4 _interfaceId) external {
        interfaces[_contract][_interfaceId] = implementsERC165InterfaceNoCache(
            _contract, _interfaceId) ? _contract : address(0);
        erc165Cached[_contract][_interfaceId] = true;
    }
    /// @notice 检查合约是否实现 ERC165 接口。
    // 如果结果未缓存,则直接在合约地址上执行查找。
    // 如果结果未缓存或缓存值已过期,则必须通过使用合约地址调用
    // “updateERC165Cache”来手动更新缓存。
    /// @param _contract 要检查的合约地址。
    /// @param _interfaceId 要检查的 ERC165 接口。
    /// @return 如果“_contract”实现了“_interfaceId”,则返回 True,否则返回 false。
    function implementsERC165Interface(address _contract, bytes4 _interfaceId) public view returns (bool) {
        if (!erc165Cached[_contract][_interfaceId]) {
            return implementsERC165InterfaceNoCache(_contract, _interfaceId);
        }
        return interfaces[_contract][_interfaceId] == _contract;
    }
    /// @notice 检查合约是否实现 ERC165 接口,无需使用或更新缓存。
    /// @param _contract 要检查的合约地址。
    /// @param _interfaceId 要检查的 ERC165 接口。
    /// @return 如果“_contract”实现了“_interfaceId”,则返回 True,否则返回 false。
    function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view returns (bool) {
        uint256 success;
        uint256 result;
        (success, result) = noThrowCall(_contract, ERC165ID);
        if (success == 0 || result == 0) {
            return false;
        }
        (success, result) = noThrowCall(_contract, INVALID_ID);
        if (success == 0 || result != 0) {
            return false;
        }
        (success, result) = noThrowCall(_contract, _interfaceId);
        if (success == 1 && result == 1) {
            return true;
        }
        return false;
    }
    /// @notice 检查哈希是否为 ERC165 接口(以 28 个零结尾)。
    /// @param _interfaceHash 要检查的哈希。
    /// @return 如果“_interfaceHash”是 ERC165 接口(以 28 个零结尾),则返回 True,否则返回 false。
    function isERC165Interface(bytes32 _interfaceHash) internal pure returns (bool) {
        return _interfaceHash & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0;
    }
    /// @dev 对合约进行调用,如果该函数不存在则不抛出异常。
    function noThrowCall(address _contract, bytes4 _interfaceId)
        internal view returns (uint256 success, uint256 result)
    {
        bytes4 erc165ID = ERC165ID;
        assembly {
            let x := mload(0x40)               // 使用“空闲内存指针”查找空存储位置
            mstore(x, erc165ID)                // 将签名放在空存储的开头
            mstore(add(x, 0x04), _interfaceId) // 将第一个参数直接放在签名旁边
            success := staticcall(
                30000,                         // 30k gas
                _contract,                     // To addr
                x,                             // 输入存储在位置 x
                0x24,                          // 输入长度为 36 (4 + 32) 字节
                x,                             // 将输出存储在输入上(节省空间)
                0x20                           // 输出长度为 32 字节
            )
            result := mload(x)                 // 加载结果
        }
    }
}
部署交易
以下是必须用于在任何链上部署智能合约的原始交易。
0xf90a388085174876e800830c35008080b909e5608060405234801561001057600080fd5b506109c5806100206000396000f3fe608060405234801561001057600080fd5b50600436106100a5576000357c010000000000000000000000000000000000000000000000000000000090048063a41e7d5111610078578063a41e7d51146101d4578063aabbb8ca1461020a578063b705676514610236578063f712f3e814610280576100a5565b806329965a1d146100aa5780633d584063146100e25780635df8122f1461012457806365ba36c114610152575b600080fd5b6100e0600480360360608110156100c057600080fd5b50600160a060020a038135811691602081013591604090910135166102b6565b005b610108600480360360208110156100f857600080fd5b5035600160a060020a0316610570565b60408051600160a060020a039092168252519081900360200190f35b6100e06004803603604081101561013a57600080fd5b50600160a060020a03813581169160200135166105bc565b6101c26004803603602081101561016857600080fd5b81019060208101813564010000000081111561018357600080fd5b82018360208201111561019557600080fd5b803590602001918460018302840111640100000000831117156101b757600080fd5b5090925090506106b3565b60408051918252519081900360200190f35b6100e0600480360360408110156101ea57600080fd5b508035600160a060020a03169060200135600160e060020a0319166106ee565b6101086004803603604081101561022057600080fd5b50600160a060020a038135169060200135610778565b61026c6004803603604081101561024c57600080fd5b508035600160a060020a03169060200135600160e060020a0319166107ef565b604080519115158252519081900360200190f35b61026c6004803603604081101561029657600080fd5b508035600160a060020a03169060200135600160e060020a0319166108aa565b6000600160a060020a038416156102cd57836102cf565b335b9050336102db82610570565b600160a060020a031614610339576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b6103428361092a565b15610397576040805160e560020a62461bcd02815260206004820152601a60248201527f4d757374206e6f7420626520616e204552433136352068617368000000000000604482015290519081900360640190fd5b600160a060020a038216158015906103b85750600160a060020a0382163314155b156104ff5760405160200180807f455243313832305f4143434550545f4d4147494300000000000000000000000081525060140190506040516020818303038152906040528051906020012082600160a060020a031663249cb3fa85846040518363ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018083815260200182600160a060020a0316600160a060020a031681526020019250505060206040518083038186803b15801561047e57600080fd5b505afa158015610492573d6000803e3d6000fd5b505050506040513d60208110156104a857600080fd5b5051146104ff576040805160e560020a62461bcd02815260206004820181905260248201527f446f6573206e6f7420696d706c656d656e742074686520696e74657266616365604482015290519081900360640190fd5b600160a060020a03818116600081815260208181526040808320888452909152808220805473ffffffffffffffffffffffffffffffffffffffff19169487169485179055518692917f93baa6efbd2244243bfee6ce4cfdd1d04fc4c0e9a786abd3a41313bd352db15391a450505050565b600160a060020a03818116600090815260016020526040812054909116151561059a5750806105b7565b50600160a060020a03808216600090815260016020526040902054165b919050565b336105c683610570565b600160a060020a031614610624576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b81600160a060020a031681600160a060020a0316146106435780610646565b60005b600160a060020a03838116600081815260016020526040808220805473ffffffffffffffffffffffffffffffffffffffff19169585169590951790945592519184169290917f605c2dbf762e5f7d60a546d42e7205dcb1b011ebc62a61736a57c9089d3a43509190a35050565b600082826040516020018083838082843780830192505050925050506040516020818303038152906040528051906020012090505b92915050565b6106f882826107ef565b610703576000610705565b815b600160a060020a03928316600081815260208181526040808320600160e060020a031996909616808452958252808320805473ffffffffffffffffffffffffffffffffffffffff19169590971694909417909555908152600284528181209281529190925220805460ff19166001179055565b600080600160a060020a038416156107905783610792565b335b905061079d8361092a565b156107c357826107ad82826108aa565b6107b85760006107ba565b815b925050506106e8565b600160a060020a0390811660009081526020818152604080832086845290915290205416905092915050565b6000808061081d857f01ffc9a70000000000000000000000000000000000000000000000000000000061094c565b909250905081158061082d575080155b1561083d576000925050506106e8565b61084f85600160e060020a031961094c565b909250905081158061```md
---
title: ERC-1820:通用伪自检注册表合约
description: 定义了一个通用的注册表智能合约,任何地址(合约或常规账户)都可以在其中注册它支持的接口以及哪个智能合约负责其实现。
---
## 目录
1. [简介](#introduction)
2. [动机](#motivation)
3. [规范](#specification)
4. [原理](#rationale)
5. [实施](#implementation)
6. [安全注意事项](#security-considerations)
7. [版权](#copyright)
## 摘要
该标准定义了一个通用的注册表智能合约,任何地址(合约或常规账户)都可以在其中注册它支持的接口以及哪个智能合约负责其实现。
## 动机
目前,没有标准化的方法来检查给定的智能合约是否实现了给定的接口。
接口检测传统上是通过 [ERC-165] 完成的,但这需要合约明确声明它实现的接口。
这在许多情况下存在问题,最常见的是:
1.  无法以向后兼容的方式为现有合约添加接口。
2.  如果合约要与其他合约交互,则需要知道合约是否实现了给定的接口。
## 规范
### 智能合约
``` solidity
pragma solidity ^0.5.0;
contract ERC1820Registry {
    function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external;
    function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address);
    function setManager(address _addr, address _newManager) external;
    function getManager(address _addr) external view returns (address);
    function interfaceHash(string _interfaceName) external pure returns(bytes32);
    function implementsERC165Interface(address _contract, bytes4 _interfaceId) external view returns (bool);
    function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) external view returns (bool);
    function updateERC165Cache(address _contract, bytes4 _interfaceId) external;
}
该注册表合约必须部署在一个众所周知且不可变的地址上。
该地址是 0x1820a4B7618BdE71Dce8cdc73aAB6c95905faD24,并且合约初始化后,keccak256(bytes("ERC1820_REGISTRY_NAME")) 必须返回该注册表合约地址。
keccak256(bytes("ERC1820_REGISTRY_NAME")) == keccak256(ERC1820Registry.address) 必须总是返回 true。
函数
      
      
         setInterfaceImplementer
      
      
    
function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external
设置实现地址特定接口的合约。
只有为该地址定义的 manager 才能设置它。(在设置新 manager 之前,每个地址都是自己的 manager,有关更多详细信息,请参见 manager 部分。)
注意:如果 _addr 和 _implementer 是两个不同的地址,则:
_implementer必须实现ERC1820ImplementerInterface(详见下文)。- 使用给定的 
_addr和_interfaceHash在_implementer上调用canImplementInterfaceForAddress必须返回ERC1820_ACCEPT_MAGIC值。 
注意:_interfaceHash 不能是 ERC-165 接口,它不能以 28 个零(0)结尾。
注意:_addr 可以为 0,那么将假定为 msg.sender。此默认值简化了通过多重签名的交互,在这种交互中,要签名的交易数据是恒定的,而与多重签名实例的地址无关。
标识符:
29965a1d
参数
_addr:要设置接口的地址。(如果_addr为零地址,则假定为msg.sender。)
_interfaceHash:接口名称的 Keccak256 哈希值,例如web3.utils.keccak256('ERC777TokensRecipient')(对于 ERC777TokensRecipient 接口)。
_implementer:实现_interfaceHash的合约。
      
      
         getInterfaceImplementer
      
      
    
function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address)
查询地址是否实现接口以及通过哪个合约实现。
注意:如果 _interfaceHash 的最后 28 个字节为零(0),则前 4 个字节将被视为 ERC-165 接口,并且注册表应将调用转发到 _addr 处的合约,以查看它是否实现了 ERC-165 接口(_interfaceHash 的前 4 个字节)。该注册表还应缓存 ERC-165 查询以减少 gas 消耗。任何人都可以调用 erc165UpdateCache 函数来更新合约是否实现接口。
注意:_addr 可以为 0,那么将假定为 msg.sender。此默认值与 setInterfaceImplementer 函数的行为一致,并简化了通过多重签名的交互,在这种交互中,要签名的交易数据是恒定的,而与多重签名实例的地址无关。
标识符:
aabbb8ca
参数
_addr:正在查询接口实现的地址。(如果_addr为零地址,则假定为msg.sender。)
_interfaceHash:接口名称的 keccak256 哈希值。例如web3.utils.keccak256('ERC777Token')
返回值: 实现_interfaceHash接口的合约的地址,如果_addr没有为此接口注册实现者,则返回0。
      
      
         setManager
      
      
    
function setManager(address _addr, address _newManager) external
将 _newManager 设置为 _addr 的 manager。
新的 manager 将能够为 _addr 调用 setInterfaceImplementer。
如果 _newManager 是 0x0,则 manager 将重置为 _addr 本身作为 manager。
标识符:
5df8122f
参数
_addr:要设置新 manager 的地址。
_newManager:_addr的新 manager 的地址。(传递0x0以将 manager 重置为_addr。)
      
      
         getManager
      
      
    
function getManager(address _addr) external view returns (address)
获取地址的 manager。
标识符:
ef277308
参数
_addr:要返回其 manager 的地址。
返回值: 给定地址的 manager 的地址。
      
      
         interfaceHash
      
      
    
function interfaceHash(string _interfaceName) external pure returns(bytes32)
计算给定接口名称的 keccak256 哈希值。
标识符:
65ba36c1
参数
_interfaceName:接口的名称。
返回值: 接口名称的keccak256哈希值。
      
      
         implementsERC165Interface
      
      
    
function implementsERC165Interface(address _contract, bytes4 _interfaceId) external view returns (bool)
检查合约是否实现 ERC-165 接口。
如果未缓存结果,则对合约地址执行直接查找。
注意:如果未缓存结果或缓存值已过期,则必须通过使用合约地址手动调用 updateERC165Cache 来手动更新缓存。
(有关更多详细信息,请参见 [ERC165 缓存]。)
标识符:
f712f3e8
参数
_contract:要检查的合约的地址。
_interfaceId:要检查的 ERC-165 接口。
返回值: 如果_contract实现了_interfaceId,则返回true;否则返回false。
      
      
         implementsERC165InterfaceNoCache
      
      
    
function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) external view returns (bool)
检查合约是否实现了 ERC-165 接口,而不使用或更新缓存。
标识符:
b7056765
参数
_contract:要检查的合约的地址。
_interfaceId:ERC-165 接口来检查。
返回值: 如果_contract实现了_interfaceId,则返回true;否则返回false。
      
      
         updateERC165Cache
      
      
    
function updateERC165Cache(address _contract, bytes4 _interfaceId) external
更新缓存,指示合约是否实现 ERC-165 接口。
标识符:
a41e7d51
参数
_contract:要更新缓存的合约的地址。
_interfaceId:要更新缓存的 ERC-165 接口。
      
      
         实施者接口 (ERC1820ImplementerInterface)
      
      
    
interface ERC1820ImplementerInterface {
    function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view returns(bytes32);
}
任何被注册为给定地址的接口实现的合约都必须实现该接口。
此外,如果它代表不同的地址实现接口,则合约必须实现上面显示的 ERC1820ImplementerInterface。
      
      
         canImplementInterfaceForAddress
      
      
    
function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view returns(bytes32)
指示合约是否使用给定的地址 (addr) 实现接口 (interfaceHash)。
如果合约为给定地址 (addr) 实现了接口 (interfaceHash),则在使用 addr 和 interfaceHash 调用时,它必须返回 ERC1820_ACCEPT_MAGIC。
如果没有为给定地址 (addr) 实现 interfaceHash,则不得返回 ERC1820_ACCEPT_MAGIC。
标识符:
f0083250
参数
interfaceHash:已实现的接口的哈希值
addr:实现接口的地址
返回值: 仅当合约为地址addr实现ìnterfaceHash时,才返回ERC1820_ACCEPT_MAGIC。
特殊值 ERC1820_ACCEPT_MAGIC 定义为字符串 "ERC1820_ACCEPT_MAGIC" 的 keccka256 哈希值。
bytes32 constant internal ERC1820_ACCEPT_MAGIC = keccak256(abi.encodePacked("ERC1820_ACCEPT_MAGIC"));
返回
ERC1820_ACCEPT_MAGIC而不是布尔值的原因是防止出现合约无法实现canImplementInterfaceForAddress但实现了不抛出的回退函数的情况。在这种情况下,由于canImplementInterfaceForAddress不存在,因此改为调用回退函数,执行时不抛出并返回1。因此,使其看起来好像canImplementInterfaceForAddress返回了true。
Manager
地址(常规帐户或合约)的 manager 是唯一允许为该地址注册接口实现的实体。 默认情况下,任何地址都是自己的 manager。
manager 可以通过在注册表合约上调用 setManager 并提供要转移 manager 的地址和新 manager 的地址,将其角色转移到另一个地址。
      
      
         setManager 函数
      
      
    
function setManager(address _addr, address _newManager) external
将 _newManager 设置为 _addr 的 manager。
新的 manager 将能够为 _addr 调用 setInterfaceImplementer。
如果 _newManager 为 0x0,则 manager 将重置为 _addr 本身作为 manager。
标识符:
5df8122f
参数
_addr:要设置新 manager 的地址。
_newManager:_addr的新 manager 的地址。(传递0x0以将 manager 重置为_addr。)
      
      
         getManager 函数
      
      
    
function getManager(address _addr) external view returns(address)
获取地址的 manager。
标识符:
ef277308
参数
_addr:要返回其 manager 的地址。
返回值: 给定地址的 manager 的地址。
事件
      
      
         InterfaceImplementerSet
      
      
    
event InterfaceImplementerSet(address indexed addr, bytes32 indexed interfaceHash, address indexed implementer);
当地址 addr 的 interfaceHash 接口设置为 implementer 时发出。
      
      
         ManagerChanged
      
      
    
event ManagerChanged(address indexed addr, address indexed newManager);
当地址 addr 的 manager 更改为 newManager 时发出。
术语约定
- 应当使用关键字 MUST、SHALL、REQUIRED、SHOULD、RECOMMENDED、MAY 和 OPTIONAL 来描述本规范,如 [RFC 2119] 中所述。
 - ERC-165:指向 ERC-165 标准的链接
 - [管理器]:指向“管理器”部分的链接
 - [ERC165 缓存]:指向“[ERC-165] 缓存”部分的链接
 web3.utils.keccak256('ERC777TokensRecipient'):用于计算ERC777TokensRecipient接口的bytes32哈希值的 JavaScript 表示形式。其他语言可以使用各自的keccak256实现来获得相同的结果,但值必须是bytes32。
实施
- ERC1820Registry.sol:ERC1820 注册智能合约的实现。
 - ERC1820Client.sol:一个抽象智能合约,可以继承它来简化与注册表合约的交互。
 
原理
与 ERC-165 相比,此标准使用 string 的 keccak256 哈希值而不是 bytes4 作为接口 ID 的原因是,使用 bytes4 可能会发生冲突。
即使发生冲突的可能性很小,也最好使用不会发生冲突的东西。
实现
ERC-1820 注册表的实现地址为 0x1820a4B7618BdE71Dce8cdc73aAB6c95905faD24。
此合约的源代码以及测试可以在存储库中找到。
<details>
<summary>
  <b>
  点击查看合约 ABI
  </b>
</summary>
```json
{
        “compiler”: {
          “version”: “0.5.3+commit.10d17f24”
        },
        “language”: “Solidity”,
        “output”: {
          “abi”: [
            {
              “constant”: false,
              “inputs”: [
                {
                  “name”: “_addr”,
                  “type”: “address”
                },
                {
                  “name”: “_interfaceHash”,
                  “type”: “bytes32”
                },
                {
                  “name”: “_implementer”,
                  “type”: “address”
                }
              ],
              “name”: “setInterfaceImplementer”,
              “outputs”: [],
              “payable”: false,
              “stateMutability”: “nonpayable”,
              “type”: “function”
            },
            {
              “constant”: true,
              “inputs”: [
                {
                  “name”: “_addr”,
                  “type”: “address”
                }
              ],
              “name”: “getManager”,
              “outputs”: [
                {
                  “name”: “”,
                  “type”: “address”
                }
              ],
              “payable”: false,
              “stateMutability”: “view”,
              “type”: “function”
            },
            {
              “constant”: false,
              “inputs”: [
                {
                  “name”: “_addr”,
                  “type”: “address”
                },
                {
                  “name”: “_newManager”,
                  “type”: “address”
                }
              ],
              “name”: “setManager”,
              “outputs”: [],
              “payable”: false,
              “stateMutability”: “nonpayable”,
              “type”: “function”
            },
            {
              “constant”: true,
              “inputs”: [
                {
                  “name”: “_interfaceName”,
                  “type”: “string”
                }
              ],
              “name”: “interfaceHash”,
              “outputs”: [
                {
                  “name”: “”,
                  “type”: “bytes32”
                }
              ],
              “payable”: false,
              “stateMutability”: “pure”,
              “type”: “function”
            },
            {
              “constant”: false,
              “inputs”: [
                {
                  “name”: “_contract”,
                  “type”: “address”
                },
                {
                  “name”: “_interfaceId”,
                  “type”: “bytes4”
                }
              ],
              “name”: “updateERC165Cache”,
              “outputs”: [],
              “payable”: false,
              “stateMutability”: “nonpayable”,
              “type”: “function”
            },
            {
              “constant”: true,
              “inputs”: [
                {
                  “name”: “_addr”,
                  “type”: “address”
                },
                {
                  “name”: “_interfaceHash”,
                  “type”: “bytes32”
                }
              ],
              “name”: “getInterfaceImplementer”,
              “outputs”: [
                {
                  “name”: “”,
                  “type”: “address”
                }
              ],
              “payable”: false,
              “stateMutability”: “view”,
              “type”: “function”
            },
            {
              “constant”: true,
              “inputs”: [
                {
                  “name”: “_contract”,
                  “type”: “address”
                },
                {
                  “name”: “_interfaceId”,
                  “type”: “bytes4”
                }
              ],
              “name”: “implementsERC165InterfaceNoCache”,
              “outputs”: [
                {
                  “name”: “”,
                  “type”: “bool”
                }
              ],
              “payable”: false,
              “stateMutability”: “view”,
              “type”: “function”
            },
            {
              “constant”: true,
              “inputs”: [
                {
                  “name”: “_contract”,
                  “type”: “address”
                },
                {
                  “name”: “_interfaceId”,
                  “type”: “bytes4”
                }
              ],
              “name”: “implementsERC165Interface”,
              “outputs”: [
                {
                  “name”: “”,
                  “type”: “bool”
                }
              ],
              “payable”: false,
              “stateMutability”: “view”,
              “type”: “function”
            },
            {
              “anonymous”: false,
              “inputs”: [
                {
                  “indexed”: true,
                  “name”: “addr”,
                  “type”: “address”
                },
                {
                  “indexed”: true,
                  “name”: “interfaceHash”,
                  “type”: “bytes32”
                },
                {
                  “indexed”: true,
                  “name”: “implementer”,
                  “type”: “address”
                }
              ],
              “name”: “InterfaceImplementerSet”,
              “type”: “event”
            },
            {
              “anonymous”: false,
              “inputs”: [
                {
                  “indexed”: true,
                  “name”: “addr”,
                  “type”: “address”
                },
                {
                  “indexed”: true,
                  “name”: “newManager”,
                  “type”: “address”
                }
              ],
              “name”: “ManagerChanged”,
              “type”: “event”
            }
          ],
          “devdoc”: {
            “author”: “Jordi Baylina and Jacques Dafflon”,
            “methods”: {
              “getInterfaceImplementer(address,bytes32)”: {
                “params”: {
                  “_addr”: “正在查询接口实现者的地址。(如果’_addr’为零地址,则假定为’msg.sender’。)”,
                  “_interfaceHash”: “接口名称的 Keccak256 哈希值,作为字符串。例如,’web3.utils.keccak256("ERC777TokensRecipient")’ 对于 ‘ERC777TokensRecipient’ 接口。”
                },
                “return”: “为’_addr’实现接口’_interfaceHash’的合约地址,如果’_addr’未为此接口注册实现者,则返回’0’。”
              },
              “getManager(address)”: {
                “params”: {
                  “_addr”: “要为其返回 manager 的地址。”
                },
                “return”: “给定地址的 manager 的地址。”
              },
              “implementsERC165Interface(address,bytes4)”: {
                “params”: {
                  “_contract”: “要检查的合约的地址。”,
                  “_interfaceId”: “要检查的 ERC165 接口。”
                },
                “return”: “如果’_contract’实现了’_interfaceId’,则返回 True,否则返回 false。”
              },
              “implementsERC165InterfaceNoCache(address,bytes4)”: {
                “params”: {
                  “_contract”: “要检查的合约的地址。”,
                  “_interfaceId”: “要检查的 ERC165 接口。”
                },
                “return”: “如果’_contract’实现了’_interfaceId’,则返回 True,否则返回 false。”
              },
              “interfaceHash(string)”: {
                “params”: {
                  “_interfaceName”: “接口的名称。”
                },
                “return”: “接口名称的 keccak256 哈希值。”
              },
              “setInterfaceImplementer(address,bytes32,address)”: {
                “params”: {
                  “_addr”: “要设置接口的地址。(如果’_addr’为零地址,则假定为’msg.sender’。)”,
                  “_implementer”: “为’_addr’实现’_interfaceHash’的合约地址。”,
                  “_interfaceHash”: “接口名称的 Keccak256 哈希值,作为字符串。例如,’web3.utils.keccak256("ERC777TokensRecipient")’ 对于 ‘ERC777TokensRecipient’ 接口。”
                }
              },
              “setManager(address,address)”: {
                “params”: {
                  “_addr”: “要为其设置新 manager 的地址。”,
                  “_newManager”: “‘addr’的新 manager 的地址。(传递’0x0’以将 manager 重置为’_addr’。)”
                }
              },
              “updateERC165Cache(address,bytes4)”: {
                “params”: {
                  “_contract”: “要为其更新缓存的合约的地址。”,
                  “_interfaceId”: “要为其更新缓存的 ERC165 接口。”
                }
              }
            },
            “title”: “ERC1820 伪自检注册表合约”
          },
          “userdoc”: {
            “methods”: {
              “getInterfaceImplementer(address,bytes32)”: {
                “notice”: “查询地址是否实现接口以及通过哪个合约实现。”
              },
              “getManager(address)”: {
                “notice”: “获取地址的 manager。”
              },
              “implementsERC165InterfaceNoCache(address,bytes4)”: {
                “notice”: “检查合约是否实现了 ERC165 接口,而不使用或更新缓存。”
              },
              “interfaceHash(string)”: {
                “notice”: “计算给定接口名称的 keccak256 哈希值。”
              },
              “setInterfaceImplementer(address,bytes32,address)”: {
                “notice”: “设置合约,该合约为特定地址实现特定接口。只有为该地址定义的 manager 才能设置它。(在设置新 manager 之前,每个地址都是自己的 manager。)”
              },
              “setManager(address,address)”: {
                “notice”: “将’_newManager’设置为’_addr’的 manager。新的 manager 将能够为’_addr’调用’setInterfaceImplementer’。”
              },
              “updateERC165Cache(address,bytes4)”: {
                “notice”: “使用合约是否实现了 ERC165 接口来更新缓存。”
              }
            },
            “notice”: “此合约是 ERC1820 注册表的官方实现。有关更多详细信息,请参见 https://eips.ethereum.org/EIPS/eip-1820”
          }
        },
        “settings”: {
          “compilationTarget”: {
            “./contracts/ERC1820Registry.sol”: “ERC1820Registry”
          },
          “evmVersion”: “byzantium”,
          “libraries”: {},
          “optimizer”: {
            “enabled”: true,
            “runs”: 200
          },
          “remappings”: []
        },
        “sources”: {
          “./contracts/ERC1820Registry.sol”: {
            “content”: “/* ERC1820 Pseudo-introspection Registry Contract\n * This standard defines a universal registry smart contract where any address (contract or regular account) can\n * register which interface it supports and which smart contract is responsible for its implementation.\n *\n * Written in 2019 by Jordi Baylina and Jacques Dafflon\n *\n * To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to\n * this software to the public domain worldwide. This software is distributed without any warranty.\n *\n * You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see\n * http://creativecommons.org/publicdomain/zero/1.0/.\n *\n *    ███████╗██████╗  ██████╗ ██╗ █████╗ ██████╗  ██████╗\n *    ██╔════╝██╔══██╗██╔════╝███║██╔══██╗╚════██╗██╔═████╗\n *    █████╗  ██████╔╝██║     ╚██║╚█████╔╝ █████╔╝██║██╔██║\n *    ██╔══╝  ██╔══██╗██║      ██║██╔══██╗██╔═══╝ ████╔╝██║\n *    ███████╗██║  ██║╚██████╗ ██║╚█████╔╝███████╗╚██████╔╝\n *    ╚══════╝╚═╝  ╚═╝ ╚═════╝ ╚═╝ ╚════╝ ╚══════╝ ╚═════╝\n *\n *    ██████╗ ███████╗ ██████╗ ██╗███████╗████████╗██████╗ ██╗   ██╗\n *    ██╔══██╗██╔════╝██╔════╝ ██║██╔════╝╚══██╔══╝██╔══██╗╚██╗ ██╔╝\n *    ██████╔╝█████╗  ██║  ███╗██║███████╗   ██║   ██████╔╝ ╚████╔╝\n *    ██╔══██╗██╔══╝  ██║   ██║██║╚════██║   ██║   ██╔══██╗  ╚██╔╝\n *    ██║  ██║███████╗╚██████╔╝██║███████║   ██║   ██║  ██║   ██║\n *    ╚═╝  ╚═╝╚══════╝ ╚═════╝ ╚═╝╚══════╝   ╚═╝   ╚═╝  ╚═╝   ╚═╝\n *\n */\npragma solidity 0.5.3;\n// IV is value needed to have a vanity address starting with ‘0x1820’.\n// IV: 53759\n\n/// @dev The interface a contract MUST implement if it is the implementer of\n/// some (other) interface for any address other than itself.\ninterface ERC1820ImplementerInterface {\n    /// @notice Indicates whether the contract implements the interface ‘interfaceHash’ for the address ‘addr’ or not.\n    /// @param interfaceHash keccak256 hash of the name of the interface\n    /// @param addr Address for which the contract will implement the interface\n    /// @return ERC1820_ACCEPT_MAGIC only if the contract implements ‘interfaceHash’ for the address ‘addr’.\n    function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view returns(bytes32);\n}\n\n\n/// @title ERC1820 Pseudo-introspection Registry Contract\n/// @author Jordi Baylina and Jacques Dafflon\n/// @notice This contract is the official implementation of the ERC1820 Registry.\n/// @notice For more details, see https://eips.ethereum.org/EIPS/eip-1820\ncontract ERC1820Registry {\n    /// @notice ERC165 Invalid ID.\n    bytes4 constant internal INVALID_ID = 0xffffffff;\n    /// @notice Method ID for the ERC165 supportsInterface method (= bytes4(keccak256('supportsInterface(bytes4)'))).\n    bytes4 constant internal ERC165ID = 0x01ffc9a7;\n    /// @notice Magic value which is returned if a contract implements an interface on behalf of some other address.\n    bytes32 constant internal ERC1820_ACCEPT_MAGIC = keccak256(abi.encodePacked("ERC1820_ACCEPT_MAGIC"));\n\n    /// @notice mapping from addresses and interface hashes to their implementers.\n    mapping(address => mapping(bytes32 => address)) internal interfaces;\n    /// @notice mapping from addresses to their manager.\n    mapping(address => address) internal managers;\n    /// @notice flag for each address and erc165 interface to indicate if it is cached.\n    mapping(address => mapping(bytes4 => bool)) internal erc165Cached;\n\n    /// @notice Indicates a contract is the ‘implementer’ of ‘interfaceHash’ for ‘addr’.\n    event InterfaceImplementerSet(address indexed addr, bytes32 indexed interfaceHash, address indexed implementer);\n    /// @notice Indicates ‘newManager’ is the address of the new manager for ‘addr’.\n    event ManagerChanged(address indexed addr, address indexed newManager);\n\n    /// @notice Query if an address implements an interface and through which contract.\n    /// @param _addr Address being queried for the implementer of an interface.\n    /// (If ‘_addr’ is the zero address then ‘msg.sender’ is assumed.)\n    /// @param _interfaceHash Keccak256 hash of the name of the interface as a string.\n    /// E.g., ‘web3.utils.keccak256("ERC777TokensRecipient")’ for the ‘ERC777TokensRecipient’ interface.\n    /// @return The address of the contract which implements the interface ‘_interfaceHash’ for ‘_addr’\n    /// or ‘0’ if ‘_addr’ did not register an implementer for this interface.\n    function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address) {\n        address addr = _addr == address(0) ? msg.sender : _addr;\n        if (isERC165Interface(_interfaceHash)) {\n            bytes4 erc165InterfaceHash = bytes4(_interfaceHash);\n            return implementsERC165Interface(addr, erc165InterfaceHash) ? addr : address(0);\n        }\n        return interfaces[addr][_interfaceHash];\n    }\n\n    /// @notice Sets the contract which implements a specific interface for an address.\n    /// Only the manager defined for that address can set it.\n    /// (Each address is the manager for itself until it sets a new manager.)\n    /// @param _addr Address for which to set the interface.\n    /// (If ‘_addr’ is the zero address then ‘msg.sender’ is assumed.)\n    /// @param _interfaceHash Keccak256 hash of the name of the interface as a string.\n    /// E.g., ‘web3.utils.keccak256("ERC777TokensRecipient")’ for the ‘ERC777TokensRecipient’ interface.\n    /// @param _implementer Contract address implementing ‘_interfaceHash’ for ‘_addr’.\n    function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external {\n        address addr = _addr == address(0) ? msg.sender : _addr;\n        require(getManager(addr) == msg.sender, "Not the manager");\n\n        require(!isERC165Interface(_interfaceHash), "Must not be an ERC165 hash");\n        if (_implementer != address(0) && _implementer != msg.sender) {\n            require(\n                ERC1820ImplementerInterface(_implementer)\n                    .canImplementInterfaceForAddress(_interfaceHash, addr) == ERC1820_ACCEPT_MAGIC,\n                "Does not implement the interface"\n            );\n        }\n        interfaces[addr][_interfaceHash] = _implementer;\n        emit InterfaceImplementerSet(addr, _interface> identifier: 3d584063
parameters
_addr: 要为其返回管理器的地址。 returns: 给定地址的管理器地址。
理由
该标准为任何类型的地址(外部拥有的和合约)提供了一种实现接口的方式,并且可能将接口的实现委托给代理合约。 这种委托给代理合约对于外部拥有的账户是必要的,并且有助于避免重新部署现有的合约,例如多重签名和 DAO。
注册表还可以充当 ERC-165 缓存,以便在查找合约是否实现了特定的 ERC-165 接口时节省 gas。
此缓存被有意地保持简单,没有自动缓存更新或失效。
任何人都可以通过调用 updateERC165Cache 函数轻松且安全地更新任何接口和任何合约的缓存。
注册表使用一种无密钥部署方法进行部署,该方法依赖于一次性部署地址,以确保没有人控制注册表,从而确保信任。
向后兼容性
此标准与 ERC-165 向后兼容,因为两种方法可以实现而不会相互冲突。
测试用例
请查看 0xjac/ERC1820 存储库以获取完整的测试套件。
实现
该实现在 repo 中可用:0xjac/ERC1820。
版权
通过 CC0 放弃版权和相关权利。
Citation
Please cite this document as:
Jordi Baylina <jordi@baylina.cat>, Jacques Dafflon <mail@0xjac.com>, "ERC-1820: 伪自省注册表合约," Ethereum Improvement Proposals, no. 1820, March 2019. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-1820.