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.
- Search the label and check the yearly price.
- Commit the name. The commitment must be at least 60 seconds old and expires after 24 hours.
- Approve the exact 6-decimal USDC amount, then reveal. The name is registered only after Arc confirms that payment.
- 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 id | 5042 |
|---|---|
| RPC | https://rpc.mainnet.arc.io |
| USDC | 0x3600000000000000000000000000000000000000 |
| Transfer decimals | 6 |
| Gas decimals | 18 |
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
| Status | Meaning | Action |
|---|---|---|
resolved | Active, and a payment address is set. | Send ERC-20 USDC to address. |
unset | Active, and no address is set. | Stop. |
expired | The term ended, or the name is in grace. | Stop. Grace does not resolve. |
unregistered | Nobody holds the label. | Stop. |
reserved | The label is blocked. | Stop. |
unsupported | The label is invalid, or the contracts are not deployed. | Stop. |
Contract reads
A resolve is two reads.
USDRegistrar.inspect(label)returns availability.3is active,4is grace,5is lapsed,2is reserved,1is available, and0is invalid.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.
| Length | Annual price |
|---|---|
| 3 characters | 30 USDC |
| 4 characters | 20 USDC |
| 5 to 32 | 10 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.