Skip to main content

ENS Name Resolution

The Simple Way
var ensService = web3.Eth.GetEnsService();
var address = await ensService.ResolveAddressAsync("nick.eth");
var name = await ensService.ReverseResolveAsync(address);

Resolution routes through the ENSIP-23 Universal Resolver by default, with automatic EIP-3668 CCIP-Read for offchain and wildcard names.

The Ethereum Name Service (ENS) maps human-readable names such as nick.eth to addresses, text records (URL, avatar, social handles), and content hashes. Nethereum resolves names through the ENSIP-23 Universal Resolver — a single onchain contract that finds the right resolver for a name and, when needed, transparently handles offchain (CCIP-Read) resolution.

ENS is one of the built-in contract standards Nethereum ships with. The full API lives in the Nethereum.Contracts package reference, and you can browse other tasks on the What Do You Want to Do? page.

Prerequisites​

dotnet add package Nethereum.Web3
using Nethereum.Web3;
using Nethereum.Contracts.Standards.ENS;

var web3 = new Web3("https://mainnet.infura.io/v3/YOUR-PROJECT-ID");

Basic resolution​

GetEnsService() returns a service that defaults to the Universal Resolver.

var ensService = web3.Eth.GetEnsService();

// Forward resolution: name -> address
var address = await ensService.ResolveAddressAsync("nick.eth");

// Reverse resolution: address -> name
var name = await ensService.ReverseResolveAsync(address);

// Text records (strongly typed keys)
var url = await ensService.ResolveTextAsync("nick.eth", TextDataKey.url);
var avatar = await ensService.ResolveTextAsync("nick.eth", TextDataKey.avatar);

// Content hash (e.g. IPFS/IPNS for decentralised websites)
var contentHash = await ensService.GetContentHashAsync("nick.eth");

TextDataKey covers the common ENSIP-5 keys (url, avatar, description, email, com_twitter, com_github, and more).

Batched resolution with the Universal Resolver​

GetEnsUniversalResolverService() exposes the Universal Resolver directly, so you can batch a name's profile (address + content hash + text records) into a single onchain round-trip via the resolver multicall — the same batching idea as the Multicall guide, applied to ENS.

var universalResolver = web3.Eth.GetEnsUniversalResolverService();

// Address + content hash + selected text records for one name, single round-trip
var record = await universalResolver.ResolveRecordAsync("nick.eth", TextDataKey.url, TextDataKey.avatar);
Console.WriteLine($"{record.Name} -> {record.Address}");
Console.WriteLine($"url: {record.Texts["url"]}");

ResolveRecordAsync returns an EnsRecord:

PropertyTypeNotes
NamestringThe resolved name
AddressstringETH address (coin type 60)
TextsDictionary<string, string>Requested text records that were set
ContentHashbyte[]Raw content hash, if set

Resolve multiple text records for one name, or resolve several names in one batch:

// Multiple text records for one name
var texts = await universalResolver.ResolveTextsAsync("nick.eth", "url", "avatar");

// Multiple names in one batch (each independently CCIP-capable)
var records = await universalResolver.ResolveRecordsAsync(
new[] { "nick.eth", "nethereum.eth" }, TextDataKey.url);

CCIP-Read: offchain names (EIP-3668)​

Names whose records live offchain (e.g. L2 or database-backed resolvers) resolve transparently. The Universal Resolver raises an OffchainLookup revert; Nethereum fetches the data from the resolver's gateway and completes the call — no extra code required.

var universalResolver = web3.Eth.GetEnsUniversalResolverService();
// Offchain (CCIP-Read) name: the Universal Resolver raises OffchainLookup, routed through the CCIP layer.
var offchainAddress = await universalResolver.ResolveAddressAsync("1.offchainexample.eth");
// offchainAddress -> 0x41563129cDbbD0c5D3e1c86cf9563926b243834d

ENSIP-21 local batch gateway (BGOLP)​

When a record for an offchain name batches several sub-lookups, the Universal Resolver bundles them into an ENSIP-21 batch gateway request (x-batch-gateway:true). Nethereum performs that batch locally by default (BGOLP) — decoding the query, running each inner CCIP-Read, and returning the combined result — rather than calling an external batch service.

var universalResolver = web3.Eth.GetEnsUniversalResolverService();

// Resolving a record batches addr + contenthash + text through the resolver multicall. For an
// offchain (CCIP-Read) name the Universal Resolver bundles the sub-lookups into an ENSIP-21 batch
// gateway request (x-batch-gateway:true), which the client performs locally (BGOLP).
var record = await universalResolver.ResolveRecordAsync("1.offchainexample.eth", TextDataKey.url);
// record.Address -> 0x41563129cDbbD0c5D3e1c86cf9563926b243834d

Gateway security (SSRF)​

CCIP-Read gateway URLs come from untrusted resolver contracts, so the fetch is hardened against SSRF (CWE-918) by default. The EnsCCIPService exposes opt-in toggles for local/dev scenarios:

PropertyDefaultEffect when enabled
AllowHttpfalsePermit non-https (plain http) gateway URLs
AllowPrivateDestinationsfalsePermit hosts resolving to private/reserved/loopback/link-local addresses
AllowLocalBatchGatewaytrueHandle the ENSIP-21 batch locally instead of via an external service

By default only https gateways are allowed, hosts resolving into private/reserved ranges are refused (DNS-aware, fail-closed), and HTTP redirect following is disabled. To use a local gateway during development, inject a configured service:

var ccip = new EnsCCIPService { AllowHttp = true, AllowPrivateDestinations = true };
var universalResolver = web3.Eth.GetEnsUniversalResolverService();
// or construct directly: new ENSUniversalResolverService(web3.Eth, universalResolverAddress, ccip);

Legacy registry-walk fallback​

On chains without a Universal Resolver (e.g. custom or local test chains), pass a null Universal Resolver address to fall back to the classic registry-walk resolution:

using Nethereum.Contracts.Standards.ENS;

var legacyEnsService = new ENSService(web3.Eth, universalResolverAddress: null);
var address = await legacyEnsService.ResolveAddressAsync("nick.eth");

Next Steps​