Розробка системи розв'язування ENS-доменів
Розв'язування ENS — це не просто виклик provider.resolveName(). Для production систем потрібно врахувати кешування, підтримку wildcard, CCIP-Read (off-chain resolvers) та коректну обробку multi-chain адрес. Особливо якщо розв'язування відбувається не у браузері, а в backend сервісах або смарт-контрактах.
Повна цепочка розв'язування
Стандартний ENS lookup виглядає так:
- Нормалізація імені за UTS-46 (нижний регістр + IDNA2008)
- Обчислення namehash
- Запит до ENS Registry:
registry.resolver(namehash) - Якщо resolver підтримує wildcard (
supportsInterface(ENSIP-10)) — використовуємо extended resolution - Запит до Resolver:
resolver.addr(namehash, coinType) - Якщо resolver повертає
OffchainLookup(EIP-3668) — виконуємо CCIP-Read - Остаточна адреса
Більшість бібліотек (ethers.js, viem) обробляють кроки 1-5. CCIP-Read підтримується в ethers v6 та viem нативно.
Кастомний сервіс розв'язування
Для високонавантажених додатків варто реалізувати власний сервіс розв'язування з кешем:
import { createPublicClient, http, normalize } from "viem";
import { mainnet } from "viem/chains";
import NodeCache from "node-cache";
class ENSResolutionService {
private client = createPublicClient({ chain: mainnet, transport: http(RPC_URL) });
private cache = new NodeCache({ stdTTL: 300 }); // 5 хвилин TTL
async resolveName(name: string): Promise<string | null> {
const normalized = normalize(name);
const cacheKey = `addr:${normalized}`;
const cached = this.cache.get<string>(cacheKey);
if (cached !== undefined) return cached;
try {
const address = await this.client.getEnsAddress({ name: normalized });
this.cache.set(cacheKey, address ?? null);
return address;
} catch (e) {
return null;
}
}
async lookupAddress(address: `0x${string}`): Promise<string | null> {
const cacheKey = `name:${address.toLowerCase()}`;
const cached = this.cache.get<string>(cacheKey);
if (cached !== undefined) return cached;
const name = await this.client.getEnsName({ address });
this.cache.set(cacheKey, name ?? null);
return name;
}
// Batch розв'язування для списку адрес
async batchLookup(addresses: `0x${string}`[]): Promise<Map<string, string | null>> {
const results = new Map<string, string | null>();
const uncached: `0x${string}`[] = [];
for (const addr of addresses) {
const cached = this.cache.get<string>(`name:${addr.toLowerCase()}`);
if (cached !== undefined) {
results.set(addr, cached);
} else {
uncached.push(addr);
}
}
// Паралельне розв'язування для некешованих
await Promise.allSettled(
uncached.map(async (addr) => {
const name = await this.lookupAddress(addr);
results.set(addr, name);
})
);
return results;
}
}
Multi-chain адреси (ENSIP-9)
ENS зберігає адреси для різних мереж через coinType (SLIP-44):
const COIN_TYPES = { ETH: 60, BTC: 0, SOL: 501, MATIC: 966, ARB: 9001 };
async function getMultiChainAddresses(name: string) {
const resolver = await provider.getResolver(name);
if (!resolver) return null;
return {
eth: await resolver.getAddress(COIN_TYPES.ETH),
btc: await resolver.getAddress(COIN_TYPES.BTC),
sol: await resolver.getAddress(COIN_TYPES.SOL),
};
}
Строк розробки кастомного сервісу розв'язування з кешем та підтримкою multi-chain — 3-5 робочих днів. Інтеграція в існуючий бекенд — додаткові 1-2 дні.







