Introduction to ENS EIP-2304
Ethereum Name Service (ENS) has evolved beyond simple ETH address mapping. With the introduction of EIP-2304, ENS now supports multicoin address resolution, allowing a single ENS name to hold multiple cryptocurrency addresses (e.g., Bitcoin, Litecoin, Dogecoin) in a single record. This improvement is critical for developers and users managing cross-chain identities. However, implementing EIP-2304 requires understanding its data encoding, resolver architecture, and integration tradeoffs. This article provides a methodical breakdown of what you need to know before adopting ENS EIP-2304.
What EIP-2304 Solves: Multicoin Address Resolution
Before EIP-2304, ENS resolvers stored only Ethereum addresses. If you wanted an ENS name like "alice.eth" to map to a Bitcoin address, you needed custom, non-standard resolver logic. EIP-2304 standardizes this by introducing a new multicoin address resolver that uses coin type identifiers as defined in SLIP-0044. The key improvement is a universal addr(bytes32 node, uint256 coinType) function that returns a bytes array containing the raw address for any supported blockchain.
For developers, this means no more bespoke mappings. Instead, you call a single resolver function with the coin type integer (e.g., 0 for Bitcoin, 2 for Litecoin) and receive the appropriate address. The process is deterministic, saving time and reducing errors. But this simplicity hides complexity in data encoding: Bitcoin addresses use Base58Check, while Ethereum uses hex. EIP-2304 mandates that addresses are stored as their raw binary representation (e.g., 20 bytes for Ethereum, 21 bytes for Bitcoin with a version byte), and client applications handle the conversion to user-friendly formats. This design keeps the on-chain data compact but shifts the burden to front-end tools.
Implementing EIP-2304: Step-by-Step for Developers
To integrate multicoin support into your dApp or wallet, follow this concrete workflow:
- Determine Supported Coins: List the coin types you want to support using SLIP-0044 identifiers. Common ones include 0 (Bitcoin), 2 (Litecoin), 3 (Dogecoin), 60 (Ethereum), 194 (ERC-20 tokens—though note these are typically handled separately via text records). Each coin type has a specific address length and format; you must know the expected byte count. For instance, Bitcoin addresses are 21 bytes (1 version byte + 20 hash bytes), while Ethereum addresses are 20 bytes.
- Use the Right Resolver Interface: Your contract or client must call the
IMulticoinAddrResolverinterface, which extends the standardIAddrResolver. The critical function isaddr(bytes32 node, uint256 coinType). Ensure your resolver contract implements this interface. Most existing ENS resolvers (e.g., the public resolver) support EIP-2304 by default since the upgrade. - Encode Addresses Correctly: When setting an address via the resolver, you must encode it as raw bytes. For Bitcoin: convert the Base58Check string to a 21-byte sequence (version byte + hash160). For Ethereum: convert the hex address to a 20-byte sequence (no checksum). Incorrect encoding will break resolution. Use libraries like
@ethersproject/addressorbitcoinjs-libfor conversion. - Test with a Local Node: Deploy a test ENS registry on a local testnet (e.g., Hardhat). Set a name, assign a resolver that supports multicoin, then call
addr(namehash, coinType)to verify the returned bytes match your input. Tools likeensjs(v0.5.0+) provide built-in multicoin support — use thegetAddr(name, coinType)method. - Handle Client-Side Decoding: On the front end, after retrieving the
bytesoutput, convert it back to a user-readable address. For Bitcoin, prepend the version byte to the hash, compute checksum, and Base58Check-encode. For Ethereum, hex-encode the bytes and add the checksum (EIP-55). Display only after validation.
A concrete example: To resolve "alice.eth" Bitcoin address using ethers.js, you write:
const resolver = await provider.getResolver('alice.eth');
const btcBytes = await resolver.getAddr(0); // coinType 0 = Bitcoin
const btcAddress = bitcoinAddress.fromBytes(btcBytes); // library-specific
Key Tradeoffs and Performance Considerations
Adopting EIP-2304 introduces several tradeoffs that developers must weigh:
- Storage Efficiency vs. Gas Costs: Each coin type address is stored as a separate mapping entry in the resolver contract. Storing one additional address (e.g., a Bitcoin address) costs approximately 20,000–30,000 gas due to SSTORE operations. For names with many coin types (e.g., 10+), gas costs can grow linearly. Consider batching multiple coin type writes in a single transaction using a multi-address setter function if your resolver supports it.
- Client-Side Complexity: While the on-chain format is uniform, client libraries must handle diverse address encoding schemes. This increases testing overhead: your dApp must validate Bitcoin, Litecoin, and Dogecoin addresses correctly. Mistranslation (e.g., missing a version byte) results in invalid addresses and potential fund loss. Always use battle-tested libraries (
@ensdomains/address-encoderis recommended). - Backward Compatibility: Older resolvers that lack multicoin support will return empty bytes for any
addr(node, coinType)call. You must check if the resolver implementsIMulticoinAddrResolverbefore querying. UsesupportsInterface('0x...')(the interface ID for multicoin resolver is0xf1cb7e06) to fallback gracefully. - Cross-Chain Trust: EIP-2304 only stores addresses on Ethereum. The actual blockchain (Bitcoin, Litecoin) is not involved. This means the ENS name's owner controls the mapping, but there is no cryptographic proof linking the ENS name to the off-chain address. Users must trust that the owner has correctly set the address. For critical use cases (e.g., high-value transfers), consider additional verification mechanisms (e.g., signed attestations).
For a deeper exploration of these tradeoffs and practical integration examples, visit the discord verification which offers detailed guides on resolver optimization and multicoin testing methodologies.
Common Pitfalls and How to Avoid Them
Based on community experience, the following issues frequently arise during EIP-2304 implementation:
- Wrong Coin Type Identifiers: Using an incorrect SLIP-0044 integer leads to resolution of the wrong chain. For instance, coin type 2 is Litecoin, but coin type 3 is Dogecoin. Double-check the SLIP-0044 registry before hardcoding values. Store coin types as constants in your codebase, not magic numbers.
- Address Encoding Mismatch: A common error is using hex-encoded string when bytes are expected, or vice versa. Remember: the resolver returns raw bytes; your client must decode them. Never compare or display raw bytes directly — always convert to the appropriate address format.
- Ignoring the Namehash Function: EIP-2304 still uses the standard ENS namehash algorithm. Failing to compute the correct namehash (e.g., for subdomains like "pay.alice.eth") results in failed resolution. Use the
namehashfunction from ethers.js or web3.js consistently. - Gas Estimation Errors: When setting addresses for many coin types, gas estimation may be inaccurate due to varying storage slots. Set explicit gas limits (e.g., 100,000 gas per coin type) in your transaction to avoid "out of gas" failures.
- Not Handling Empty Resolvers: If a name has no resolver set,
getAddrwill throw an error. Always wrap calls in a try-catch block and handle the case where the resolver is not deployed or non-multicoin.
If you encounter issues with lost or misconfigured ENS names, you may need to reclaim ENS domain access via the domain renewal process, which ensures your resolver settings remain intact. This is particularly important after long periods of inactivity, as expired domains can be contested.
Future-Proofing Your Integration
ENS continues to evolve. EIP-2304 is now part of the ENSIP (ENS Improvement Proposals) framework, and future proposals may add new coin types or encoding schemes. To ensure your application remains compatible:
- Use Abstract Interfaces: Instead of importing a specific resolver implementation, depend on the
IMulticoinAddrResolverinterface. This allows your code to work with any compliant resolver. - Monitor SLIP-0044 Updates: New coin types are added periodically. Subscribe to the SLIP repository or use an off-chain registry that tracks updates (e.g.,
@ensdomains/address-encodermaintains a list). - Test with Multiple Coin Types: Even if you only need Ethereum and Bitcoin now, test your code with coin type 2 (Litecoin) and 3 (Dogecoin) to verify your encoding/decoding pipeline is generic. This catches early mistakes.
- Cache Resolver Data: Since multicoin lookups are read-heavy, cache results client-side using a TTL (e.g., 5 minutes). Avoid repeated on-chain calls for the same name-coin pair.
Conclusion
EIP-2304 is a straightforward yet powerful extension to ENS, enabling multicoin address resolution with minimal on-chain complexity. The main challenges lie in client-side encoding and gas management. By following the steps outlined above—correctly encoding addresses, using the right interfaces, and handling edge cases—you can integrate multicoin support robustly. Always test with actual testnet transactions before deploying to mainnet, and leverage community-maintained libraries to avoid encoding errors. As the ENS ecosystem grows, EIP-2304 will likely become a standard feature in multichain wallets and cross-chain protocols.