mirror of https://github.com/status-im/EIPs.git
929 lines
60 KiB
Markdown
929 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/pull/1820
|
|
status: Final
|
|
type: Standards Track
|
|
category: ERC
|
|
requires: 165, 214
|
|
created: 2019-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: 53759
|
|
|
|
/// @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'
|
|
/// or '0' if '_addr' 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'.
|
|
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' as manager for '_addr'.
|
|
/// The new manager will be able to call 'setInterfaceImplementer' for '_addr'.
|
|
/// @param _addr Address for which to set the new manager.
|
|
/// @param _newManager Address of the new manager for 'addr'. (Pass '0x0' to reset the manager to '_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 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' 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' 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.
|
|
|
|
```
|
|
0xf90a388085174876e800830c35008080b909e5608060405234801561001057600080fd5b506109c5806100206000396000f3fe608060405234801561001057600080fd5b50600436106100a5576000357c010000000000000000000000000000000000000000000000000000000090048063a41e7d5111610078578063a41e7d51146101d4578063aabbb8ca1461020a578063b705676514610236578063f712f3e814610280576100a5565b806329965a1d146100aa5780633d584063146100e25780635df8122f1461012457806365ba36c114610152575b600080fd5b6100e0600480360360608110156100c057600080fd5b50600160a060020a038135811691602081013591604090910135166102b6565b005b610108600480360360208110156100f857600080fd5b5035600160a060020a0316610570565b60408051600160a060020a039092168252519081900360200190f35b6100e06004803603604081101561013a57600080fd5b50600160a060020a03813581169160200135166105bc565b6101c26004803603602081101561016857600080fd5b81019060208101813564010000000081111561018357600080fd5b82018360208201111561019557600080fd5b803590602001918460018302840111640100000000831117156101b757600080fd5b5090925090506106b3565b60408051918252519081900360200190f35b6100e0600480360360408110156101ea57600080fd5b508035600160a060020a03169060200135600160e060020a0319166106ee565b6101086004803603604081101561022057600080fd5b50600160a060020a038135169060200135610778565b61026c6004803603604081101561024c57600080fd5b508035600160a060020a03169060200135600160e060020a0319166107ef565b604080519115158252519081900360200190f35b61026c6004803603604081101561029657600080fd5b508035600160a060020a03169060200135600160e060020a0319166108aa565b6000600160a060020a038416156102cd57836102cf565b335b9050336102db82610570565b600160a060020a031614610339576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b6103428361092a565b15610397576040805160e560020a62461bcd02815260206004820152601a60248201527f4d757374206e6f7420626520616e204552433136352068617368000000000000604482015290519081900360640190fd5b600160a060020a038216158015906103b85750600160a060020a0382163314155b156104ff5760405160200180807f455243313832305f4143434550545f4d4147494300000000000000000000000081525060140190506040516020818303038152906040528051906020012082600160a060020a031663249cb3fa85846040518363ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018083815260200182600160a060020a0316600160a060020a031681526020019250505060206040518083038186803b15801561047e57600080fd5b505afa158015610492573d6000803e3d6000fd5b505050506040513d60208110156104a857600080fd5b5051146104ff576040805160e560020a62461bcd02815260206004820181905260248201527f446f6573206e6f7420696d706c656d656e742074686520696e74657266616365604482015290519081900360640190fd5b600160a060020a03818116600081815260208181526040808320888452909152808220805473ffffffffffffffffffffffffffffffffffffffff19169487169485179055518692917f93baa6efbd2244243bfee6ce4cfdd1d04fc4c0e9a786abd3a41313bd352db15391a450505050565b600160a060020a03818116600090815260016020526040812054909116151561059a5750806105b7565b50600160a060020a03808216600090815260016020526040902054165b919050565b336105c683610570565b600160a060020a031614610624576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b81600160a060020a031681600160a060020a0316146106435780610646565b60005b600160a060020a03838116600081815260016020526040808220805473ffffffffffffffffffffffffffffffffffffffff19169585169590951790945592519184169290917f605c2dbf762e5f7d60a546d42e7205dcb1b011ebc62a61736a57c9089d3a43509190a35050565b600082826040516020018083838082843780830192505050925050506040516020818303038152906040528051906020012090505b92915050565b6106f882826107ef565b610703576000610705565b815b600160a060020a03928316600081815260208181526040808320600160e060020a031996909616808452958252808320805473ffffffffffffffffffffffffffffffffffffffff19169590971694909417909555908152600284528181209281529190925220805460ff19166001179055565b600080600160a060020a038416156107905783610792565b335b905061079d8361092a565b156107c357826107ad82826108aa565b6107b85760006107ba565b815b925050506106e8565b600160a060020a0390811660009081526020818152604080832086845290915290205416905092915050565b6000808061081d857f01ffc9a70000000000000000000000000000000000000000000000000000000061094c565b909250905081158061082d575080155b1561083d576000925050506106e8565b61084f85600160e060020a031961094c565b909250905081158061086057508015155b15610870576000925050506106e8565b61087a858561094c565b909250905060018214801561088f5750806001145b1561089f576001925050506106e8565b506000949350505050565b600160a060020a0382166000908152600260209081526040808320600160e060020a03198516845290915281205460ff1615156108f2576108eb83836107ef565b90506106e8565b50600160a060020a03808316600081815260208181526040808320600160e060020a0319871684529091529020549091161492915050565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff161590565b6040517f01ffc9a7000000000000000000000000000000000000000000000000000000008082526004820183905260009182919060208160248189617530fa90519096909550935050505056fea165627a7a72305820377f4a2d4301ede9949f163f319021a6e9c687c292a5e2b2c4734c126b524e6c00291ba01820182018201820182018201820182018201820182018201820182018201820a01820182018201820182018201820182018201820182018201820182018201820
|
|
```
|
|
|
|
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
|
|
|
|
```
|
|
0xa990077c3205cbDf861e17Fa532eeB069cE9fF96
|
|
```
|
|
|
|
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
|
|
|
|
```
|
|
0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24
|
|
```
|
|
|
|
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' or '0' if '_addr' 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' implements '_interfaceId', false otherwise."
|
|
},
|
|
"implementsERC165InterfaceNoCache(address,bytes4)": {
|
|
"params": {
|
|
"_contract": "Address of the contract to check.",
|
|
"_interfaceId": "ERC165 interface to check."
|
|
},
|
|
"return": "True if '_contract' 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'.",
|
|
"_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'. (Pass '0x0' to reset the manager to '_addr'.)"
|
|
}
|
|
},
|
|
"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' as manager for '_addr'. The new manager will be able to call 'setInterfaceImplementer' for '_addr'."
|
|
},
|
|
"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: 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, _interfaceHash, _implementer);\n }\n\n /// @notice Sets '_newManager' as manager for '_addr'.\n /// The new manager will be able to call 'setInterfaceImplementer' for '_addr'.\n /// @param _addr Address for which to set the new manager.\n /// @param _newManager Address of the new manager for 'addr'. (Pass '0x0' to reset the manager to '_addr'.)\n function setManager(address _addr, address _newManager) external {\n require(getManager(_addr) == msg.sender, \"Not the manager\");\n managers[_addr] = _newManager == _addr ? address(0) : _newManager;\n emit ManagerChanged(_addr, _newManager);\n }\n\n /// @notice Get the manager of an address.\n /// @param _addr Address for which to return the manager.\n /// @return Address of the manager for a given address.\n function getManager(address _addr) public view returns(address) {\n // By default the manager of an address is the same address\n if (managers[_addr] == address(0)) {\n return _addr;\n } else {\n return managers[_addr];\n }\n }\n\n /// @notice Compute the keccak256 hash of an interface given its name.\n /// @param _interfaceName Name of the interface.\n /// @return The keccak256 hash of an interface name.\n function interfaceHash(string calldata _interfaceName) external pure returns(bytes32) {\n return keccak256(abi.encodePacked(_interfaceName));\n }\n\n /* --- ERC165 Related Functions --- */\n /* --- Developed in collaboration with William Entriken. --- */\n\n /// @notice Updates the cache with whether the contract implements an ERC165 interface or not.\n /// @param _contract Address of the contract for which to update the cache.\n /// @param _interfaceId ERC165 interface for which to update the cache.\n function updateERC165Cache(address _contract, bytes4 _interfaceId) external {\n interfaces[_contract][_interfaceId] = implementsERC165InterfaceNoCache(\n _contract, _interfaceId) ? _contract : address(0);\n erc165Cached[_contract][_interfaceId] = true;\n }\n\n /// @notice Checks whether a contract implements an ERC165 interface or not.\n // If the result is not cached a direct lookup on the contract address is performed.\n // If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling\n // 'updateERC165Cache' with the contract address.\n /// @param _contract Address of the contract to check.\n /// @param _interfaceId ERC165 interface to check.\n /// @return True if '_contract' implements '_interfaceId', false otherwise.\n function implementsERC165Interface(address _contract, bytes4 _interfaceId) public view returns (bool) {\n if (!erc165Cached[_contract][_interfaceId]) {\n return implementsERC165InterfaceNoCache(_contract, _interfaceId);\n }\n return interfaces[_contract][_interfaceId] == _contract;\n }\n\n /// @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.\n /// @param _contract Address of the contract to check.\n /// @param _interfaceId ERC165 interface to check.\n /// @return True if '_contract' implements '_interfaceId', false otherwise.\n function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view returns (bool) {\n uint256 success;\n uint256 result;\n\n (success, result) = noThrowCall(_contract, ERC165ID);\n if (success == 0 || result == 0) {\n return false;\n }\n\n (success, result) = noThrowCall(_contract, INVALID_ID);\n if (success == 0 || result != 0) {\n return false;\n }\n\n (success, result) = noThrowCall(_contract, _interfaceId);\n if (success == 1 && result == 1) {\n return true;\n }\n return false;\n }\n\n /// @notice Checks whether the hash is a ERC165 interface (ending with 28 zeroes) or not.\n /// @param _interfaceHash The hash to check.\n /// @return True if '_interfaceHash' is an ERC165 interface (ending with 28 zeroes), false otherwise.\n function isERC165Interface(bytes32 _interfaceHash) internal pure returns (bool) {\n return _interfaceHash & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0;\n }\n\n /// @dev Make a call on a contract without throwing if the function does not exist.\n function noThrowCall(address _contract, bytes4 _interfaceId)\n internal view returns (uint256 success, uint256 result)\n {\n bytes4 erc165ID = ERC165ID;\n\n assembly {\n let x := mload(0x40) // Find empty storage location using \"free memory pointer\"\n mstore(x, erc165ID) // Place signature at beginning of empty storage\n mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature\n\n success := staticcall(\n 30000, // 30k gas\n _contract, // To addr\n x, // Inputs are stored at location x\n 0x24, // Inputs are 36 (4 + 32) bytes long\n x, // Store output over input (saves space)\n 0x20 // Outputs are 32 bytes long\n )\n\n result := mload(x) // Load the result\n }\n }\n}\n",
|
|
"keccak256": "0x64025ecebddb6e126a5075c1fd6c01de2840492668e2909cef7157040a9d1945"
|
|
}
|
|
},
|
|
"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/
|