This repo contains a single contract — ChainResolver.sol — that lets clients look up chain IDs and ENS records in one place. Read operations use the ENSIP‑10 extended resolver entrypoint resolve(bytes name, bytes data) (https://docs.ens.domains/ensip/10). Only the contract owner (ideally a multisig) can make changes to the label–chain ID pairs.
- Everything is keyed by labelhash (computed as
labelhash = keccak256(bytes(label)), for example withlabel = "optimism"). This keeps the contract agnostic to the final namespace (cid.eth,on.eth,l2.eth, etc.), so we can change hierarchies later without migrating fields. - One source of truth: Ownership, 7930 chain IDs (ERC‑7930), ENS records, and reverse lookups live in one place.
- ENSIP‑10 Extended Resolver: Once registered, the chain owner (or an authorised operator) can set ENS fields like addresses, contenthash and other text/data fields. Reads go through the extended resolver entrypoint
resolve(bytes name, bytes data)(see ENSIP‑10), so clients can call the standard ENS fields -addr,contenthash, andtext- to pull chain metadata directly from an ENS name likeoptimism.cid.eth. For available fields and examples, see Contract Interfaces. - Clear forward and reverse: Forward returns a chain’s 7930 identifier; reverse maps 7930 bytes back to the chain name.
- The
chainIdbytes follow the 7930 chain identifier format; see 7930 Chain Identifier. - Forward mapping:
labelhash → chainId (bytes) - Reverse mapping:
chainId (bytes) → chainName (string) - Per‑label ENS records:
addr(coinType),contenthash,text, anddata. - Ownership and operator permissions per label owner.
Forward resolution (label → 7930):
The ENS field text(..., "chain-id") (per ENSIP‑5) returns the chain’s 7930 ID as a hex string. The field data(..., "chain-id") returns the raw 7930 bytes (per ENSIP‑TBD‑19). This value is written at registration by the contract owner (e.g., a multisig) and the resolver ignores any user‑set text under that key. To resolve a chain ID:
- DNS‑encode the ENS name (e.g.,
optimism.cid.eth). - Compute the node of the ENS name (e.g., using
ethers:namehash(name)) - Calls:
resolve(name, abi.encodeWithSelector(text(node, "chain-id")))→ returns a hex string.resolve(name, abi.encodeWithSelector(data(node, "chain-id")))→ returns raw bytes.
Reverse resolution (7930 → name):
-
Reverse lookups are performed via the ENS text interface. Pass a key prefixed with
"chain-name:"and suffixed with the 7930 hex usingtext(bytes32 node,string key)(per ENSIP‑5). This follows thechain-name:text key parameter standard (per ENSIP‑TBD‑17). For example:- Text key parameter (string):
"chain-name:<7930-hex>" - Call:
resolve(name, encode(text(node, serviceKey)))
- Text key parameter (string):
Core reads and admin (see src/interfaces/IChainResolver.sol):
function chainId(bytes32 labelhash) external view returns (bytes memory);
function chainName(bytes calldata chainIdBytes) external view returns (string memory);
function register(string calldata chainName, address owner, bytes calldata chainId) external; // owner-only
function setLabelOwner(bytes32 labelhash, address owner) external; // label owner or operator
function setOperator(address operator, bool isOperator) external; // per-owner operatorENS fields available via IExtendedResolver.resolve(name,data):
addr(bytes32 node)→ address (ETH, coin type 60) — per ENSIP‑1addr(bytes32 node,uint256 coinType)→ bytes (raw multi‑coin value) — per ENSIP‑9contenthash(bytes32 node)→ bytes — per ENSIP‑7text(bytes32 node,string key)→ string — per ENSIP‑5 (with special handling for"chain-id"and"chain-name:")data(bytes32 node,string key)→ bytes — per ENSIP‑TBD‑19 (with special handling for"chain-id")
We use the chain identifier variant of ERC‑7930. Examples:
- Optimism (chain 10):
0x000000010001010a00 - Arbitrum (chain 102):
0x000000010001016600
See ERC‑7930: Universal Chain Identifier for the full specification.
forge install
bun installforge test -vvbun run test/ChainResolver.blocksmith.ts- Deploy unified resolver
bun run deploy/DeployChainResolver.ts -- --chain=sepolia- Capture deployed address in
.env:
# Unified ChainResolver deployed address
CHAIN_RESOLVER_ADDRESS=0x...
- Register a chain and set records
bun run deploy/RegisterChainAndSetRecords.ts -- --chain=sepolia- Set records for an existing label
bun run deploy/SetRecords.ts -- --chain=sepolia- Resolve records (addr, contenthash, text, data)
bun run deploy/ResolveRecords.ts -- --chain=sepolia- Resolve chain-id by label
bun run deploy/ResolveByLabel.ts -- --chain=sepolia- Reverse resolve by chain‑id
bun run deploy/ReverseResolveByChainId.ts -- --chain=sepolia- ERC‑7930 — Chain‑aware addresses (used here for chain identifiers)
- CAIP‑2 — Chain IDs (namespace:reference) mapping (
eip155:<id>) - ENSIP‑5 — Text records
- ENSIP‑7 — Contenthash records
- ENSIP‑9 — Multi‑coin addresses
- ENSIP‑10 — Extended resolver
- ENSIP‑11 — Coin types (SLIP‑44 mapping)
- ENSIP‑TBD‑17 — Service Key Parameters (e.g.,
chain-name:) - ENSIP‑TBD‑18 — Global
chain-idtext record - ENSIP‑TBD‑19 —
data()records for chain IDs
