This proposal extends ERC-7930 (Interoperable Addresses & Names) by standardizing a human-readable format for chain-specific addresses in the form <address>@<chain>#<checksum>
. It introduces:
The current Ethereum address landscape is leading to an ecosystem that will have hundreds and eventually thousands of L2s that use the same address format as Ethereum mainnet. This means an address by itself is not enough information to know which chain the address is related to. This can be problematic if funds are sent to an unreachable address on the incorrect chain. From the user account it should be possible to obtain the right chain identifier (chainID) to include in a transaction.
The mapping from chain names to identifiers has, since EIP-155, been maintained off chain using a centralized list. This solution has a few shortcomings: - It does not scale with the growing number of L2s. - The list maintainer is a trusted centralized entity. - It does not (currently) support non-EVM chains, even when naming systems (such as ENS, since ENSIP-9) do.
Instead of using non-human-readable numeric chain identifiers, this specification SHALL require a human-readable chain name resolved on-chain via ENS wildcard resolver. The centralized chain list maintained in ethereum-lists/chains
SHALL be superseded by an on-chain registry. This registry provides a single source of truth for mapping chain names to chain identifiers and enables decentralized, extensible chain metadata resolution.
In the same spirit, the address could be a human-readable name as well, which is already a use case for ENS. By coupling the TLD to the resolving method used, this standard could leverage current and future features of ENS as well as other naming registries.
Moreover, the above can be leveraged to allow for a name to represent the same entity corresponding to different addresses on different chains, mitigating the risk of sending funds to an address the intended recipient doesn't actually control.
Desired properties: - Backwards compatibility with [ERC-7930] - The chain portion can be an ERC-7785 domain name when that standard is productive. - The address portion can be either the appropriate type of address for the chain, or a domain name. - The address portion and the chain portion should be resolved separately.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.
This standard defines a sub-syntax with extra semantics on top of the Interoperable Names syntax defined in [ERC-7930].
<human readable name>: := <address> "@" <chain> "#" <checksum>
<address>: := <raw-address> | <ens-name>
<chain>: := <raw-address> | <chain-name>
<checksum>: := [0-9A-F]{8}
<raw-address>: := [-:_%a-zA-Z0-9]+
<raw-chain>: := [-:_a-zA-Z0-9]+
<chain-name> := (<label> ".")* "<namespace>.eth"
<ens-name>: := (<label> .)+ <label>
Where:
<raw-chain>
, <raw-address>
and <checksum>
are defined to maintain backwards compatibility with [ERC-7930], and their semantics remain the same.<chain-name>
: A human-readable chain label within the reserved <namespace>.eth
domain. This standard uses <namespace>.eth
to provide a consistent abstraction for chain identity, mapping these labels to their corresponding canonical chain identifiers.<ens-name>
: An ENS name, which should be resolved following the appropiate ENSIPs (which this standard does not aim to overwrite). It's grammar definition is slightly different than that of ENSIP-1, as this standard does not support names consisting of only a TLD, since they'd be of limited usefulness.<label>
: Any valid string label per UTS46, as defined in ENSIP-1.This allows for Interoperable Addresses' text representation to mix and match 'resolved' and 'unresolved' usages in both the chain and address parts.
A few examples below:
Mainnet
- 0x12345...6789@eip155:1#FFFFFFFF
- 0x12345...6789@ethereum#FFFFFFFF
- alice.eth@eth#FFFFFFFF
Testnet (Sepolia)
- 0x12345...6789@eip155:11155111#00000000
- alice.testeth@sepolia#00000000
Rollup
- 0x12345...6789@eip155:4270#AAAAAAAA
- 0x12345...6789@arbitrum-nova#AAAAAAAA
- alice.eth@arbitrum-nova#AAAAAAAA
My ENS name is registered on rollup1 (via ENSIP-10 & ENSIP-16), but I want to receive funds on rollup2
- alice.rollup1.eth@rollup2#BBBBBBBB
Non-evm chain
- bc1..23@bip122:000000000019d6689c085ae165831e93#CCCCCCCC
- alice.eth@bip122:000000000019d6689c085ae165831e93#CCCCCCCC
Assuming the on-chain registry list adds a few other CAIP namespaces
- alice.eth@btc#CCCCCCCC
[!NOTE] This standard explicitly defines the use of an agreed
.eth second-level domain for chain names. Beyond this, hierarchical name structures continue to be inherited from ENS, as exemplified in the ENS-in-rollup example.
Interoperable Addresses MUST be serialized in the format defined by [ERC-7930]: ChainType, ChainReferenceLength, ChainReference, AddressLength, Address
A 4-byte checksum MUST be included when displaying or sharing addresses, as specified in [ERC-7930]. Wallets MUST compute and verify this checksum silently when parsing or validating addresses.
If a user-provided address includes a checksum, wallets MUST recompute it and compare it to the provided value. On mismatch, wallets MUST alert the user and MUST NOT allow sending funds without explicit override.
Wallets MAY display the checksum when formatting addresses, and MAY accept inputs without a checksum by computing it internally.
Chain names without a dot (.) are interpreted as labels under a reserved ENS second-level domain
To enable onchain resolution between chain names and chain identifiers, a minimal L2Resolver contract SHOULD implement the following methods. The examples below assume the function chainId(bytes32 _node) returns (bytes memory _chainBytes)
which resolves a chain l2.eth
name to its [ERC-7930] chain identifier representation.
- function chainName(bytes calldata _chainIdBytes) returns (string memory _chainName)
which resolves an [ERC-7930] chain identifier to a human-readable chain name.
To obtain the human-readable chain name corresponding to a CAIP-2 chain identifier, clients SHALL:
AddressLength = 0
); name the result chainBytes
.callData = abi.encodeWithSelector(IL2Resolver.chainName.selector, chainBytes)
.result = UniversalResolver.resolve(dnsEncode("l2.eth"), calldata)
.result
as (string fullName)
.fullName
ends with .l2.eth
.To obtain the chain identifier corresponding to a human-readable chain name, clients SHALL:
full = "arbitrum" + ".l2.eth"
.node = namehash(full)
as per ENSIP-1.callData = abi.encodeWithSelector(IL2Resolver.chainId.selector, node)
.result = UniversalResolver.resolve(dnsEncode(full), callData)
.result
as (bytes chainBytes)
.
• If chainBytes
is empty → no mapping exists; treat the label as unknown.While currently only eip155
chains are supported, the on-chain registry could start listing other chains in order to make human-readable names for non-evm chains possible.
The entirety of address name resolving of all TLDs is delegated to ENS, as specified in current & future ENSIPs.
Some caveats for ENS support are:
eip155
namespace defined in ENSIP-11, the latter should be used.Forward resolution (Interoperable Address -> Interoperable Name): Supported via the multichain address resolution standard defined in ERC-2304, which uses BIP-44 registered coin type identifiers to resolve names for various chains, including the eip155
namespace.
Reverse resolution (Interoperable Name -> Interoperable Address): EIP-155 chains explicitly supported by ENS by deployment of L2ReverseRegistrar
on them and registration of that contract's address on the ENS registry.
The naming scheme herein defined can represent all names supported by [ERC-7930] by displaying raw addresses without resolution
This section is non-normative, as future ENSIPs can modify the way in which ENS is used. Its purpose is to demonstrate how human-readable interoperable names are resolved to the [ERC-7930] binary format used at the protocol level. The following examples assume that chain names are registered as subdomains under l2.eth
, and that resolution is performed via L2Resolver
capable of mapping those subdomains to [ERC-7930] compliant chain identifiers.
alice.eth
.optimism
. The l2.eth
suffix is implicit and hidden from end users.l2.eth
and compute the ENS namehash as per ENSIP-1: namehash("optimism.l2.eth")
.L2Resolver.chainId(bytes32 _node)
with the result of step 3 as input, which returns the chain ID in [ERC-7930] byte format.chainId
into an ENSIP-11-specific coinType
: 0x80000000 & 0x0000000A == 0x8000000A
.resolver(bytes32 node)
with the result of the step above.addr(bytes32 node, uint256 coinType)
on the contract returned on the step above with the results of steps 6 and 5, respectively. This will return the 20 bytes of alice's OP Mainnet address, assume it's 0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa
ChainType
, ChainReferenceLength
, ChainReference
, AddressLength
and Address
according to [ERC-7930] (Interoperable Address v1): [ 0002 0000 01 0A 14 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ]
0x00020000010A14AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
0xC69BEB13
and display it to the user.Starting from the Interoperable Address serialized above: 0x00010000010A14AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
0x0001
. Remaining payload: 0000010A14AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA00010002
ChainType
, ChainReferenceLength
, ChainReference
, AddressLength
and Address
according to [ERC-7930]. Remaining payload 00010002
ChainType
: 0x0000
-> eip155
ChainReferenceLength
: 0x01
-> 1ChainReference
: 0x0A
-> 10 (optimism)AddressLength
: 0x14
-> 20 bytes, consistent with eip155
Address
: 0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa
-> alice's addressL2Resolver.chainName(bytes calldata _chainIdBytes)
with 0x00000000010A00
as the input. The resolver will return the corresponding ENS name, in this case optimism.l2.eth
.coinTypeAsHex
for ENSIP-19:ChainType == 0x0000
, as ENSIP-19 does not support non-EVM chains.ChainReference & 0xFFFFFFFF == ChainReference
, meaning, all 28 most significant bytes are zero. If this is not the case, fail resolution as ENSIP-19 does not support chainids larger than 4 bytes.ChainReference
to its 4 least significant bytes: 0x000000A
ChainReference
's MSB to 1: 0x000000A | 0x8000000
: 0x8000000A
0x
prefix: 8000000a
0x
prefix of Address
: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
<address>.<coinType>.reverse
for ENSIP-19: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.8000000a.reverse
resolver(namehash(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.8000000a.reverse))
on the mainnet ENS registry. It'll return the zero address, since there is no resolver registered on that address specifically.resolver(namehash(8000000a.reverse))
on the mainnet ENS registry. It'll return 0x00000beef055f7934784d6d81b6bc86665630dba
, the address of the L2ReverseRegistrar
on the network with chainid 10.coinType
method of the L2ReverseRegistrar
matches the coinType as serialized in step 7.nameForAddr(bytes address)
with the address as serialized in step 8. This will return the human-readable name alice.eth
.0xC69BEB13
.<address>@<chain>#<checksum>
by removing l2.eth
from step 3 and display it to the user: alice.eth@optimism#C69BEB13
alice.eth@chain.<<namespace>.eth#00112233
:chain.<namespace>.eth
such that the checksum of the Interoperable Address is 00112233
.аlice.eth
(using the russian vowel а
instead of the latin a
), and point it to the address above.ENSIP-19 introduces trust assumptions when resolving reverse records offchain:
Wallets MUST surface whether reverse resolution was trust-minimized or trusted.
Copyright and related rights waived via CC0. EIP-155 ERC-7930 ERC-7785 ERC-2304 BIP-44