ARC Name Service
Human-readable .arc names for wallets, AI agents, and payment apps on the Arc Network.
Identity Layer
Replace 0x858f...5f4E with david.arc. One name for payments, profiles, and discovery.
USDC Native
All fees paid in USDC. Gas is USDC on Arc. No ETH, no bridging, no wrapped tokens.
Agent Ready
AI agents register bot-agent.arc names, discover services, and pay each other autonomously.
| Property | Value |
|---|---|
| Network | Arc Testnet (Chain ID: 5042002) |
| Name format | 3–32 chars, lowercase a-z, 0-9, hyphens |
| Registration fee | 5 USDC/year (standard), 50 USDC/year (≤4 chars) |
| Grace period | 30 days after expiry |
| Gas token | USDC |
Capabilities
Name Registration
Claim a .arc name linked to your wallet. Auto-detects commit-reveal mode for front-running protection.
Forward Resolution
Convert .arc names to wallet addresses. david.arc → 0x858f...
Reverse Resolution
Convert wallet addresses back to .arc names. Show names in navbars, profiles, and tx logs.
Name Management
Renew, transfer, update resolved address, set primary name, or release.
Payment QR Codes
Generate scannable QR codes linked to .arc profiles for instant payment.
Commit-Reveal Protection
Two-step registration prevents mempool front-running of name claims.
Name Types
Human identities (alice.arc), AI agents (bot-agent.arc), and payment apps (pay-usdc.arc).
Batch Resolution
Resolve many names in a single multicall for CSV payouts and batch operations.
Getting Started
Integrate .arc name resolution into any dApp in under 5 minutes. Install from npm, instantiate, and resolve.
Step 1 — Install
# Core SDK (required)
npm install @arcnames/sdk
# React hooks (optional, for React/Next.js apps)
npm install @arcnames/sdk-reactStep 2 — Instantiate
Create a single SDK instance. All read methods cache automatically.
import { ARCNames } from "@arcnames/sdk";
const ans = new ARCNames({
rpcUrl: "https://rpc.testnet.arc.network",
registryAddress: "0xf5e0E328119D16c75Fb4a001282a3a7b733EF6db",
cacheTimeout: 60000, // optional, default 60s
});Step 3 — Resolve
Convert any .arc name to an address instantly:
const address = await ans.resolve("david");
if (!address) throw new Error("Name not found");
// Use the resolved address in any transaction
await sendUSDC(address, amount);Pattern — Show .arc names in your UI (React)
Replace raw 0x addresses with readable names in navbars, profiles, and transaction lists.
import { useANSReverse } from "@arcnames/sdk-react";
function Navbar({ walletAddress }) {
const { arcName, isLoading } = useANSReverse(walletAddress, {
rpcUrl: "https://rpc.testnet.arc.network",
registryAddress: "0xf5e0E328119D16c75Fb4a001282a3a7b733EF6db",
});
return (
<span>
{isLoading ? "..." : arcName ?? `${walletAddress.slice(0, 6)}...${walletAddress.slice(-4)}`}
</span>
);
}Pattern — Address input with live resolution
Let users type alice.arc instead of 0x... in any send/receive form.
import { useANSResolve } from "@arcnames/sdk-react";
function RecipientInput({ onResolved }) {
const [input, setInput] = useState("");
const { address, arcName, isResolving, reason } = useANSResolve(input, {
rpcUrl: "https://rpc.testnet.arc.network",
registryAddress: "0xf5e0E328119D16c75Fb4a001282a3a7b733EF6db",
debounceMs: 400,
});
if (address) onResolved(address);
return (
<div>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="0x... or name.arc"
/>
{isResolving && <span>Resolving...</span>}
{arcName && address && <span>{arcName} → {address.slice(0, 10)}...</span>}
{reason === "not_registered" && <span>Name not found</span>}
</div>
);
}Pattern — Batch resolve for payouts
Resolve hundreds of names in a single multicall. Perfect for payroll, airdrops, and treasury ops.
const recipients = ["alice", "bob", "charlie", "david"];
const results = await ans.resolveMany(recipients);
// [{ name: "alice.arc", address: "0x..." }, { name: "bob.arc", address: null }, ...]
const valid = results.filter((r) => r.address !== null);
const failed = results.filter((r) => r.address === null);Pattern — Check availability before registering
Pre-flight check before showing a registration form.
const available = await ans.isAvailable("myname");
if (available) {
const price = await ans.quotePrice("myname", 1); // 1 year in USDC (6 decimals)
console.log(`Register for ${price} USDC`);
}Pattern — Register a name
Requires an ethers.js signer for on-chain transactions. Auto-detects commit-reveal mode.
import { BrowserProvider } from "ethers";
const provider = new BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const ansWrite = new ARCNames({
rpcUrl: "https://rpc.testnet.arc.network",
registryAddress: "0xf5e0E328119D16c75Fb4a001282a3a7b733EF6db",
signer,
});
const txHash = await ansWrite.register("myname");
console.log(`Registered: https://testnet.arcscan.app/tx/${txHash}`);TypeScript SDK — @arcnames/sdk
The official SDK for JavaScript/TypeScript. Works in Node.js, browsers, and serverless functions.
Setup
import { ARCNames, normalizeName } from "@arcnames/sdk";
// Read-only (no signer needed)
const ans = new ARCNames({
rpcUrl: "https://rpc.testnet.arc.network",
registryAddress: "0xf5e0E328119D16c75Fb4a001282a3a7b733EF6db",
cacheTimeout: 60000, // optional, default 60s
});
// Read + Write (requires ethers.js signer)
import { BrowserProvider } from "ethers";
const provider = new BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const ansWrite = new ARCNames({
rpcUrl: "https://rpc.testnet.arc.network",
registryAddress: "0xf5e0E328119D16c75Fb4a001282a3a7b733EF6db",
signer,
});Read Methods
| Method | Returns | Description |
|---|---|---|
resolve(name) | string | null | Forward lookup: name → address |
resolveWithReason(name) | {address, reason} | Resolve with failure reason |
reverseLookup(address) | string | null | Reverse lookup: address → name.arc |
isAvailable(name) | boolean | Check if name can be registered |
getNameInfo(name) | NameInfo | Full record: owner, expiry, status |
quotePrice(name, years?) | bigint | Fee in USDC (6 decimals) |
resolveMany(names[]) | Array | Batch resolve via multicall |
getQRCodeDataUrl(name) | string | QR code as data URL |
Write Methods
All write methods require a signer and return the transaction hash.
| Method | Description |
|---|---|
register(name, address?) | Register a name. Auto-detects commit-reveal mode. |
renew(name) | Extend registration by 1 year |
transferName(name, newOwner) | Transfer ownership |
updateResolvedAddress(name, addr) | Change where the name points |
setPrimaryName(name) | Set as reverse-lookup identity |
releaseName(name) | Permanently give up a name |
React Hooks — @arcnames/sdk-react
Drop-in hooks for React/Next.js apps. Built-in debounce, caching, cleanup on unmount.
ANSProvider (recommended)
Wrap your app once, and all hooks share the same SDK instance and cache:
import { ANSProvider } from "@arcnames/sdk-react";
function App() {
return (
<ANSProvider config={{
rpcUrl: "https://rpc.testnet.arc.network",
registryAddress: "0xf5e0E328119D16c75Fb4a001282a3a7b733EF6db",
}}>
<YourApp />
</ANSProvider>
);
}
// Then in any child — no config needed:
const { address } = useANSResolve(input);
const { arcName } = useANSReverse(walletAddress);
const { available } = useANSAvailability(nameInput);Available Hooks
| Hook | Use Case | Returns |
|---|---|---|
useANSResolve(input) | Live name → address in forms | {address, arcName, isResolving, reason} |
useANSReverse(address) | Display name in navbars/profiles | {arcName, isLoading} |
useANSAvailability(name) | Registration availability check | {available, isChecking} |
REST API
Use the REST API for backend integrations, mobile apps, Python/Go services, or quick prototyping.
/resolve/:nameResolve a name to its address and full record.
curl http://localhost:8787/resolve/david
# {"name":"david.arc","address":"0x858f...","owner":"0x858f...","expiry":1745441232,"resolved":true}/reverse/:addressReverse lookup an address to its primary .arc name.
curl http://localhost:8787/reverse/0x858f3232E7d6702F20c4D3FEAB987A405D225f4E
# {"address":"0x858f...","name":"david.arc","resolved":true}/available/:nameCheck if a name is available for registration.
curl http://localhost:8787/available/myname
# {"name":"myname.arc","available":true}/profile/:nameFull profile with QR code URL.
curl http://localhost:8787/profile/david
# {"name":"david.arc","address":"0x858f...","expiry":1745441232,"qrUrl":"...","profileUrl":"..."}/qr/:nameQR code PNG image. Params: size (100–1000), format (png|svg).
/healthzHealth check. Returns API status and chain info.
curl http://localhost:8787/healthz
# {"ok":true,"network":"arcTestnet","chainId":5042002,"timestamp":"..."}Solidity Integration
Call the ANS registry directly from your smart contracts.
Interface
interface IANSRegistry {
function resolve(string calldata rawLabel) external view returns (address);
function getRecord(string calldata rawLabel) external view returns (
address owner, address resolvedAddress, uint64 expiry, bool expired, bool reserved
);
function isAvailable(string calldata rawLabel) external view returns (bool);
function reverseLookup(address wallet) external view returns (string memory);
}Example: Accept .arc names in a payment contract
contract PaymentRouter {
IANSRegistry public immutable ans;
constructor(address ansRegistry) {
ans = IANSRegistry(ansRegistry);
}
function payByName(string calldata name) external payable {
address recipient = ans.resolve(name);
require(recipient != address(0), "Name not registered");
(bool ok, ) = recipient.call{value: msg.value}("");
require(ok, "Transfer failed");
}
}Frontend Widget Pattern
Add .arc name resolution to any address input field in your dApp:
import { ARCNames } from "@arcnames/sdk";
import { useState, useMemo } from "react";
function AddressInput({ onResolved }) {
const [input, setInput] = useState("");
const [resolved, setResolved] = useState(null);
const ans = useMemo(() => new ARCNames({
rpcUrl: "https://rpc.testnet.arc.network",
registryAddress: "0xf5e0E328119D16c75Fb4a001282a3a7b733EF6db",
}), []);
async function handleChange(value) {
setInput(value);
if (value.endsWith(".arc") || (!value.startsWith("0x") && value.length >= 3)) {
try {
const addr = await ans.resolve(value.replace(/\.arc$/, ""));
setResolved(addr);
if (addr) onResolved(addr);
} catch { setResolved(null); }
} else if (value.startsWith("0x")) {
setResolved(value);
onResolved(value);
}
}
return (
<div>
<input value={input} onChange={(e) => handleChange(e.target.value)} placeholder="0x... or name.arc" />
{resolved && <small>Resolved: {resolved}</small>}
</div>
);
}For AI Agents
AI agents need semantic identity to transact autonomously. .arc names solve this — an agent understands alice.arc the same way it understands alice@email.com.
Agent Identity Layer
Every agent gets a .arc name as its onchain identity: payroll-agent.arc, rebalancer-agent.arc. Agents register names, publish capabilities, and discover each other by name.
Agent-to-Agent Payments
// Without ANS — brittle, opaque
agent.send("0x858f3232...", "100 USDC")
// With ANS — semantic, auditable
agent.send("supplier-agent.arc", "100 USDC")Why Agents Need .arc Names
| Problem Without ANS | Solution With ANS |
|---|---|
| Agents pass around opaque 0x... addresses | Agents reference alice.arc — semantic and verifiable |
| Transaction logs are unreadable | Logs show paid supplier.arc 500 USDC — auditable |
| Agent config requires hardcoded addresses | Agents discover each other by .arc name |
| LLMs can hallucinate hex addresses | .arc names are short, validatable strings |
| No agent identity standard | .arc names become the agent identity layer |
The Killer Demo: Natural Language → Onchain Action
User: "Pay the design team 500 USDC each"
Agent:
→ Looks up "design-team.arc"
→ Resolves alice.arc, bob.arc, charlie.arc
→ Executes 3 USDC transfers on Arc
→ Reports: "Paid 1500 USDC to 3 members of design-team.arc"Use Cases
| Use Case | Integration Method | Example |
|---|---|---|
| Wallet / DEX | SDK resolve() | Send tokens to alice.arc instead of 0x... |
| NFT Marketplace | SDK reverseLookup() | Show david.arc next to creator addresses |
| Payment Gateway | REST API /resolve/:name | Backend resolves names before processing payments |
| On-chain Protocol | Solidity interface | Accept .arc names directly in smart contracts |
| Profile Pages | REST API /profile/:name | Embed user profiles with QR codes |
| Block Explorer | SDK reverseLookup() | Display names instead of raw addresses |
| AI Agent | SDK resolve + register | Agents discover and pay each other by name |
| CSV Batch Payouts | SDK resolveMany() | Resolve hundreds of names in one multicall |
Naming Rules
Human
alice.arc
Personal identity. No suffix required.
AI Agent
bot-agent.arc
Must end with -agent suffix.
Payment App
pay-usdc.arc
Must end with -usdc suffix.
| Rule | Detail |
|---|---|
| Length | 3–32 characters |
| Characters | Lowercase a-z, digits 0-9, hyphens - |
| No leading/trailing hyphens | -name.arc is invalid |
| Auto-normalization | Uppercase converted, .arc suffix stripped |
| Reserved names | admin, arc, treasury, null, void |
| Pricing | 5 USDC/year (standard), 50 USDC/year (≤4 chars) |
Contract Addresses
Arc Testnet (Chain ID: 5042002)
| Contract | Address |
|---|---|
| ARCNameRegistry | 0xf5e0E328119D16c75Fb4a001282a3a7b733EF6db |
| USDC Token | 0x3600000000000000000000000000000000000000 |
Network Details
| Property | Value |
|---|---|
| Chain ID | 5042002 |
| RPC URL | https://rpc.testnet.arc.network |
| Block Explorer | https://testnet.arcscan.app |
| Gas Token | USDC |
Error Reference
Contract Errors
| Error | Cause | Resolution |
|---|---|---|
InvalidLabel() | Name too short/long or invalid chars | Use 3-32 chars, a-z, 0-9, hyphens |
NameNotAvailable() | Name registered, not past grace | Wait for expiry + 30 days |
NotLabelOwner() | Caller doesn't own the name | Connect correct wallet |
NameExpired() | Past expiry + grace period | Re-register instead of renew |
NameIsReserved() | Name is reserved | Choose a different name |
CommitmentMissing() | No prior commitment | Call submitCommitment first |
CommitmentExpired() | Commitment older than 1 day | Submit a new commitment |
Frontend Errors
| Symptom | Cause | Fix |
|---|---|---|
| "No wallet found" | No MetaMask installed | Install MetaMask extension |
| "Wrong network" badge | Not on Arc Testnet | Click "Switch to Arc" |
| "You need X USDC" | Insufficient balance | Get testnet USDC from Circle faucet |
| "Transaction rejected" | User clicked Reject | Try again, approve in wallet |
| "Network error" | RPC/internet issue | Check connection, retry |
Security Model
On-Chain Protections
- ReentrancyGuard on all payment functions
- SafeERC20 for all token transfers
- Ownable2Step for ownership transfers
- Pausable emergency circuit breaker
- Commit-reveal prevents front-running
- Input normalization prevents duplicates
Off-Chain Security
- No private keys in frontend code
- API is read-only — cannot modify state
- Rate limiting: 100 req/min per IP
- NEXT_PUBLIC_ prefix for safe env vars
- Wallet events properly cleaned on unmount
- encodeURIComponent on all user input
| Entity | Trust Level | Powers |
|---|---|---|
| Contract Owner | Fully trusted | Pause, set fees, reserve names, toggle commit-reveal |
| Users | Untrusted | Only manage own names, must pay fees |
| API Server | Read-only | Cannot modify on-chain state |
| Frontend | Untrusted client | All writes require wallet signature |