forked from DecentralizedClimateFoundation/DCIPs
220 lines
13 KiB
Markdown
220 lines
13 KiB
Markdown
---
|
|
eip: 2304
|
|
title: Multichain address resolution for ENS
|
|
author: Nick Johnson <nick@ens.domains>
|
|
type: Standards Track
|
|
category: ERC
|
|
status: Stagnant
|
|
created: 2019-09-09
|
|
discussions-to: https://discuss.ens.domains/t/new-standard-proposal-ens-multicoin-support/1148
|
|
requires: 137
|
|
---
|
|
|
|
## Abstract
|
|
|
|
This EIP introduces new overloads for the the `addr` field for ENS resolvers, which permit resolution of addresses for other blockchains via ENS.
|
|
|
|
## Motivation
|
|
|
|
With the increasing uptake of ENS by multi-coin wallets, wallet authors have requested the ability to resolve addresses for non-Ethereum chains inside ENS. This specification standardises a way to enter and retrieve these addresses in a cross-client fashion.
|
|
|
|
## Specification
|
|
|
|
A new accessor function for resolvers is specified:
|
|
|
|
```solidity
|
|
function addr(bytes32 node, uint coinType) external view returns(bytes memory);
|
|
```
|
|
|
|
The EIP165 interface ID for this function is 0xf1cb7e06.
|
|
|
|
When called on a resolver, this function must return the cryptocurrency address for the specified namehash and coin type. A zero-length string must be returned if the specified coin ID does not exist on the specified node.
|
|
|
|
`coinType` is the cryptocurrency coin type index from [SLIP44](https://github.com/satoshilabs/slips/blob/master/slip-0044.md).
|
|
|
|
The return value is the cryptocurency address in its native binary format. Detailed descriptions of the binary encodings for several popular chains are provided in the Address Encoding section below.
|
|
|
|
A new event for resolvers is defined:
|
|
|
|
```solidity
|
|
event AddressChanged(bytes32 indexed node, uint coinType, bytes newAddress);
|
|
```
|
|
|
|
Resolvers MUST emit this event on each change to the address for a name and coin type.
|
|
|
|
### Recommended accessor functions
|
|
|
|
The following function provides the recommended interface for changing the addresses stored for a node. Resolvers SHOULD implement this interface for setting addresses unless their needs dictate a different interface.
|
|
|
|
```solidity
|
|
function setAddr(bytes32 node, uint coinType, bytes calldata addr);
|
|
```
|
|
|
|
`setAddr` adds or replaces the address for the given node and coin type. The parameters for this function are as per those described in `addr()` above.
|
|
|
|
This function emits an `AddressChanged` event with the new address; see also the backwards compatibility section below for resolvers that also support `addr(bytes32)`.
|
|
|
|
### Address Encoding
|
|
|
|
In general, the native binary representation of the address should be used, without any checksum commonly used in the text representation.
|
|
|
|
A table of encodings for common blockchains is provided, followed by a more detailed description of each format. In the table, 'encodings' lists the address encodings supported by that chain, along with any relevant parameters. Details of those address encodings are described in the following sections.
|
|
|
|
| Cryptocurrency | Coin Type | Encoding |
|
|
| --- | --- | --- |
|
|
| Bitcoin | 0 | P2PKH(0x00), P2SH(0x05), SegWit('bc') |
|
|
| Litecoin | 2 | P2PKH(0x30), P2SH(0x32), P2SH(0x05), SegWit('ltc') |
|
|
| Dogecoin | 3 | P2PKH(0x1e), P2SH(0x16) |
|
|
| Monacoin | 22 | P2PKH(0x32), P2SH(0x05) |
|
|
| Ethereum | 60 | ChecksummedHex |
|
|
| Ethereum Classic | 61 | ChecksummedHex |
|
|
| Rootstock | 137 | ChecksummedHex(30) |
|
|
| Ripple | 144 | Ripple |
|
|
| Bitcoin Cash | 145 | P2PKH(0x00), P2SH(0x05), CashAddr |
|
|
| Binance | 714 | Bech32('bnb') |
|
|
|
|
#### P2PKH(version)
|
|
|
|
Pay to Public Key Hash addresses are [base58check](https://en.bitcoin.it/wiki/Base58Check_encoding) encoded. After decoding, the first byte is a version byte. For example, the Bitcoin address `1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa` base58check decodes to the 21 bytes `0062e907b15cbf27d5425399ebf6f0fb50ebb88f18`.
|
|
|
|
P2PKH addresses have a version byte, followed by a 20 byte pubkey hash. Their canonical encoding is their scriptPubkey encoding (specified [here](https://en.bitcoin.it/wiki/Transaction#Types_of_Transaction)) is `OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG`.
|
|
|
|
The above example address is thus encoded as the 25 bytes `76a91462e907b15cbf27d5425399ebf6f0fb50ebb88f1888ac`.
|
|
|
|
##### P2SH(version)
|
|
|
|
P2SH addresses are base58check encoded in the same manner as P2PKH addresses.
|
|
P2SH addresses have a version, followed by a 20 byte script hash. Their scriptPubkey encoding (specified [here](https://en.bitcoin.it/wiki/Transaction#Pay-to-Script-Hash)) is `OP_HASH160 <scriptHash> OP_EQUAL`. A Bitcoin address of `3Ai1JZ8pdJb2ksieUV8FsxSNVJCpoPi8W6` decodes to the 21 bytes `0562e907b15cbf27d5425399ebf6f0fb50ebb88f18` and is encoded as the 23 bytes `a91462e907b15cbf27d5425399ebf6f0fb50ebb88f1887`.
|
|
|
|
##### SegWit(hrp)
|
|
|
|
SegWit addresses are encoded with [bech32](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki). Bech32 addresses consist of a human-readable part - 'bc' for Bitcoin mainnet - and a machine readable part. For SegWit addresses, this decodes to a 'witness version', between 0 and 15, and a 'witness program', as defined in [BIP141](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki).
|
|
|
|
The scriptPubkey encoding for a bech32 address, as defined in BIP141, is `OP_n`, where `n` is the witness version, followed by a push of the witness program. Note this warning from BIP173:
|
|
|
|
> Implementations should take special care when converting the address to a scriptPubkey, where witness version n is stored as OP_n. OP_0 is encoded as 0x00, but OP_1 through OP_16 are encoded as 0x51 though 0x60 (81 to 96 in decimal). If a bech32 address is converted to an incorrect scriptPubKey the result will likely be either unspendable or insecure.
|
|
|
|
For example, the Bitcoin SegWit address `BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4` decodes to a version of `0` and a witness script of `751e76e8199196d454941c45d1b3a323f1433bd6`, and then encodes to a scriptPubkey of `0014751e76e8199196d454941c45d1b3a323f1433bd6`.
|
|
|
|
#### ChecksummedHex(chainId?)
|
|
|
|
To translate a text format checksummed hex address into binary format, simply remove the '0x' prefix and hex decode it. `0x314159265dD8dbb310642f98f50C066173C1259b` is hex-decoded and stored as the 20 bytes `314159265dd8dbb310642f98f50c066173c1259b`.
|
|
|
|
A checksum format is specified by [EIP-55](./eip-55.md), and extended by [RSKIP60](https://github.com/rsksmart/RSKIPs/blob/master/IPs/RSKIP60.md), which specifies a means of including the chain ID in the checksum. The checksum on a text format address must be checked. Addresses with invalid checksums that are not all uppercase or all lowercase MUST be rejected with an error. Implementations may choose whether to accept non-checksummed addresses, but the authors recommend at least providing a warning to users in this situation.
|
|
|
|
When encoding an address from binary to text, an EIP55/RSKIP60 checksum MUST be used - so the correct encoding of the above address for Ethereum is `0x314159265dD8dbb310642f98f50C066173C1259b`.
|
|
|
|
#### Ripple
|
|
|
|
Ripple addresses are encoded using a version of base58check with an alternative alphabet, described [here](https://xrpl.org/base58-encodings.html). Two types of ripple addresses are supported, 'r-addresses', and 'X-addresss'. r-addresses consist of a version byte followed by a 20 byte hash, while X-addresses consist of a version byte, a 20 byte hash, and a tag, specified [here](https://github.com/xrp-community/standards-drafts/issues/6).
|
|
|
|
Both address types should be stored in ENS by performing ripple's version of base58check decoding and storing them directly (including version byte). For example, the ripple address `rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn` decodes to and is stored as `004b4e9c06f24296074f7bc48f92a97916c6dc5ea9`, while the address `X7qvLs7gSnNoKvZzNWUT2e8st17QPY64PPe7zriLNuJszeg` decodes to and is stored as `05444b4e9c06f24296074f7bc48f92a97916c6dc5ea9000000000000000000`.
|
|
|
|
#### CashAddr
|
|
|
|
Bitcoin Cash defines a new address format called 'CashAddr', specified [here](https://github.com/bitcoincashorg/bitcoincash.org/blob/master/spec/cashaddr.md). This uses a variant of bech32 encoding to encode and decode (non-segwit) Bitcoin Cash addresses, using a prefix of 'bitcoincash:'. A CashAddr should be decoded using this bech32 variant, then converted and stored based on its type (P2PKH or P2SH) as described in the relevant sections above.
|
|
|
|
#### Bech32
|
|
|
|
[Bech32](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki) addresses consist of a human-readable part - for example, 'bnb' for Binance - and a machine readable part. The encoded data is simply the address, which can be converted to binary and stored directly.
|
|
|
|
For example, the BNB address `bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2` decodes to the binary representation `40c2979694bbc961023d1d27be6fc4d21a9febe6`, which is stored directly in ENS.
|
|
|
|
### Example
|
|
|
|
An example implementation of a resolver that supports this EIP is provided here:
|
|
|
|
```solidity
|
|
pragma solidity ^0.5.8;
|
|
|
|
contract AddrResolver is ResolverBase {
|
|
bytes4 constant private ADDR_INTERFACE_ID = 0x3b3b57de;
|
|
bytes4 constant private ADDRESS_INTERFACE_ID = 0xf1cb7e06;
|
|
uint constant private COIN_TYPE_ETH = 60;
|
|
|
|
event AddrChanged(bytes32 indexed node, address a);
|
|
event AddressChanged(bytes32 indexed node, uint coinType, bytes newAddress);
|
|
|
|
mapping(bytes32=>mapping(uint=>bytes)) _addresses;
|
|
|
|
/**
|
|
* Sets the address associated with an ENS node.
|
|
* May only be called by the owner of that node in the ENS registry.
|
|
* @param node The node to update.
|
|
* @param a The address to set.
|
|
*/
|
|
function setAddr(bytes32 node, address a) external authorised(node) {
|
|
setAddr(node, COIN_TYPE_ETH, addressToBytes(a));
|
|
}
|
|
|
|
/**
|
|
* Returns the address associated with an ENS node.
|
|
* @param node The ENS node to query.
|
|
* @return The associated address.
|
|
*/
|
|
function addr(bytes32 node) public view returns (address) {
|
|
bytes memory a = addr(node, COIN_TYPE_ETH);
|
|
if(a.length == 0) {
|
|
return address(0);
|
|
}
|
|
return bytesToAddress(a);
|
|
}
|
|
|
|
function setAddr(bytes32 node, uint coinType, bytes memory a) public authorised(node) {
|
|
emit AddressChanged(node, coinType, a);
|
|
if(coinType == COIN_TYPE_ETH) {
|
|
emit AddrChanged(node, bytesToAddress(a));
|
|
}
|
|
_addresses[node][coinType] = a;
|
|
}
|
|
|
|
function addr(bytes32 node, uint coinType) public view returns(bytes memory) {
|
|
return _addresses[node][coinType];
|
|
}
|
|
|
|
function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
|
|
return interfaceID == ADDR_INTERFACE_ID || interfaceID == ADDRESS_INTERFACE_ID || super.supportsInterface(interfaceID);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Implementation
|
|
|
|
An implementation of this interface is provided in the [ensdomains/resolvers](https://github.com/ensdomains/resolvers/) repository.
|
|
|
|
## Backwards Compatibility
|
|
|
|
If the resolver supports the `addr(bytes32)` interface defined in EIP137, the resolver MUST treat this as a special case of this new specification in the following ways:
|
|
|
|
1. The value returned by `addr(node)` from EIP137 should always match the value returned by `addr(node, 60)` (60 is the coin type ID for Ethereum).
|
|
2. Anything that causes the `AddrChanged` event from EIP137 to be emitted must also emit an `AddressChanged` event from this EIP, with the `coinType` specified as 60, and vice-versa.
|
|
|
|
## Tests
|
|
|
|
The table below specifies test vectors for valid address encodings for each cryptocurrency described above.
|
|
|
|
| Cryptocurrency | Coin Type | Text | Onchain (hex) |
|
|
| --- | --- | --- | --- |
|
|
| Bitcoin | 0 | `1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa` | `76a91462e907b15cbf27d5425399ebf6f0fb50ebb88f1888ac` |
|
|
| | | `3Ai1JZ8pdJb2ksieUV8FsxSNVJCpoPi8W6` | `a91462e907b15cbf27d5425399ebf6f0fb50ebb88f1887` |
|
|
| | | `BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4` | `0014751e76e8199196d454941c45d1b3a323f1433bd6` |
|
|
| Litecoin | 2 | `LaMT348PWRnrqeeWArpwQPbuanpXDZGEUz` | `76a914a5f4d12ce3685781b227c1f39548ddef429e978388ac` |
|
|
| | | `MQMcJhpWHYVeQArcZR3sBgyPZxxRtnH441` | `a914b48297bff5dadecc5f36145cec6a5f20d57c8f9b87` |
|
|
| | | `ltc1qdp7p2rpx4a2f80h7a4crvppczgg4egmv5c78w8` | `0014687c150c26af5493befeed7036043812115ca36c` |
|
|
| Dogecoin | 3 | `DBXu2kgc3xtvCUWFcxFE3r9hEYgmuaaCyD` | `76a9144620b70031f0e9437e374a2100934fba4911046088ac` |
|
|
| | | `AF8ekvSf6eiSBRspJjnfzK6d1EM6pnPq3G` | `a914f8f5d99a9fc21aa676e74d15e7b8134557615bda87` |
|
|
| Monacoin | 22 | `MHxgS2XMXjeJ4if2PRRbWYcdwZPWfdwaDT` | `76a9146e5bb7226a337fe8307b4192ae5c3fab9fa9edf588ac` |
|
|
| Ethereum | 60 | `0x314159265dD8dbb310642f98f50C066173C1259b` | `314159265dd8dbb310642f98f50c066173c1259b` |
|
|
| Ethereum Classic | 61 | `0x314159265dD8dbb310642f98f50C066173C1259b` | `314159265dd8dbb310642f98f50c066173c1259b` |
|
|
| Rootstock | 137 | `0x5aaEB6053f3e94c9b9a09f33669435E7ef1bEAeD` | `5aaeb6053f3e94c9b9a09f33669435e7ef1beaed` |
|
|
| Ripple | 144 | `rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn` | `004b4e9c06f24296074f7bc48f92a97916c6dc5ea9` |
|
|
| | | `X7qvLs7gSnNoKvZzNWUT2e8st17QPY64PPe7zriLNuJszeg` | `05444b4e9c06f24296074f7bc48f92a97916c6dc5ea9000000000000000000` |
|
|
| Bitcoin Cash | 145 | `1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu` | `76a91476a04053bda0a88bda5177b86a15c3b29f55987388ac` |
|
|
| | | `bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a` | `76a91476a04053bda0a88bda5177b86a15c3b29f55987388ac` |
|
|
| | | `3CWFddi6m4ndiGyKqzYvsFYagqDLPVMTzC` | `a91476a04053bda0a88bda5177b86a15c3b29f55987387` |
|
|
| | | `bitcoincash:ppm2qsznhks23z7629mms6s4cwef74vcwvn0h829pq` | `a91476a04053bda0a88bda5177b86a15c3b29f55987387` |
|
|
| Binance | 714 | `bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2` | `40c2979694bbc961023d1d27be6fc4d21a9febe6` |
|
|
|
|
## Copyright
|
|
Copyright and related rights waived via [CC0](../LICENSE.md).
|