ENS provides a decentralized naming system that makes blockchain addresses human-readable. This guide covers the essential contracts and patterns for integrating ENS into your application.
Overview
Integrating ENS into your application involves:
- Resolving ENS names to addresses
- Setting up reverse resolution
- Managing resolver records
- Handling different coin types and chains
Core Contracts
ENS Registry
The ENS registry (ENSRegistry.sol) is the core contract that maintains the ownership and resolver information for all ENS names.
Key Functions:
owner(bytes32 node) - Returns the owner of a name
resolver(bytes32 node) - Returns the resolver for a name
setResolver(bytes32 node, address resolver) - Sets the resolver for a name
Public Resolver
The PublicResolver contract implements various resolver profiles:
- AddrResolver - Address resolution (EIP-137, EIP-2304)
- TextResolver - Text records (EIP-634)
- ContentHashResolver - Content hash support (EIP-1577)
- NameResolver - Reverse resolution (EIP-181)
- ABIResolver - Contract ABI support (EIP-205)
- PubkeyResolver - Public key records (EIP-619)
Source: ~/workspace/source/contracts/resolvers/PublicResolver.sol:19-29
Installation
Install the ENS contracts package:
npm install @ensdomains/ens-contracts
Importing Contracts
JavaScript/TypeScript
import {
ENS,
ENSRegistry,
PublicResolver,
ETHRegistrarController,
ReverseRegistrar
} from '@ensdomains/ens-contracts'
Solidity
import '@ensdomains/ens-contracts/contracts/registry/ENS.sol';
import '@ensdomains/ens-contracts/contracts/resolvers/PublicResolver.sol';
Basic Integration Pattern
Get the ENS Registry
Connect to the ENS registry contract deployed on your network.import { namehash } from 'viem'
const ENS_REGISTRY_ADDRESS = '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e'
const ensRegistry = await viem.getContractAt(
'ENSRegistry',
ENS_REGISTRY_ADDRESS
)
Resolve the Name
Use namehash to convert the ENS name to a node, then look up its resolver.const node = namehash('vitalik.eth')
const resolverAddress = await ensRegistry.read.resolver([node])
Query the Resolver
Get the address from the resolver contract.const resolver = await viem.getContractAt(
'PublicResolver',
resolverAddress
)
const address = await resolver.read.addr([node])
Multi-Chain Address Resolution
ENS supports addresses for multiple blockchains using coin types (ENSIP-9).
import { namehash } from 'viem'
const node = namehash('example.eth')
const resolver = await viem.getContractAt('PublicResolver', resolverAddress)
// Get Ethereum address (coin type 60)
const ethAddress = await resolver.read.addr([node, 60])
// Get Bitcoin address (coin type 0)
const btcAddress = await resolver.read.addr([node, 0])
// Get Polygon address (coin type 966)
const polygonAddress = await resolver.read.addr([node, 966])
Source: ~/workspace/source/contracts/resolvers/profiles/AddrResolver.sol:47-66
Text Records
Text records store arbitrary key-value data for ENS names.
const node = namehash('example.eth')
const resolver = await viem.getContractAt('PublicResolver', resolverAddress)
// Read text records
const email = await resolver.read.text([node, 'email'])
const url = await resolver.read.text([node, 'url'])
const avatar = await resolver.read.text([node, 'avatar'])
const twitter = await resolver.read.text([node, 'com.twitter'])
Source: ~/workspace/source/contracts/resolvers/profiles/TextResolver.sol:28-33
Common Text Record Keys
email - Email address
url - Website URL
avatar - Avatar image URL or NFT identifier
description - Profile description
com.twitter - Twitter handle
com.github - GitHub username
com.discord - Discord username
Setting Records (as Name Owner)
Only the owner of an ENS name (or an authorized operator) can set resolver records.
import { namehash } from 'viem'
const node = namehash('myname.eth')
const resolver = await viem.getContractAt('PublicResolver', resolverAddress)
// Set address
await resolver.write.setAddr([node, ownerAddress])
// Set multi-chain address
await resolver.write.setAddr([node, 60, ethAddressBytes]) // Ethereum
// Set text records
await resolver.write.setText([node, 'email', '[email protected]'])
await resolver.write.setText([node, 'url', 'https://example.com'])
Authorization
The PublicResolver checks authorization before allowing record updates:
function isAuthorised(bytes32 node) internal view returns (bool) {
address owner = ens.owner(node);
if (owner == address(nameWrapper)) {
owner = nameWrapper.ownerOf(uint256(node));
}
return
owner == msg.sender ||
isApprovedForAll(owner, msg.sender) ||
isApprovedFor(owner, node, msg.sender);
}
Source: ~/workspace/source/contracts/resolvers/PublicResolver.sol:112-127
Best Practices
Always validate that a resolver is set before attempting to query it. A name without a resolver will return the zero address.
Never hardcode resolver addresses. Always look up the current resolver from the ENS registry, as name owners can change resolvers at any time.
Error Handling
async function resolveENS(name) {
try {
const node = namehash(name)
const resolverAddress = await ensRegistry.read.resolver([node])
if (resolverAddress === zeroAddress) {
throw new Error('No resolver set for this name')
}
const resolver = await viem.getContractAt('PublicResolver', resolverAddress)
const address = await resolver.read.addr([node])
if (address === zeroAddress) {
throw new Error('No address set for this name')
}
return address
} catch (error) {
console.error('Failed to resolve ENS name:', error)
throw error
}
}
Caching Considerations
ENS records can change at any time. Implement appropriate cache invalidation strategies based on your application’s requirements.
Network Deployments
The ENS Registry is deployed at the same address across networks:
- Mainnet:
0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e
- Sepolia:
0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e
Next Steps