Arc. Where the receipts settle, and what answers when you read them.
Arc is Circle's EVM layer 1 for stablecoin finance, where USDC is the gas token. Arcveil's registries are deployed to mainnet, and the verifier reads them straight from the browser over plain JSON-RPC.
Network
| Mainnet | Arc, chain ID 5042 |
|---|---|
| Testnet | chain ID 5042002 |
| RPC | https://rpc.mainnet.arc.io |
| Explorer | https://explorer.arc.io |
| Gas | USDC — the native balance, at 18 decimals |
| CORS | The RPC endpoint allows cross-origin requests, which is why the verifier needs no backend |
Deployed
On Arc mainnet. The registry bytecode was checked byte-identical to the local build, so what answers at these addresses is what the test suite ran against.
| MandateRegistry | 0xcd48ede31bd45d8fda65d5d24f8a6a317fd131f5 |
|---|---|
| AnchorRegistry | 0xb2af157f269b31e315099e9da693096833ab8289 |
| ArcveilAccount | 0xb1c0983a7b84f38fbaf5f3af92f0fecaa62ce25d |
| USDC (ERC-20) | 0x3600000000000000000000000000000000000000 |
MandateRegistry
Holds one commitment per account per epoch, and whether it has been revoked. It never holds terms — only their hash.
| Function | What it does |
|---|---|
| register(uint64 epoch, bytes32 commitment) | Publishes a commitment for an epoch. Reverts if that epoch already has one. |
| revoke(uint64 epoch) | Retires it. The epoch stays registered and stays revoked — it can never be reused. |
| mandateOf(address, uint64) view | The record: commitment, epoch, revoked flag. |
| isLive(address, uint64, bytes32) view | One call for the question the verifier actually asks. |
AnchorRegistry
Anchors the budget chain so a bundle that starts mid-sequence can still be placed.
| Function | What it does |
|---|---|
| anchor(bytes32 commitment) | Anchors a budget commitment for the caller. Anchoring the same one twice reverts. |
| isAnchored(address, bytes32) view | Whether a starting commitment is on chain — the linkage check for a receipt with no predecessor. |
| anchoredAt(address, bytes32) view | When it was anchored. |
| head(address) view | The latest commitment anchored by that account. |
The account
| Standard | ERC-4337, EntryPoint v0.7 |
|---|---|
| Quorum | 2 of 3 — device shard, policy co-signer, passkey recovery |
| Gate | execute reverts unless the mandate named in the intent is registered and live |
| Binding | The EIP-712 domain is this account on this chain, and the payload covers the epoch and commitment |
| Relaying | Permissionless — any funded wallet can submit an intent that already carries two signatures |
# Prepare an intent, sign it with a keystore, then relay it.pnpm intent prepare transfer 0x<to> <usdc>pnpm intent send 0x<sig1> 0x<sig2>
Pointing the reader elsewhere
The reader takes the endpoint and the registry addresses, so testnet or a fork is a matter of configuration. Leave an address null and the checks that need it report unknown instead of failing — a fail would read as an accusation the evidence does not support.
import { createRpcChainReader, arc, arcTestnet, ARC_REGISTRIES } from "@arcveildev/sdk"; // Mainnet — the registries below are deployed and answering.createRpcChainReader({ endpoint: arc.rpcUrls.default.http[0], chainId: arc.id, ...ARC_REGISTRIES[arc.id],}); // Testnet — nothing of ours is deployed there, so pass null and let those// checks report unknown instead of pretending.createRpcChainReader({ endpoint: arcTestnet.rpcUrls.default.http[0], chainId: arcTestnet.id, mandateRegistry: null, anchorRegistry: null,});