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:
A unified format for accounts that specifies, together with the address, the chain where the address lives.
The use of human-readable chain names, with resolution to chain identifiers via ENS.
The use of human-readable account names, with resolution to addresses via ENS.
An on-chain registry mapping chain names to identifiers, enabling decentralized resolution of chain metadata.
The ENS domain suffix used for chain names SHALL be abstracted from users for readability, ensuring a simpler display format.
Motivation
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/chainsSHALL 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.
Specification
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.
Format
This standard defines a sub-syntax with extra semantics on top of the Interoperable Names syntax defined in [ERC-7930].
<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.
Checksum
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.
Resolving chain names
Chain names without a dot (.) are interpreted as labels under a reserved ENS second-level domain .eth and resolved via a wildcard resolver contract. This resolver acts as a single source of truth for mapping human-readable chain names to their corresponding chain identifiers encoded in the [ERC-7930] binary format.
To enable onchain resolution between chain names and chain identifiers, a minimal L2Resolver contract SHOULD implement the following methods. The examples below assume the .eth name to be l2.eth.
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:
Encode the CAIP-2 identifier in ERC-7930 v1 binary form (AddressLength = 0); name the result chainBytes.
Query the Universal Resolver: result = UniversalResolver.resolve(dnsEncode(full), callData).
ABI-decode result as (bytes chainBytes).
• If chainBytes is empty → no mapping exists; treat the label as unknown.
Supported CASA namespaces
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.
Resolving address names
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:
For addresses on chains that are both supported by BIP-44 registered coin type identifiers and the special scheme for the eip155 namespace defined in ENSIP-11, the latter should be used.
Supported CASA namespaces
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.
Rationale
Using ENS as the only resolving method means it’s enough to use the same Interoperable Addresses v1 for this standard, without extending it to also store the name resolver used. This means however that wallets are free to show users both resolved and unresolved (raw) Interoperable Names.
Backwards Compatibility
The naming scheme herein defined can represent all names supported by [ERC-7930] by displaying raw addresses without resolution
Reference Implementation
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.
Forward resolution step-by-step example
Let the user input an address name. Assume it’s alice.eth.
Let the user select a chain. Assume it’s optimism. The l2.eth suffix is implicit and hidden from end users.
Append l2.eth and compute the ENS namehash as per ENSIP-1: namehash("optimism.l2.eth").
Call L2Resolver.chainId(bytes32 _node) with the result of step 3 as input, which returns the chain ID in [ERC-7930] byte format.
Convert the returned chain identifier into the CAIP-2 format, as specified by [ERC-7930].
As per ENSIP-11, convert the EIP-155chainId into an ENSIP-11-specific coinType: 0x80000000 & 0x0000000A == 0x8000000A.
Compute the namehash of the result of step 1, according to ENSIP-1.
Query the ENS registry for the appropriate resolver by calling resolver(bytes32 node) with the result of the step above.
Call 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
Failure mode: if result is address zero, that means the name is not registered and resolution can’t be finished.
Serialize ChainType, ChainReferenceLength, ChainReference, AddressLength and Address according to [ERC-7930] (Interoperable Address v1): [ 0002 0000 01 0A 14 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ]
Interoperable Address is complete: 0x00020000010A14AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Compute the checksum as described in [ERC-7930]: 0xC69BEB13 and display it to the user.
Reverse Resolution step-by-step example
Starting from the Interoperable Address serialized above: 0x00010000010A14AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Pick the first two bytes corresponding to the version: 0x0001. Remaining payload: 0000010A14AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA00010002
Parse the ChainType, ChainReferenceLength, ChainReference, AddressLength and Address according to [ERC-7930]. Remaining payload 00010002
ChainType: 0x0000 -> eip155
ChainReferenceLength: 0x01 -> 1
ChainReference: 0x0A -> 10 (optimism)
AddressLength: 0x14 -> 20 bytes, consistent with eip155
Call L2Resolver.chainName(bytes calldata _chainIdBytes) with 0x00000000010A00 as the input. The resolver will return the corresponding ENS name, in this case optimism.l2.eth.
Compute coinTypeAsHex for ENSIP-19:
Check if ChainType == 0x0000, as ENSIP-19 does not support non-EVM chains.
Check if 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.
Truncate the ChainReference to its 4 least significant bytes: 0x000000A
Set the ChainReference’s MSB to 1: 0x000000A | 0x8000000: 0x8000000A
Convert to a lowercase-hexadecimal string without 0x prefix: 8000000a
Compute the address as ENSIP-19 expects it, by producing the lowercase-hexadecimal string without 0x prefix of Address: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Convert the output from two steps above to a reverse lookup string of the form <address>.<coinType>.reverse for ENSIP-19: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.8000000a.reverse
Find resolver to use for address according to ENS wildcard resolution.
Call 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.
Call resolver(namehash(8000000a.reverse)) on the mainnet ENS registry. It’ll return 0x00000beef055f7934784d6d81b6bc86665630dba, the address of the L2ReverseRegistraron the network with chainid 10.
Verify the return of the coinType method of the L2ReverseRegistrar matches the coinType as serialized in step 7.
Call nameForAddr(bytes address) with the address as serialized in step 8. This will return the human-readable name alice.eth.
Check forward resolution of the name as described in ENSIP-11 and repeated in the section above.
- If it resolves to the same address, then proceed normally.
- If forward resolution of the ENS name returns an address different from the original, or returns an empty byte array, the wallet MUST display the raw, human-readable [ERC-7930] serialized address, indicating the address is unresolved. The wallet SHOULD also warn the user of the mismatch to prevent potential spoofing or confusion.
Compute the checksum as described in [ERC-7930]: 0xC69BEB13.
Format the address in the format <address>@<chain>#<checksum> by removing l2.eth from step 3 and display it to the user: alice.eth@optimism#C69BEB13
Security Considerations
Wallet developers should be aware of possible unicode glyph collisions in resolved names and warn users about them in order to keep checksums effective, since an attacker could, in order to impersonate alice.eth@chain.<<namespace>.eth#00112233:
Mine an address on chain.<namespace>.eth such that the checksum of the Interoperable Address is 00112233.
Register а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:
ZK-rollups may offer trust-minimized resolution via on-chain proofs.
Sidechains may rely on centralized CCIP-read gateways or signature authorities.
Wallets MUST surface whether reverse resolution was trust-minimized or trusted.