Arcveil
Private bridge

Bridge. One CCTP burn in, one unlinkable withdrawal out.

Circle's USDC Bridge publishes everything about a crossing: the sender, the amount and the destination. This bridges into a shielded pool on Arc instead. The deposit stays public and every withdrawal stays public; what is hidden is which deposit paid which withdrawal — and only as well as the pool is busy.

What is hidden

One line of the table below is the product. The rest is the price, and reading them in this order is the only way the claim means anything.

What the bridge publishes and what it does not.
FactWho sees it
The burnPublic on the source chain: your address, the amount, the time
The depositPublic on Arc: a commitment joining the pool
The pool balancePublic on Arc
Every withdrawalPublic on Arc: recipient, amount, time
Deposit → withdrawalPrivate. This is the only thing the pool hides.
Your note's secretsPrivate to your device, derived from one signature
Recipient, amount and IP in flightSeen by the relayer you choose

CCTP on Arc

Circle's CCTP V2 is live on Arc mainnet and testnet. Every address below was confirmed by reading the chain, not by copying a document.

CCTP V2 as deployed on Arc.
Arc's CCTP domain26 — the same number on mainnet and testnet
TokenMessengerV20x28b5a0e9C621a5BadaA536219b3a228C8168cf5d
MessageTransmitterV20x81D40F21F12A8F0E3252Bccb954D722d4c464B64
TokenMinterV20xfd78EE919681417d192449715b2594ab58f5D002
Testnet set0x8FE6B999…2DAA / 0xE737e5cE…E275 / 0xb43db544…F192
Attestationiris-api.circle.com, and iris-api-sandbox.circle.com for testnet
FinalityStandard (threshold 2000) is free; fast (1000) costs basis points

Depositing

Two transactions on the source chain, built locally and sent from your own wallet.

deposit.ts
import { approveCall, burnCall, deriveNote, routeFor } from "@arcveil/bridge"; const route = routeFor(8453);            // Baseconst note = deriveNote(signature, 0);   // one signature derives every note // Two transactions from the source chain. Nothing is sent for you.approveCall({ route, amount: 25_000_000n });burnCall({  route,  amount: 25_000_000n,  gateway: VEIL_GATEWAY,  hook: { precommitment: note.precommitment, refund: yourArcAddress },});

VeilGateway

One contract, no owner, no pause, no sweep. It holds funds only inside a single transaction.

The gateway's whole surface.
FunctionWhat it does
relay(bytes message, bytes attestation)Mints an attested CCTP transfer and deposits it under the precommitment the hook carried. Permissionless.
ragequit(RagequitProof proof)Pulls a bridged deposit back out publicly, to the refund address the burn named. The proof is the authorisation; nobody's permission is needed.
refundOf(uint256 commitment) viewWhere that deposit's funds go if it is ever ragequit.
Deployed addresses. Null until DeployVeil.s.sol has run.
Entrypointnot deployed
PrivacyPoolnot deployed
VeilGatewaynot deployed
Relayernot deployed
Scopenot deployed

Withdrawing

The proof is built where the secrets are and nowhere else. What reaches the relayer is the proof and eight public numbers, none of which names a deposit.

withdraw.ts
import { contextFor, proveWithdrawal, withdrawalFor, witnessFor } from "@arcveil/bridge"; // The recipient and the fee are hashed into the proof, so the relayer that// submits it cannot redirect the money or pay itself more.const withdrawal = withdrawalFor(ENTRYPOINT, { recipient, feeRecipient, relayFeeBPS: 25n }); const proof = await proveWithdrawal(  {    note, change, label, existingValue, withdrawnValue,    stateWitness: witnessFor(commitments, myCommitment),    aspWitness: witnessFor(labels, label),    context: contextFor(withdrawal, SCOPE),  },  artifacts,   // withdraw.wasm and withdraw.zkey, fetched by `pnpm veil:artifacts`  groth16,     // snarkjs, loaded only when someone actually withdraws);
shell
# What the relayer needs before you build the proof.curl https://arcveil-relay.workers.dev/quote # The proof and eight public numbers. Nothing that names a deposit.curl -X POST https://arcveil-relay.workers.dev/withdraw \  -H 'content-type: application/json' \  -d '{"recipient":"0x…","relayFeeBPS":"25","proof":{…}}'

The association set

What is audited, and what is not

The pool contracts and the circuits are Privacy Pools by 0xbow, copied unmodified at commit c312dcd5 and audited by Oxorio and Auditware. VeilGateway, the relayer and the ASP postman are ours, and none of them has been audited by anyone. contracts/privacy/VERIFY.md is how to check the first claim without trusting us.