Skip to content
Arc Testnet — names have no monetary value yet
ArcNS / Verified records

Verified records

What the verified flag on an ArcNS address record means, how an owner proves control of an address for a coin type, and the arcns:v1 challenge format.

Every address record ArcNS returns carries a verification flag. This page says exactly what it means, so a wallet or explorer can decide what to show and what to warn about.

What verified means

ValueMeaningWhat to do
verifiedThe owner proved control of this address for this coin type on chain (verified(node, coinType) on the resolver).Show it plainly.
unverifiedA record exists but ownership was never proved for it. It may still be correct; nobody has shown that it is.Show a warning and make the user look twice before sending.

The flag is per coin type. alice.arc can have a verified Arc record and an unverified Bitcoin record at the same time. In the SDK it is Resolved.verification and AddressRecord.verification; in the API it is the boolean addr.verified.

Two things the flag never does:

  • It never turns a controller into a payment address. controller (the SDK) or owner (the API) is who holds the name; the resolvers never fall back to it, verified or not.
  • It never survives an ownership change. Records are keyed by the name's epoch, which bumps on every transfer, so a proof made by a previous owner is unreachable by construction.

ArcNS resolves .arc and .circle names from its own single, authoritative registry only — there is no other pinned registry it falls back to or compares against. verified is an extension of ArcNS's own resolver (verified(node, coinType)); a node whose resolver doesn't implement that extension — a plain legacy resolver exposing only addr(bytes32) — always reads as unverified, never as confirmed.

How a record becomes verified

Not yet on npm

@arcns/resolve is the real, currently-shipping SDK surface below, but it has not been published to the npm registry yet — see the Quickstart for why and what to use today for read-only lookups. The transaction builders on this page (writes) have no HTTP API equivalent; treat the code below as a preview of the shape you'll get once the package publishes.

For the Arc address itself, the simplest proof is a transaction from that address: buildVerifyAddrSelf proves msg.sender.

const call = buildVerifyAddrSelf({ deployment, name: "alice.arc" });
await wallet.sendTransaction({ to: call.to, data: call.data, value: call.value }); // proves the Arc address (msg.sender)

For another chain's address the owner signs a challenge with the wallet that holds that address, then submits the signature on Arc. resolver.challenge() reads the name's current on-chain versionOf(node) and hands back both the raw challenge string and the EIP-712 typed data to sign:

import { buildVerifyAddr } from "@arcns/resolve";

const { challenge, typedData, nonce } = await resolver.challenge("alice.arc", "ETH");
// arcns:v1:rec:0x9a7a…7bae:60:0x0000…0003:5042002:0x2841…6287a:9f3c1a2b…

// EVM coin types (ETH, ARC/ERC-1271) sign the EIP-712 `RecordProof` digest, not the raw string:
const signature = await wallet.signTypedData(typedData);
const call = buildVerifyAddr({ deployment, name: "alice.arc", chain: "ETH", nonce, proof: { signature } });
await wallet.sendTransaction({ to: call.to, data: call.data, value: call.value });

The challenge format

arcns:v1:rec:<node>:<coinType>:<version>:<chainId>:<resolver>:<nonce>
FieldExampleMeaning
arcns:v1scheme and version
purposereca record proof, verified on chain (ctl is a separate control proof, verified off chain)
node0x9a7a…7baethe ENS / handle node the record hangs off (parseName(...).node); encodes the namespace and label, so no separate namespace/label field exists
coin type60ENSIP-9 / ENSIP-11 coin type being proved (BTC 0, ETH 60, SOL 501, ARC 2152525650)
version0x0000…0003ArcNSResolver.versionOf(node); changes on every ownership change, so the proof dies with it
chain id5042002Arc testnet
resolver0x2841…6287athe resolver that verifies the proof (ArcNSResolver)
nonce9f3c1a2b…from generateNonce(); one use

Binding the node, coin type, version, chain id and resolver into the signed string is what stops a signature from being replayed for a different name, a different chain, or the same name after it changes hands.

Signature forms by chain:

  • ETH (and other EVM coin types): the EIP-712 RecordProof digest (recordProofTypedData), not the raw challenge string — verified on chain by verifyAddrSecp256k1.
  • BTC: a BIP-137 signature over the raw challenge string, plus the 64-byte uncompressed public key.
  • SOL / X1 (ed25519): a signature over the raw challenge string, followed by the public key.

Local pre-checks (verifyEthTypedProofLocal, verifyControlProofLocal) let a wallet refuse a doomed transaction before it is sent. The resolver verifies only the EIP-712 typed-data form for EVM record proofs; an earlier EIP-191 form (verifyEthProofLocal) was removed before the SDK's first published version.

Showing it

  • Wallets: verified next to the address, nothing more; unverified as a warning that the user must pass through before "Send" is enabled. See Wallets.
  • Explorers: show the flag per record on the name page and never render an unverified record as if it were confirmed. See Explorers.
  • Agents and machine consumers: treat verification !== "verified" as "do not pay without a human".

Questions

Questions: [email protected].

Browse the docs