Docs

Integrate .usd

No app resolves a .usd name yet. A wallet, a browser, and ENS leave the name as text. This page is how an app reads the registry and finds an address.

Overview

Send USDC only when resolveUsdName returns resolved. Every other status means there is no payment address. Do not fall back to the NFT owner, the token image, a text record, or an address saved from an earlier session.

The helpers are in sdk/ in this repository. The package is not on npm. Copy that folder, or call the same contract reads with viem or ethers. alice and alice.usd are the same input to the helper. The helper lowercases. The contract does not. This website, usdomains.xyz, is not a .usd name.

No wallet, browser, or ENS integration resolves .usd. An app has to read this registry. If the status is anything other than resolved, there is no address to guess.

Registration

A public name is 3 to 32 characters: lowercase letters, digits, and single hyphens between characters.

  1. Search the label and check the yearly price.
  2. Commit the name. The commitment must be at least 60 seconds old and expires after 24 hours.
  3. Approve the exact 6-decimal USDC amount, then reveal. The name is registered only after Arc confirms that payment.
  4. Set the Arc payment address on the NFT.

A term is 1 to 10 years of 365 days. One- and two-character names are not for sale. If a price change is scheduled, it is shown before you commit, and reveal charges the price in effect at that moment. Native msg.value is rejected. The commitment binds the label, recipient, years, resolver, payer, secret, chain id, and registrar. The payer who reveals must be the payer in the commitment. The reserved list blocks exact labels. It does not catch every lookalike.

Expiry

Grace starts at 30 days and can be scheduled between 7 and 90 days. During grace the name does not resolve. The current registrant can still renew. After grace, someone else can register the same label, and the old NFT is burned.

The NFT

The ERC-721 lets the holder set the Arc payment address, renew, transfer, or choose a primary name. The image describes the label and phase. It is not proof of ownership or expiry. After a name expires, or after someone else registers it, a wallet may still show the old image. Read the registrar before you send. No administrator can take a name that is still active or in grace.

Network

Use Arc mainnet. Name payments use the ERC-20 interface. Gas is a separate native balance.

Chain id5042
RPChttps://rpc.mainnet.arc.io
USDC0x3600000000000000000000000000000000000000
Transfer decimals6
Gas decimals18

Do not put a name payment or a registration fee in msg.value. The two units differ by 1e12. Contract addresses are in deployments/arc-mainnet.json. Until deployed is true, there is no registrar to call.

Resolve

This is the send path. Fill contracts from the deployment file after it is deployed.

resolve.ts

import { createPublicClient, http, parseUnits } from "viem";
import { resolveUsdName, type UsdContracts } from "@usd-names/sdk";

const contracts: UsdContracts = {
  chainId: 5042,
  deployed: false, // set from deployments/arc-mainnet.json
  registrar: null,
  resolver: null,
  name: null,
  reverse: null,
  usdc: "0x3600000000000000000000000000000000000000",
  startBlock: null,
};

const client = createPublicClient({
  chain: {
    id: 5042,
    name: "Arc",
    nativeCurrency: { name: "USDC", symbol: "USDC", decimals: 18 },
    rpcUrls: { default: { http: ["https://rpc.mainnet.arc.io"] } },
  },
  transport: http("https://rpc.mainnet.arc.io"),
});

const result = await resolveUsdName(client, contracts, "alice.usd");
if (result.status !== "resolved") {
  throw new Error(`Not sending. alice.usd is ${result.status}.`);
}

const destination = result.address;
const amount = parseUnits("1", 6);

Status values

StatusMeaningAction
resolvedActive, and a payment address is set.Send ERC-20 USDC to address.
unsetActive, and no address is set.Stop.
expiredThe term ended, or the name is in grace.Stop. Grace does not resolve.
unregisteredNobody holds the label.Stop.
reservedThe label is blocked.Stop.
unsupportedThe label is invalid, or the contracts are not deployed.Stop.

Contract reads

A resolve is two reads.

  1. USDRegistrar.inspect(label) returns availability. 3 is active, 4 is grace, 5 is lapsed, 2 is reserved, 1 is available, and 0 is invalid.
  2. USDResolver.addrIfActive(label) returns the payment address. It returns the zero address unless the name is active.

USDResolver.addr(tokenId) still returns a stored record after expiry. Do not pay that address. A direct call with uppercase, emoji, or a bad hyphen reverts.

Primary name

reverseLookupUsdName returns verified only when that wallet’s primary name still forward-resolves to the same wallet. Anything else is none. Do not display a primary name from an event log or from the NFT alone.

Price

getPrice(client, contracts, "alice.usd", years) returns a 6-decimal USDC amount. A term is 1 to 10 years.

LengthAnnual price
3 characters30 USDC
4 characters20 USDC
5 to 3210 USDC

Registration details, the 60-second commit, grace, and what the NFT controls are in the registration, expiry, and NFT sections. sdk/examples/integrate.ts refuses every status other than resolved.