Understanding EIP-2304 and Its Role in the ENS Ecosystem
Ethereum Name Service (ENS) has evolved far beyond simple ETH address resolution. One of the most impactful yet frequently misunderstood standards is Ethereum Improvement Proposal 2304 (EIP-2304), which introduced multicoin address support via the addr field variant. This article provides a precise, technical breakdown of EIP-2304, answers common integration questions, and clarifies operational nuances for developers and protocol engineers.
EIP-2304 extends ENS by allowing a single ENS name to resolve to addresses on multiple blockchains. Prior to this standard, ENS records were limited to Ethereum addresses only. The proposal defines a mechanism where the existing addr(bytes32 node) function is overloaded with a coin type parameter: addr(bytes32 node, uint256 coinType). This enables a user to store, for example, a Bitcoin address, a Litecoin address, and an Ethereum address all under the same ENS name, with each address identified by its SLIP-44 coin type integer. The standard ensures backward compatibility: calling addr(node) without a coin type continues to return the primary Ethereum address.
For integrators, the key technical change is the shift from a single-address resolver to a multi-address resolver contract. The ENS PublicResolver was updated to implement the IAddrResolver and IAddressResolver interfaces, where the latter includes the coin-specific variant. This design allows resolvers to support any number of coin types without modifying the core ENS registry. The coin type values follow the SLIP-44 standard, which assigns unique integers to each cryptocurrency—for example, 0 for Bitcoin, 60 for Ethereum, 2 for Litecoin, and 714 for Binance Coin (BEP-2).
How EIP-2304 Resolves Cross-Chain Addresses: The Lookup Flow
The resolution process for a multicoin ENS record follows a deterministic sequence. To retrieve a non-Ethereum address, a caller must invoke resolver(ensNode).addr(ensNode, coinType). The resolver contract then performs a lookup using a mapping stored at the resolver level. The returned value is a byte array of variable length, typically 20 bytes for EVM-compatible chains (e.g., Ethereum, Polygon, Avalanche) or longer for others (e.g., 21 bytes for Bitcoin with a version byte, or 25 bytes for Litecoin).
Common pitfalls in the lookup flow:
- Coin type mismatch: Developers often mistakenly use chain IDs instead of SLIP-44 coin types. For instance, Ethereum mainnet chain ID is 1, but its SLIP-44 coin type is 60. Always verify against the official SLIP-44 registry.
- Byte encoding: Addresses for non-EVM chains must be base58-decoded (for Bitcoin-like chains) or bech32-decoded (for newer chains like Cosmos). The resolver expects raw bytes, not encoded strings. For Ethereum, the standard 20-byte hex address is converted to bytes without the "0x" prefix.
- Resolver contract support: Not all public resolvers implement the multicoin interface. Always check that the resolver contract address deployed for the ENS name supports the
addr(bytes32, uint256)function. The universalPublicResolversince v2.0.0 supports EIP-2304, but custom resolvers may not.
To programmatically discover which resolvers support multicoin resolution, you can perform a github verification of the resolver contract bytecode. Look for the presence of the 0x3b3b57de function selector (the signature for addr(bytes32)) and 0x7ae1cfca (the signature for addr(bytes32,uint256)) using tools like Etherscan's contract reader or via an ENS subgraph query that returns the resolver implementation details. This dual-check ensures your integration handles both legacy and multicoin-enabled names correctly.
Integrating EIP-2304: Backend and Frontend Considerations
Building a service that resolves ENS names to multicoin addresses requires careful handling of data types and network I/O. The following sections detail the critical integration steps.
1) Resolver Selection and Address Decoding
When your application receives an ENS name request, the first step is to resolve the resolver address from the ENS registry. With the ENS registry contract at 0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e (mainnet), call resolver(bytes32 node) to get the resolver contract. Then query supportsInterface(bytes4 interfaceId) on the resolver to verify it implements IAddrResolver (interface ID 0x3b3b57de) and IAddressResolver (interface ID 0x7ae1cfca). If only the former is supported, the name only has an Ethereum address.
For decoding returned bytes:
- EVM chains (coinType 60, 137, etc.): The returned bytes are exactly 20 bytes. Convert to hex string with leading "0x".
- Bitcoin-family chains (coinType 0, 2, 3, etc.): The first byte is the version byte (e.g., 0x00 for mainnet P2PKH, 0x05 for P2SH). The remaining bytes are the hash. The final 4 bytes are a checksum. To display as a standard address, you must re-encode using base58check.
- Other chains: Refer to the coin's SLIP-44 documentation for exact byte encoding. For example, Cosmos (coinType 118) uses bech32 encoding with a human-readable part (HRP) like "cosmos".
2) Gas-Efficient Batch Querying
If your application needs to resolve multiple coin types for the same ENS name, batch calls using multicall (available on Ethereum and L2s) will reduce gas costs significantly. The ENS PublicResolver does not expose a batch addr function, but you can aggregate calls via the Multicall contract. The recommended pattern is:
- Get the resolver address once.
- Call
addr(node, coinType1),addr(node, coinType2), etc., in a singlemulticalltransaction. - Parse the results based on coin type lengths.
This approach reduces RPC calls from N to 2 (one for resolver, one for multicall).
3) Caching and Staleness Detection
ENS records are mutable—the owner can change the resolver or update addresses at any time. Implement a cache with a Time-To-Live (TTL) derived from the ENS name's ttl field from the registry. However, note that the TTL on the registry is a default that can be overridden by the resolver. For real-time accuracy, you can listen to AddrChanged(bytes32 indexed node, address a) and AddressChanged(bytes32 indexed node, uint256 coinType, bytes newAddress) events. The latter is emitted when the multicoin address is updated. Subscribing to these events on-chain via a WebSocket provider allows your service to invalidate cached entries immediately.
Common EIP-2304 Integration Errors and Debugging
Developers frequently encounter a predictable set of errors when integrating EIP-2304. Below is a numbered breakdown of the most common issues and their resolutions.
1) "Resolver does not support coin type" error: This occurs when the resolver contract does not implement the addr(bytes32,uint256) function, or the function returns an empty byte array. The fix is to verify the resolver's interface support as described above. If using a custom resolver, ensure it inherits from AddrResolver and implements setAddr(bytes32,uint256,bytes) for writing and addr(bytes32,uint256) for reading.
2) Coin type zero confusion: Coin type 0 is reserved for Bitcoin, but many integrations mistakenly use 0 as a "default" or "no type" flag. Passing 0 to addr(node, 0) will query the Bitcoin address, not the Ethereum address (which is coinType 60). This is a common off-by-one conceptual error.
3) Byte length validation failures: Smart contract functions returning bytes are variable-length. If your client expects exactly 20 bytes but the resolver returns a different length (e.g., 21 bytes for a Bitcoin address with version), the client must handle arbitrary-length returns gracefully. Always check bytes.length before parsing.
4) Case sensitivity in ENS name normalization: ENS uses Ethereum's namehash algorithm, which is case-insensitive for the name itself but case-sensitive for the resolver. The name "vitalik.eth" and "VITALIK.ETH" resolve to the same node, but the ENS registry's owner and resolver lookups are case-insensitive. However, when calling the resolver's addr function, the node parameter must be the computed namehash; passing the raw string will fail.
5) Event indexing fragmentation: The AddressChanged event is indexed by node but not by coinType. This means you cannot easily filter events for a specific coin type across many names. A workaround is to maintain your own index of (node, coinType) -> address by listening to all AddressChanged events and filtering client-side. For large-scale monitoring, consider using an ENS subgraph query that indexes these events with coin type filters, reducing the need for heavy client-side processing.
Advanced Topics: Off-Chain Resolvers and L2 Integration
EIP-2304 is not limited to Layer 1 Ethereum. The standard works identically on Layer 2 solutions (Arbitrum, Optimism, Base) and sidechains (Polygon, BNB Smart Chain). However, there are nuances when using off-chain resolvers like DNS-based ENS (DNSSEC) or CCIP-Read (ENSIP-10).
Off-chain resolvers must handle multicoin queries by storing the full mapping off-chain and serving it through a gateway. The gateway's response format is defined by the original ENS resolver interface; the addr function signature is the same. For CCIP-Read resolvers, the off-chain proof must include the coin type as part of the query path. This adds complexity because the proof must prove the address for a specific coin type, not just the primary Ethereum address.
For L2 integrations, note that the ENS registry on L2 is a separate deployment. If a user registers a name on Arbitrum, the resolver must be deployed on Arbitrum as well. Cross-chain address resolution (e.g., resolving an L2-registered ENS name to a Bitcoin address) is fully supported because the resolver contract's addr(bytes32,uint256) function is chain-agnostic—the coin type specifies the destination chain, not the source chain. The name's node, however, must be computed using the same namehash algorithm across all chains.
Future Evolution: EIP-2304 and the Multichain ENS Roadmap
EIP-2304 remains the canonical standard for multicoin address resolution in ENS. While the ENS protocol is actively developing cross-chain name ownership via ENSIP-12 (Cross-Chain Name Wrapper) and ENSIP-13 (Batch Resolution), EIP-2304 will remain foundational. A potential future extension (still in discussion) is the addition of a multiAddr function that returns addresses for an array of coin types in a single call, reducing RPC calls further. The SLIP-44 registry is also likely to be extended as new blockchains launch, but EIP-2304's design—using a simple integer coin type—is infinitely extensible without protocol changes.
For now, developers building wallets, exchanges, or identity protocols should implement EIP-2304 as a mandatory standard. The combination of addr(node, coinType) and event-based caching provides a robust, future-proof approach to multichain address resolution. Always test against the official ENS public resolver on mainnet (address 0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41) and ensure your code handles edge cases like empty results and unsupported coin types gracefully.