mirror of https://github.com/status-im/EIPs.git
930 lines
60 KiB
Markdown
930 lines
60 KiB
Markdown
|
---
|
||
|
eip: 1820
|
||
|
title: Pseudo-introspection Registry Contract
|
||
|
author: Jordi Baylina <jordi@baylina.cat>, Jacques Dafflon <mail@0xjac.com>
|
||
|
discussions-to: https://github.com/ethereum/EIPs/pulls/1820
|
||
|
status: Draft
|
||
|
type: Standards Track
|
||
|
category: ERC
|
||
|
requires: 165, 214
|
||
|
created: 2018-03-04
|
||
|
replaces: 820
|
||
|
---
|
||
|
|
||
|
> :information_source: **[ERC1820] has superseded [ERC820].** :information_source:
|
||
|
> [ERC1820] fixes the incompatibility in the [ERC165] logic which was introduced by the Solidty 0.5 update.
|
||
|
> Have a look at the [official announcement][erc1820-annoucement], and the comments about the [bug][erc820-bug] and the [fix][erc820-fix].
|
||
|
> Apart from this fix, [ERC1820] is functionally equivalent to [ERC820].
|
||
|
>
|
||
|
> :warning: [ERC1820] MUST be used in lieu of [ERC820]. :warning:
|
||
|
|
||
|
## Simple Summary
|
||
|
|
||
|
This standard defines a universal registry smart contract where any address (contract or regular account) can register which interface it supports and which smart contract is responsible for its implementation.
|
||
|
|
||
|
This standard keeps backward compatibility with [ERC165].
|
||
|
|
||
|
## Abstract
|
||
|
|
||
|
This standard defines a registry where smart contracts and regular accounts can publish which functionality they implement---either directly or through a proxy contract.
|
||
|
|
||
|
Anyone can query this registry to ask if a specific address implements a given interface and which smart contract handles its implementation.
|
||
|
|
||
|
This registry MAY be deployed on any chain and shares the same address on all chains.
|
||
|
|
||
|
Interfaces with zeroes (`0`) as the last 28 bytes are considered [ERC165] interfaces,
|
||
|
and this registry SHALL forward the call to the contract to see if it implements the interface.
|
||
|
|
||
|
This contract also acts as an [ERC165] cache to reduce gas consumption.
|
||
|
|
||
|
## Motivation
|
||
|
|
||
|
There have been different approaches to define pseudo-introspection in Ethereum.
|
||
|
The first is [ERC165] which has the limitation that it cannot be used by regular accounts.
|
||
|
The second attempt is [ERC672] which uses reverse [ENS]. Using reverse [ENS] has two issues.
|
||
|
First, it is unnecessarily complicated, and second, [ENS] is still a centralized contract controlled by a multisig.
|
||
|
This multisig theoretically would be able to modify the system.
|
||
|
|
||
|
This standard is much simpler than [ERC672], and it is *fully* decentralized.
|
||
|
|
||
|
This standard also provides a *unique* address for all chains.
|
||
|
Thus solving the problem of resolving the correct registry address for different chains.
|
||
|
|
||
|
## Specification
|
||
|
|
||
|
### [ERC1820] Registry Smart Contract
|
||
|
|
||
|
> This is an exact copy of the code of the [ERC1820 registry smart contract].
|
||
|
|
||
|
``` solidity
|
||
|
/* ERC1820 Pseudo-introspection Registry Contract
|
||
|
* This standard defines a universal registry smart contract where any address (contract or regular account) can
|
||
|
* register which interface it supports and which smart contract is responsible for its implementation.
|
||
|
*
|
||
|
* Written in 2019 by Jordi Baylina and Jacques Dafflon
|
||
|
*
|
||
|
* To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to
|
||
|
* this software to the public domain worldwide. This software is distributed without any warranty.
|
||
|
*
|
||
|
* You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see
|
||
|
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||
|
*
|
||
|
* ███████╗██████╗ ██████╗ ██╗ █████╗ ██████╗ ██████╗
|
||
|
* ██╔════╝██╔══██╗██╔════╝███║██╔══██╗╚════██╗██╔═████╗
|
||
|
* █████╗ ██████╔╝██║ ╚██║╚█████╔╝ █████╔╝██║██╔██║
|
||
|
* ██╔══╝ ██╔══██╗██║ ██║██╔══██╗██╔═══╝ ████╔╝██║
|
||
|
* ███████╗██║ ██║╚██████╗ ██║╚█████╔╝███████╗╚██████╔╝
|
||
|
* ╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚════╝ ╚══════╝ ╚═════╝
|
||
|
*
|
||
|
* ██████╗ ███████╗ ██████╗ ██╗███████╗████████╗██████╗ ██╗ ██╗
|
||
|
* ██╔══██╗██╔════╝██╔════╝ ██║██╔════╝╚══██╔══╝██╔══██╗╚██╗ ██╔╝
|
||
|
* ██████╔╝█████╗ ██║ ███╗██║███████╗ ██║ ██████╔╝ ╚████╔╝
|
||
|
* ██╔══██╗██╔══╝ ██║ ██║██║╚════██║ ██║ ██╔══██╗ ╚██╔╝
|
||
|
* ██║ ██║███████╗╚██████╔╝██║███████║ ██║ ██║ ██║ ██║
|
||
|
* ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
|
||
|
*
|
||
|
*/
|
||
|
pragma solidity 0.5.3;
|
||
|
// IV is value needed to have a vanity address starting with `0x1820`.
|
||
|
// IV: 87018
|
||
|
|
||
|
/// @dev The interface a contract MUST implement if it is the implementer of
|
||
|
/// some (other) interface for any address other than itself.
|
||
|
interface ERC1820ImplementerInterface {
|
||
|
/// @notice Indicates whether the contract implements the interface `interfaceHash` for the address `addr` or not.
|
||
|
/// @param interfaceHash keccak256 hash of the name of the interface
|
||
|
/// @param addr Address for which the contract will implement the interface
|
||
|
/// @return ERC1820_ACCEPT_MAGIC only if the contract implements `interfaceHash` for the address `addr`.
|
||
|
function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view returns(bytes32);
|
||
|
}
|
||
|
|
||
|
|
||
|
/// @title ERC1820 Pseudo-introspection Registry Contract
|
||
|
/// @author Jordi Baylina and Jacques Dafflon
|
||
|
/// @notice This contract is the official implementation of the ERC1820 Registry.
|
||
|
/// @notice For more details, see https://eips.ethereum.org/EIPS/eip-1820
|
||
|
contract ERC1820Registry {
|
||
|
/// @notice ERC165 Invalid ID.
|
||
|
bytes4 constant internal INVALID_ID = 0xffffffff;
|
||
|
/// @notice Method ID for the ERC165 supportsInterface method (= `bytes4(keccak256('supportsInterface(bytes4)'))`).
|
||
|
bytes4 constant internal ERC165ID = 0x01ffc9a7;
|
||
|
/// @notice Magic value which is returned if a contract implements an interface on behalf of some other address.
|
||
|
bytes32 constant internal ERC1820_ACCEPT_MAGIC = keccak256(abi.encodePacked("ERC1820_ACCEPT_MAGIC"));
|
||
|
|
||
|
/// @notice mapping from addresses and interface hashes to their implementers.
|
||
|
mapping(address => mapping(bytes32 => address)) internal interfaces;
|
||
|
/// @notice mapping from addresses to their manager.
|
||
|
mapping(address => address) internal managers;
|
||
|
/// @notice flag for each address and erc165 interface to indicate if it is cached.
|
||
|
mapping(address => mapping(bytes4 => bool)) internal erc165Cached;
|
||
|
|
||
|
/// @notice Indicates a contract is the `implementer` of `interfaceHash` for `addr`.
|
||
|
event InterfaceImplementerSet(address indexed addr, bytes32 indexed interfaceHash, address indexed implementer);
|
||
|
/// @notice Indicates `newManager` is the address of the new manager for `addr`.
|
||
|
event ManagerChanged(address indexed addr, address indexed newManager);
|
||
|
|
||
|
/// @notice Query if an address implements an interface and through which contract.
|
||
|
/// @param _addr Address being queried for the implementer of an interface.
|
||
|
/// (If '_addr' is the zero address then 'msg.sender' is assumed.)
|
||
|
/// @param _interfaceHash Keccak256 hash of the name of the interface as a string.
|
||
|
/// E.g., 'web3.utils.keccak256("ERC777TokensRecipient")' for the 'ERC777TokensRecipient' interface.
|
||
|
/// @return The address of the contract which implements the interface `_interfaceHash` for `_addr.address()`
|
||
|
/// or '0' if `_addr.address()` did not register an implementer for this interface.
|
||
|
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 Sets the contract which implements a specific interface for an address.
|
||
|
/// Only the manager defined for that address can set it.
|
||
|
/// (Each address is the manager for itself until it sets a new manager.)
|
||
|
/// @param _addr Address for which to set the interface.
|
||
|
/// (If '_addr' is the zero address then 'msg.sender' is assumed.)
|
||
|
/// @param _interfaceHash Keccak256 hash of the name of the interface as a string.
|
||
|
/// E.g., 'web3.utils.keccak256("ERC777TokensRecipient")' for the 'ERC777TokensRecipient' interface.
|
||
|
/// @param _implementer Contract address implementing `_interfaceHash` for `_addr.address()`.
|
||
|
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");
|
||
|
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 Sets `_newManager.address()` as manager for `_addr.address()`.
|
||
|
/// The new manager will be able to call 'setInterfaceImplementer' for `_addr.address()`.
|
||
|
/// @param _addr Address for which to set the new manager.
|
||
|
/// @param _newManager Address of the new manager for `addr.address()`.
|
||
|
/// (Pass '0x0' to reset the manager to `_addr.address()`.)
|
||
|
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 Get the manager of an address.
|
||
|
/// @param _addr Address for which to return the manager.
|
||
|
/// @return Address of the manager for a given address.
|
||
|
function getManager(address _addr) public view returns(address) {
|
||
|
// By default the manager of an address is the same address
|
||
|
if (managers[_addr] == address(0)) {
|
||
|
return _addr;
|
||
|
} else {
|
||
|
return managers[_addr];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// @notice Compute the keccak256 hash of an interface given its name.
|
||
|
/// @param _interfaceName Name of the interface.
|
||
|
/// @return The keccak256 hash of an interface name.
|
||
|
function interfaceHash(string calldata _interfaceName) external pure returns(bytes32) {
|
||
|
return keccak256(abi.encodePacked(_interfaceName));
|
||
|
}
|
||
|
|
||
|
/* --- ERC165 Related Functions --- */
|
||
|
/* --- Developed in collaboration with William Entriken. --- */
|
||
|
|
||
|
/// @notice Updates the cache with whether the contract implements an ERC165 interface or not.
|
||
|
/// @param _contract Address of the contract for which to update the cache.
|
||
|
/// @param _interfaceId ERC165 interface for which to update the cache.
|
||
|
function updateERC165Cache(address _contract, bytes4 _interfaceId) external {
|
||
|
interfaces[_contract][_interfaceId] = implementsERC165InterfaceNoCache(
|
||
|
_contract, _interfaceId) ? _contract : address(0);
|
||
|
erc165Cached[_contract][_interfaceId] = true;
|
||
|
}
|
||
|
|
||
|
/// @notice Checks whether a contract implements an ERC165 interface or not.
|
||
|
// If the result is not cached a direct lookup on the contract address is performed.
|
||
|
// If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling
|
||
|
// 'updateERC165Cache' with the contract address.
|
||
|
/// @param _contract Address of the contract to check.
|
||
|
/// @param _interfaceId ERC165 interface to check.
|
||
|
/// @return True if `_contract.address()` implements `_interfaceId`, false otherwise.
|
||
|
function implementsERC165Interface(address _contract, bytes4 _interfaceId) public view returns (bool) {
|
||
|
if (!erc165Cached[_contract][_interfaceId]) {
|
||
|
return implementsERC165InterfaceNoCache(_contract, _interfaceId);
|
||
|
}
|
||
|
return interfaces[_contract][_interfaceId] == _contract;
|
||
|
}
|
||
|
|
||
|
/// @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.
|
||
|
/// @param _contract Address of the contract to check.
|
||
|
/// @param _interfaceId ERC165 interface to check.
|
||
|
/// @return True if `_contract.address()` implements `_interfaceId`, false otherwise.
|
||
|
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 Checks whether the hash is a ERC165 interface (ending with 28 zeroes) or not.
|
||
|
/// @param _interfaceHash The hash to check.
|
||
|
/// @return True if `_interfaceHash` is an ERC165 interface (ending with 28 zeroes), false otherwise.
|
||
|
function isERC165Interface(bytes32 _interfaceHash) internal pure returns (bool) {
|
||
|
return _interfaceHash & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0;
|
||
|
}
|
||
|
|
||
|
/// @dev Make a call on a contract without throwing if the function does not exist.
|
||
|
function noThrowCall(address _contract, bytes4 _interfaceId)
|
||
|
internal view returns (uint256 success, uint256 result)
|
||
|
{
|
||
|
bytes4 erc165ID = ERC165ID;
|
||
|
|
||
|
assembly {
|
||
|
let x := mload(0x40) // Find empty storage location using "free memory pointer"
|
||
|
mstore(x, erc165ID) // Place signature at beginning of empty storage
|
||
|
mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature
|
||
|
|
||
|
success := staticcall(
|
||
|
30000, // 30k gas
|
||
|
_contract, // To addr
|
||
|
x, // Inputs are stored at location x
|
||
|
0x24, // Inputs are 36 (4 + 32) bytes long
|
||
|
x, // Store output over input (saves space)
|
||
|
0x20 // Outputs are 32 bytes long
|
||
|
)
|
||
|
|
||
|
result := mload(x) // Load the result
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
```
|
||
|
|
||
|
### Deployment Transaction
|
||
|
|
||
|
Below is the raw transaction which MUST be used to deploy the smart contract on any chain.
|
||
|
|
||
|
```
|
||
|
0xf90a388085174876e800830c35008080b909e5608060405234801561001057600080fd5b506109c5806100206000396000f3fe608060405234801561001057600080fd5b50600436106100a5576000357c010000000000000000000000000000000000000000000000000000000090048063a41e7d5111610078578063a41e7d51146101d4578063aabbb8ca1461020a578063b705676514610236578063f712f3e814610280576100a5565b806329965a1d146100aa5780633d584063146100e25780635df8122f1461012457806365ba36c114610152575b600080fd5b6100e0600480360360608110156100c057600080fd5b50600160a060020a038135811691602081013591604090910135166102b6565b005b610108600480360360208110156100f857600080fd5b5035600160a060020a0316610570565b60408051600160a060020a039092168252519081900360200190f35b6100e06004803603604081101561013a57600080fd5b50600160a060020a03813581169160200135166105bc565b6101c26004803603602081101561016857600080fd5b81019060208101813564010000000081111561018357600080fd5b82018360208201111561019557600080fd5b803590602001918460018302840111640100000000831117156101b757600080fd5b5090925090506106b3565b60408051918252519081900360200190f35b6100e0600480360360408110156101ea57600080fd5b508035600160a060020a03169060200135600160e060020a0319166106ee565b6101086004803603604081101561022057600080fd5b50600160a060020a038135169060200135610778565b61026c6004803603604081101561024c57600080fd5b508035600160a060020a03169060200135600160e060020a0319166107ef565b604080519115158252519081900360200190f35b61026c6004803603604081101561029657600080fd5b508035600160a060020a03169060200135600160e060020a0319166108aa565b6000600160a060020a038416156102cd57836102cf565b335b9050336102db82610570565b600160a060020a031614610339576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b6103428361092a565b15610397576040805160e560020a62461bcd02815260206004820152601a60248201527f4d757374206e6f7420626520616e204552433136352068617368000000000000604482015290519081900360640190fd5b600160a060020a038216158015906103b85750600160a060020a0382163314155b156104ff5760405160200180807f455243313832305f4143434550545f4d4147494300000000000000000000000081525060140190506040516020818303038152906040528051906020012082600160a060020a031663249cb3fa85846040518363ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018083815260200182600160a060020a0316600160a060020a031681526020019250505060206040518083038186803b15801561047e57600080fd5b505afa158015610492573d6000803e3d6000fd5b505050506040513d60208110156104a857600080fd5b5051146104ff576040805160e560020a62461bcd02815260206004820181905260248201527f446f6573206e6f7420696d706c656d656e742074686520696e74657266616365604482015290519081900360640190fd5b600160a060020a03818116600081815260208181526040808320888452909152808220805473ffffffffffffffffffffffffffffffffffffffff19169487169485179055518692917f93baa6efbd2244243bfee6ce4cfdd1d04fc4c0e9a786abd3a41313bd352db15391a450505050565b600160a060020a03818116600090815260016020526040812054909116151561059a5750806105b7565b50600160a060020a03808216600090815260016020526040902054165b919050565b336105c683610570565b600160a060020a031614610624576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b81600160a060020a031681600160a060020a0316146106435780610646565b60005b600160a060020a03838116600081815260016020526040808220805473ffffffffffffffffffffffffffffffffffffffff19169585169590951790945592519184169290917f605c2dbf762e5f7d60a546d42e7205dcb1b011ebc62a61736a57c9089d3a43509190a35050565b600082826040516020018083838082843780830192505050925050506040516020818303038152906040528051906020012090505b92915050565b6106f882826107ef565b610703576000610705565b815b600160a060020a03928316600081815260208181526040808320600160e060020a031996909616808452958252808320805473ffffffffffffffffffffffffffffffffffffffff19169590971694909417909555908152600284528181209281529190925220805460ff19166001179055565b600080600160a060020a038416156107905783610792565b335b905061079d8361092a565b156107c357826107ad82826108aa565b6107b85760006107ba565b815b925050506106e8565b600160a060020a0
|
||
|
```
|
||
|
|
||
|
The strings of `1820`'s at the end of the transaction are the `r` and `s` of the signature.
|
||
|
From this deterministic pattern (generated by a human), anyone can deduce that no one knows the private key for the deployment account.
|
||
|
|
||
|
### Deployment Method
|
||
|
|
||
|
This contract is going to be deployed using the keyless deployment method---also known as [Nick]'s method---which relies on a single-use address.
|
||
|
(See [Nick's article] for more details). This method works as follows:
|
||
|
|
||
|
1. Generate a transaction which deploys the contract from a new random account.
|
||
|
- This transaction MUST NOT use [EIP155] in order to work on any chain.
|
||
|
- This transaction MUST have a relatively high gas price to be deployed on any chain. In this case, it is going to be 100 Gwei.
|
||
|
|
||
|
2. Set the `v`, `r`, `s` of the transaction signature to the following values:
|
||
|
|
||
|
```
|
||
|
v: 27,
|
||
|
r: 0x1820182018201820182018201820182018201820182018201820182018201820'
|
||
|
s: 0x1820182018201820182018201820182018201820182018201820182018201820'
|
||
|
```
|
||
|
|
||
|
Those `r` and `s` values---made of a repeating pattern of `1820`'s---are predictable "random numbers" generated deterministically by a human.
|
||
|
|
||
|
3. We recover the sender of this transaction, i.e., the single-use deployment account.
|
||
|
|
||
|
> Thus we obtain an account that can broadcast that transaction, but we also have the warranty that nobody knows the private key of that account.
|
||
|
|
||
|
4. Send exactly 0.08 ether to this single-use deployment account.
|
||
|
|
||
|
5. Broadcast the deployment transaction.
|
||
|
|
||
|
This operation can be done on any chain, guaranteeing that the contract address is always the same and nobody can use that address with a different contract.
|
||
|
|
||
|
|
||
|
### Single-use Registry Deployment Account
|
||
|
|
||
|
```
|
||
|
0x5808bA8E60E0367C9067b328D75C1f3d29de58cf
|
||
|
```
|
||
|
|
||
|
This account is generated by reverse engineering it from its signature for the transaction.
|
||
|
This way no one knows the private key, but it is known that it is the valid signer of the deployment transaction.
|
||
|
|
||
|
> To deploy the registry, 0.08 ether MUST be sent to this account *first*.
|
||
|
|
||
|
### Registry Contract Address
|
||
|
|
||
|
```
|
||
|
0x1820b744B33945482C17Dc37218C01D858EBc714
|
||
|
```
|
||
|
|
||
|
The contract has the address above for every chain on which it is deployed.
|
||
|
|
||
|
<details>
|
||
|
<summary>Raw metadata of <code>./contracts/ERC1820Registry.sol</code></summary>
|
||
|
<pre>
|
||
|
<code>{
|
||
|
"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": "Address being queried for the implementer of an interface. (If '_addr' is the zero address then 'msg.sender' is assumed.)",
|
||
|
"_interfaceHash": "Keccak256 hash of the name of the interface as a string. E.g., 'web3.utils.keccak256(\"ERC777TokensRecipient\")' for the 'ERC777TokensRecipient' interface."
|
||
|
},
|
||
|
"return": "The address of the contract which implements the interface `_interfaceHash` for `_addr.address()` or '0' if `_addr.address()` did not register an implementer for this interface."
|
||
|
},
|
||
|
"getManager(address)": {
|
||
|
"params": {
|
||
|
"_addr": "Address for which to return the manager."
|
||
|
},
|
||
|
"return": "Address of the manager for a given address."
|
||
|
},
|
||
|
"implementsERC165Interface(address,bytes4)": {
|
||
|
"params": {
|
||
|
"_contract": "Address of the contract to check.",
|
||
|
"_interfaceId": "ERC165 interface to check."
|
||
|
},
|
||
|
"return": "True if `_contract.address()` implements `_interfaceId`, false otherwise."
|
||
|
},
|
||
|
"implementsERC165InterfaceNoCache(address,bytes4)": {
|
||
|
"params": {
|
||
|
"_contract": "Address of the contract to check.",
|
||
|
"_interfaceId": "ERC165 interface to check."
|
||
|
},
|
||
|
"return": "True if `_contract.address()` implements `_interfaceId`, false otherwise."
|
||
|
},
|
||
|
"interfaceHash(string)": {
|
||
|
"params": {
|
||
|
"_interfaceName": "Name of the interface."
|
||
|
},
|
||
|
"return": "The keccak256 hash of an interface name."
|
||
|
},
|
||
|
"setInterfaceImplementer(address,bytes32,address)": {
|
||
|
"params": {
|
||
|
"_addr": "Address for which to set the interface. (If '_addr' is the zero address then 'msg.sender' is assumed.)",
|
||
|
"_implementer": "Contract address implementing `_interfaceHash` for `_addr.address()`.",
|
||
|
"_interfaceHash": "Keccak256 hash of the name of the interface as a string. E.g., 'web3.utils.keccak256(\"ERC777TokensRecipient\")' for the 'ERC777TokensRecipient' interface."
|
||
|
}
|
||
|
},
|
||
|
"setManager(address,address)": {
|
||
|
"params": {
|
||
|
"_addr": "Address for which to set the new manager.",
|
||
|
"_newManager": "Address of the new manager for `addr.address()`. (Pass '0x0' to reset the manager to `_addr.address()`.)"
|
||
|
}
|
||
|
},
|
||
|
"updateERC165Cache(address,bytes4)": {
|
||
|
"params": {
|
||
|
"_contract": "Address of the contract for which to update the cache.",
|
||
|
"_interfaceId": "ERC165 interface for which to update the cache."
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
"title": "ERC1820 Pseudo-introspection Registry Contract"
|
||
|
},
|
||
|
"userdoc": {
|
||
|
"methods": {
|
||
|
"getInterfaceImplementer(address,bytes32)": {
|
||
|
"notice": "Query if an address implements an interface and through which contract."
|
||
|
},
|
||
|
"getManager(address)": {
|
||
|
"notice": "Get the manager of an address."
|
||
|
},
|
||
|
"implementsERC165InterfaceNoCache(address,bytes4)": {
|
||
|
"notice": "Checks whether a contract implements an ERC165 interface or not without using nor updating the cache."
|
||
|
},
|
||
|
"interfaceHash(string)": {
|
||
|
"notice": "Compute the keccak256 hash of an interface given its name."
|
||
|
},
|
||
|
"setInterfaceImplementer(address,bytes32,address)": {
|
||
|
"notice": "Sets the contract which implements a specific interface for an address. Only the manager defined for that address can set it. (Each address is the manager for itself until it sets a new manager.)"
|
||
|
},
|
||
|
"setManager(address,address)": {
|
||
|
"notice": "Sets `_newManager.address()` as manager for `_addr.address()`. The new manager will be able to call 'setInterfaceImplementer' for `_addr.address()`."
|
||
|
},
|
||
|
"updateERC165Cache(address,bytes4)": {
|
||
|
"notice": "Updates the cache with whether the contract implements an ERC165 interface or not."
|
||
|
}
|
||
|
},
|
||
|
"notice": "This contract is the official implementation of the ERC1820 Registry.For more details, see 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: 87018\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
|
||
|
"keccak256": "0xdb01f4b0c42ecb3a60f096b0a082411875fbbd78e87d88868975ad9d983c030d"
|
||
|
}
|
||
|
},
|
||
|
"version": 1
|
||
|
}</code>
|
||
|
</pre>
|
||
|
</details>
|
||
|
|
||
|
### Interface Name
|
||
|
|
||
|
Any interface name is hashed using `keccak256` and sent to `getInterfaceImplementer()`.
|
||
|
|
||
|
If the interface is part of a standard, it is best practice to explicitly state the interface name and link to this published [ERC1820] such that other people don't have to come here to look up these rules.
|
||
|
|
||
|
For convenience, the registry provides a function to compute the hash on-chain:
|
||
|
|
||
|
``` solidity
|
||
|
function interfaceHash(string _interfaceName) public pure returns(bytes32)
|
||
|
```
|
||
|
|
||
|
Compute the keccak256 hash of an interface given its name.
|
||
|
|
||
|
> <small>**identifier:** `65ba36c1`</small>
|
||
|
> <small>**parameters**</small>
|
||
|
> <small>`_interfaceName`: Name of the interface.</small>
|
||
|
> <small>**returns:** The `keccak256` hash of an interface name.</small>
|
||
|
|
||
|
#### **Approved ERCs**
|
||
|
|
||
|
If the interface is part of an approved ERC, it MUST be named `ERC###XXXXX` where `###` is the number of the ERC and XXXXX should be the name of the interface in CamelCase.
|
||
|
The meaning of this interface SHOULD be defined in the specified ERC.
|
||
|
|
||
|
Examples:
|
||
|
|
||
|
- `keccak256("ERC20Token")`
|
||
|
- `keccak256("ERC777Token")`
|
||
|
- `keccak256("ERC777TokensSender")`
|
||
|
- `keccak256("ERC777TokensRecipient")`
|
||
|
|
||
|
#### **[ERC165] Compatible Interfaces**
|
||
|
|
||
|
> The compatibility with [ERC165], including the [ERC165 Cache], has been designed and developed with [William Entriken].
|
||
|
|
||
|
Any interface where the last 28 bytes are zeroes (`0`) SHALL be considered an [ERC165] interface.
|
||
|
|
||
|
**[ERC165] Lookup**
|
||
|
|
||
|
Anyone can explicitly check if a contract implements an [ERC165] interface using the registry by calling one of the two functions below:
|
||
|
|
||
|
``` solidity
|
||
|
function implementsERC165Interface(address _contract, bytes4 _interfaceId) public view returns (bool)
|
||
|
```
|
||
|
|
||
|
Checks whether a contract implements an [ERC165] interface or not.
|
||
|
|
||
|
If the result is not cached a direct lookup on the contract address is performed.
|
||
|
|
||
|
*NOTE*: If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling `updateERC165Cache` with the contract address.
|
||
|
(See [ERC165 Cache] for more details.)
|
||
|
|
||
|
> <small>**identifier:** `f712f3e8`</small>
|
||
|
> <small>**parameters**</small>
|
||
|
> <small>`_contract`: Address of the contract to check.</small>
|
||
|
> <small>`_interfaceId`: [ERC165] interface to check.</small>
|
||
|
> <small>**returns:** `true` if `_contract` implements `_interfaceId`, `false` otherwise.</small>
|
||
|
|
||
|
``` solidity
|
||
|
function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view returns (bool)
|
||
|
```
|
||
|
|
||
|
Checks whether a contract implements an [ERC165] interface or not without using nor updating the cache.
|
||
|
|
||
|
> <small>**identifier:** `b7056765`</small>
|
||
|
> <small>**parameters**</small>
|
||
|
> <small>`_contract`: Address of the contract to check.</small>
|
||
|
> <small>`_interfaceId`: [ERC165] interface to check.</small>
|
||
|
> <small>**returns:** `true` if `_contract` implements `_interfaceId`, false otherwise.</small>
|
||
|
|
||
|
**[ERC165] Cache** <a id="erc165-cache"></a>
|
||
|
|
||
|
Whether a contract implements an [ERC165] interface or not can be cached manually to save gas.
|
||
|
|
||
|
If a contract dynamically changes its interface and relies on the [ERC165] cache of the [ERC1820] registry, the cache MUST be updated manually---there is no automatic cache invalidation or cache update.
|
||
|
Ideally the contract SHOULD automatically update the cache when changing its interface.
|
||
|
However anyone MAY update the cache on the contract's behalf.
|
||
|
|
||
|
The cache update MUST be done using the `updateERC165Cache` function:
|
||
|
|
||
|
``` solidity
|
||
|
function updateERC165Cache(address _contract, bytes4 _interfaceId) external
|
||
|
```
|
||
|
|
||
|
> <small>**identifier:** `a41e7d51`</small>
|
||
|
> <small>**parameters**</small>
|
||
|
> <small>`_contract`: Address of the contract for which to update the cache.</small>
|
||
|
> <small>`_interfaceId`: [ERC165] interface for which to update the cache.</small>
|
||
|
|
||
|
#### **Private User-defined Interfaces**
|
||
|
|
||
|
This scheme is extensible.
|
||
|
You MAY make up your own interface name and raise awareness to get other people to implement it and then check for those implementations.
|
||
|
Have fun but please, you MUST not conflict with the reserved designations above.
|
||
|
|
||
|
### Set An Interface For An Address
|
||
|
|
||
|
For any address to set a contract as the interface implementation, it must call the following function of the [ERC1820] registry:
|
||
|
|
||
|
``` solidity
|
||
|
function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external
|
||
|
```
|
||
|
|
||
|
Sets the contract which implements a specific interface for an address.
|
||
|
|
||
|
Only the `manager` defined for that address can set it.
|
||
|
(Each address is the manager for itself, see the [manager] section for more details.)
|
||
|
|
||
|
*NOTE*: If `_addr` and `_implementer` are two different addresses, then:
|
||
|
|
||
|
- The `_implementer` MUST implement the `ERC1820ImplementerInterface` (detailed below).
|
||
|
- Calling `canImplementInterfaceForAddress` on `_implementer` with the given `_addr` and `_interfaceHash` MUST return the `ERC1820_ACCEPT_MAGIC` value.
|
||
|
|
||
|
*NOTE*: The `_interfaceHash` MUST NOT be an [ERC165] interface---it MUST NOT end with 28 zeroes (`0`).
|
||
|
|
||
|
*NOTE*: The `_addr` MAY be `0`, then `msg.sender` is assumed.
|
||
|
This default value simplifies interactions via multisigs where the data of the transaction to sign is constant regardless of the address of the multisig instance.
|
||
|
|
||
|
> <small>**identifier:** `29965a1d`</small>
|
||
|
> <small>**parameters**</small>
|
||
|
> <small>`_addr`: Address for which to set the interface. (If `_addr` is the zero address then `msg.sender` is assumed.)</small>
|
||
|
> <small>`_interfaceHash`: Keccak256 hash of the name of the interface as a string, for example `web3.utils.keccak256('ERC777TokensRecipient')` for the ERC777TokensRecipient interface.</small>
|
||
|
> <small>`_implementer`: Contract implementing `_interfaceHash` for `_addr`.</small>
|
||
|
|
||
|
### Get An Implementation Of An Interface For An Address
|
||
|
|
||
|
Anyone MAY query the [ERC1820] Registry to obtain the address of a contract implementing an interface on behalf of some address using the `getInterfaceImplementer` function.
|
||
|
|
||
|
``` solidity
|
||
|
function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address)
|
||
|
```
|
||
|
|
||
|
Query if an address implements an interface and through which contract.
|
||
|
|
||
|
*NOTE*: If the last 28 bytes of the `_interfaceHash` are zeroes (`0`), then the first 4 bytes are considered an [ERC165] interface and the registry SHALL forward the call to the contract at `_addr` to see if it implements the [ERC165] interface (the first 4 bytes of `_interfaceHash`).
|
||
|
The registry SHALL also cache [ERC165] queries to reduce gas consumption. Anyone MAY call the `erc165UpdateCache` function to update whether a contract implements an interface or not.
|
||
|
|
||
|
*NOTE*: The `_addr` MAY be `0`, then `msg.sender` is assumed.
|
||
|
This default value is consistent with the behavior of the `setInterfaceImplementer` function and simplifies interactions via multisigs where the data of the transaction to sign is constant regardless of the address of the multisig instance.
|
||
|
|
||
|
> <small>**identifier:** `aabbb8ca`</small>
|
||
|
> <small>**parameters**</small>
|
||
|
> <small>`_addr`: Address being queried for the implementer of an interface. (If `_addr` is the zero address then `msg.sender` is assumed.)</small>
|
||
|
> <small>`_interfaceHash`: keccak256 hash of the name of the interface as a string. E.g. `web3.utils.keccak256('ERC777Token')`</small>
|
||
|
> <small>**returns:** The address of the contract which implements the interface `_interfaceHash` for `_addr` or `0` if `_addr` did not register an implementer for this interface.</small>
|
||
|
|
||
|
|
||
|
### Interface Implementation (`ERC1820ImplementerInterface`)
|
||
|
|
||
|
``` solidity
|
||
|
interface ERC1820ImplementerInterface {
|
||
|
/// @notice Indicates whether the contract implements the interface `interfaceHash` for the address `addr` or not.
|
||
|
/// @param interfaceHash keccak256 hash of the name of the interface
|
||
|
/// @param addr Address for which the contract will implement the interface
|
||
|
/// @return ERC1820_ACCEPT_MAGIC only if the contract implements `interfaceHash` for the address `addr`.
|
||
|
function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view returns(bytes32);
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Any contract being registered as the implementation of an interface for a given address MUST implement said interface.
|
||
|
In addition if it implements an interface on behalf of a different address, the contract MUST implement the `ERC1820ImplementerInterface` shown above.
|
||
|
|
||
|
``` solidity
|
||
|
function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view returns(bytes32)
|
||
|
```
|
||
|
|
||
|
Indicates whether a contract implements an interface (`interfaceHash`) for a given address (`addr`).
|
||
|
|
||
|
If a contract implements the interface (`interfaceHash`) for a given address (`addr`), it MUST return `ERC1820_ACCEPT_MAGIC` when called with the `addr` and the `interfaceHash`.
|
||
|
If it does not implement the `interfaceHash` for a given address (`addr`), it MUST NOT return `ERC1820_ACCEPT_MAGIC`.
|
||
|
|
||
|
> <small>**identifier:** `f0083250`</small>
|
||
|
> <small>**parameters**</small>
|
||
|
> <small>`interfaceHash`: Hash of the interface which is implemented</small>
|
||
|
> <small>`addr`: Address for which the interface is implemented</small>
|
||
|
> <small>**returns:** `ERC1820_ACCEPT_MAGIC` only if the contract implements `ìnterfaceHash` for the address `addr`.</small>
|
||
|
|
||
|
The special value `ERC1820_ACCEPT_MAGIC` is defined as the `keccka256` hash of the string `"ERC1820_ACCEPT_MAGIC"`.
|
||
|
|
||
|
``` solidity
|
||
|
bytes32 constant internal ERC1820_ACCEPT_MAGIC = keccak256(abi.encodePacked("ERC1820_ACCEPT_MAGIC"));
|
||
|
```
|
||
|
|
||
|
> The reason to return `ERC1820_ACCEPT_MAGIC` instead of a boolean is to prevent cases where a contract fails to implement the `canImplementInterfaceForAddress` but implements a fallback function which does not throw. In this case, since `canImplementInterfaceForAddress` does not exist, the fallback function is called instead, executed without throwing and returns `1`. Thus making it appear as if `canImplementInterfaceForAddress` returned `true`.
|
||
|
|
||
|
### Manager
|
||
|
|
||
|
The manager of an address (regular account or a contract) is the only entity allowed to register implementations of interfaces for the address.
|
||
|
By default, any address is its own manager.
|
||
|
|
||
|
The manager can transfer its role to another address by calling `setManager` on the registry contract with the address for which to transfer the manager and the address of the new manager.
|
||
|
|
||
|
**`setManager` Function**
|
||
|
|
||
|
``` solidity
|
||
|
function setManager(address _addr, address _newManager) external
|
||
|
```
|
||
|
|
||
|
Sets `_newManager` as manager for `_addr`.
|
||
|
|
||
|
The new manager will be able to call `setInterfaceImplementer` for `_addr`.
|
||
|
|
||
|
If `_newManager` is `0x0`, the manager is reset to `_addr` itself as the manager.
|
||
|
|
||
|
> <small>**identifier:** `5df8122f`</small>
|
||
|
> <small>**parameters**</small>
|
||
|
> <small>`_addr`: Address for which to set the new manager.</small>
|
||
|
> <small>`_newManager`: The address of the new manager for `_addr`. (Pass `0x0` to reset the manager to `_addr`.)</small>
|
||
|
|
||
|
**`getManager` Function**
|
||
|
|
||
|
``` solidity
|
||
|
function getManager(address _addr) public view returns(address)
|
||
|
```
|
||
|
|
||
|
Get the manager of an address.
|
||
|
|
||
|
> <small>**identifier:** `3d584063`</small>
|
||
|
> <small>**parameters**</small>
|
||
|
> <small>`_addr`: Address for which to return the manager.</small>
|
||
|
> <small>**returns:** Address of the manager for a given address.</small>
|
||
|
|
||
|
## Rationale
|
||
|
|
||
|
This standards offers a way for any type of address (externally owned and contracts) to implement an interface and potentially delegate the implementation of the interface to a proxy contract.
|
||
|
This delegation to a proxy contract is necessary for externally owned accounts and useful to avoid redeploying existing contracts such as multisigs and DAOs.
|
||
|
|
||
|
The registry can also act as a [ERC165] cache in order to save gas when looking up if a contract implements a specific [ERC165] interface.
|
||
|
This cache is intentionally kept simple, without automatic cache update or invalidation.
|
||
|
Anyone can easily and safely update the cache for any interface and any contract by calling the `updateERC165Cache` function.
|
||
|
|
||
|
The registry is deployed using a keyless deployment method relying on a single-use deployment address to ensure no one controls the registry, thereby ensuring trust.
|
||
|
|
||
|
## Backward Compatibility
|
||
|
|
||
|
This standard is backward compatible with [ERC165], as both methods MAY be implemented without conflicting with each other.
|
||
|
|
||
|
## Test Cases
|
||
|
|
||
|
Please check the [0xjac/ERC1820] repository for the full test suite.
|
||
|
|
||
|
## Implementation
|
||
|
|
||
|
The implementation is available in the repo: [0xjac/ERC1820].
|
||
|
|
||
|
## Copyright
|
||
|
Copyright and related rights waived via [CC0].
|
||
|
|
||
|
[EIP155]: https://eips.ethereum.org/EIPS/eip-155
|
||
|
[ERC165]: https://eips.ethereum.org/EIPS/eip-165
|
||
|
[ERC672]: https://github.com/ethereum/EIPs/issues/672
|
||
|
[ERC820]: https://eips.ethereum.org/EIPS/eip-820
|
||
|
[ERC1820]: https://eips.ethereum.org/EIPS/eip-1820
|
||
|
[ERC1820 registry smart contract]: https://github.com/0xjac/ERC1820/blob/master/contracts/ERC1820Registry.sol
|
||
|
[erc1820-annoucement]: https://github.com/ethereum/EIPs/issues/820#issuecomment-464109166
|
||
|
[erc820-bug]: https://github.com/ethereum/EIPs/issues/820#issuecomment-452465748
|
||
|
[erc820-fix]: https://github.com/ethereum/EIPs/issues/820#issuecomment-454021564
|
||
|
[manager]: #manager
|
||
|
[lookup]: #get-an-implementation-of-an-interface-for-an-address
|
||
|
[ERC165 Cache]: #erc165-cache
|
||
|
[Nick's article]: https://medium.com/@weka/how-to-send-ether-to-11-440-people-187e332566b7
|
||
|
[0xjac/ERC1820]: https://github.com/0xjac/ERC1820
|
||
|
[CC0]: https://creativecommons.org/publicdomain/zero/1.0/
|
||
|
[Nick]: https://github.com/Arachnid/
|
||
|
[William Entriken]: https://github.com/fulldecent
|
||
|
[ENS]: https://ens.domains/
|