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.

PropertyValue
NetworkArc Testnet (Chain ID: 5042002)
Name format3–32 chars, lowercase a-z, 0-9, hyphens
Registration fee5 USDC/year (standard), 50 USDC/year (≤4 chars)
Grace period30 days after expiry
Gas tokenUSDC

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-react

Step 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

MethodReturnsDescription
resolve(name)string | nullForward lookup: name → address
resolveWithReason(name){address, reason}Resolve with failure reason
reverseLookup(address)string | nullReverse lookup: address → name.arc
isAvailable(name)booleanCheck if name can be registered
getNameInfo(name)NameInfoFull record: owner, expiry, status
quotePrice(name, years?)bigintFee in USDC (6 decimals)
resolveMany(names[])ArrayBatch resolve via multicall
getQRCodeDataUrl(name)stringQR code as data URL

Write Methods

All write methods require a signer and return the transaction hash.

MethodDescription
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

HookUse CaseReturns
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.

GET
/resolve/:name

Resolve 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}
GET
/reverse/:address

Reverse lookup an address to its primary .arc name.

curl http://localhost:8787/reverse/0x858f3232E7d6702F20c4D3FEAB987A405D225f4E
# {"address":"0x858f...","name":"david.arc","resolved":true}
GET
/available/:name

Check if a name is available for registration.

curl http://localhost:8787/available/myname
# {"name":"myname.arc","available":true}
GET
/profile/:name

Full profile with QR code URL.

curl http://localhost:8787/profile/david
# {"name":"david.arc","address":"0x858f...","expiry":1745441232,"qrUrl":"...","profileUrl":"..."}
GET
/qr/:name

QR code PNG image. Params: size (100–1000), format (png|svg).

GET
/healthz

Health 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 ANSSolution With ANS
Agents pass around opaque 0x... addressesAgents reference alice.arc — semantic and verifiable
Transaction logs are unreadableLogs show paid supplier.arc 500 USDC — auditable
Agent config requires hardcoded addressesAgents 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 CaseIntegration MethodExample
Wallet / DEXSDK resolve()Send tokens to alice.arc instead of 0x...
NFT MarketplaceSDK reverseLookup()Show david.arc next to creator addresses
Payment GatewayREST API /resolve/:nameBackend resolves names before processing payments
On-chain ProtocolSolidity interfaceAccept .arc names directly in smart contracts
Profile PagesREST API /profile/:nameEmbed user profiles with QR codes
Block ExplorerSDK reverseLookup()Display names instead of raw addresses
AI AgentSDK resolve + registerAgents discover and pay each other by name
CSV Batch PayoutsSDK 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.

RuleDetail
Length3–32 characters
CharactersLowercase a-z, digits 0-9, hyphens -
No leading/trailing hyphens-name.arc is invalid
Auto-normalizationUppercase converted, .arc suffix stripped
Reserved namesadmin, arc, treasury, null, void
Pricing5 USDC/year (standard), 50 USDC/year (≤4 chars)

Contract Addresses

Arc Testnet (Chain ID: 5042002)

ContractAddress
ARCNameRegistry0xf5e0E328119D16c75Fb4a001282a3a7b733EF6db
USDC Token0x3600000000000000000000000000000000000000

Network Details

PropertyValue
Chain ID5042002
RPC URLhttps://rpc.testnet.arc.network
Block Explorerhttps://testnet.arcscan.app
Gas TokenUSDC

Error Reference

Contract Errors

ErrorCauseResolution
InvalidLabel()Name too short/long or invalid charsUse 3-32 chars, a-z, 0-9, hyphens
NameNotAvailable()Name registered, not past graceWait for expiry + 30 days
NotLabelOwner()Caller doesn't own the nameConnect correct wallet
NameExpired()Past expiry + grace periodRe-register instead of renew
NameIsReserved()Name is reservedChoose a different name
CommitmentMissing()No prior commitmentCall submitCommitment first
CommitmentExpired()Commitment older than 1 daySubmit a new commitment

Frontend Errors

SymptomCauseFix
"No wallet found"No MetaMask installedInstall MetaMask extension
"Wrong network" badgeNot on Arc TestnetClick "Switch to Arc"
"You need X USDC"Insufficient balanceGet testnet USDC from Circle faucet
"Transaction rejected"User clicked RejectTry again, approve in wallet
"Network error"RPC/internet issueCheck 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
EntityTrust LevelPowers
Contract OwnerFully trustedPause, set fees, reserve names, toggle commit-reveal
UsersUntrustedOnly manage own names, must pay fees
API ServerRead-onlyCannot modify on-chain state
FrontendUntrusted clientAll writes require wallet signature