Bridge. One CCTP burn in, one unlinkable withdrawal out.
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.
| Fact | Who sees it |
|---|---|
| The burn | Public on the source chain: your address, the amount, the time |
| The deposit | Public on Arc: a commitment joining the pool |
| The pool balance | Public on Arc |
| Every withdrawal | Public on Arc: recipient, amount, time |
| Deposit → withdrawal | Private. This is the only thing the pool hides. |
| Your note's secrets | Private to your device, derived from one signature |
| Recipient, amount and IP in flight | Seen 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.
| Arc's CCTP domain | 26 — the same number on mainnet and testnet |
|---|---|
| TokenMessengerV2 | 0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d |
| MessageTransmitterV2 | 0x81D40F21F12A8F0E3252Bccb954D722d4c464B64 |
| TokenMinterV2 | 0xfd78EE919681417d192449715b2594ab58f5D002 |
| Testnet set | 0x8FE6B999…2DAA / 0xE737e5cE…E275 / 0xb43db544…F192 |
| Attestation | iris-api.circle.com, and iris-api-sandbox.circle.com for testnet |
| Finality | Standard (threshold 2000) is free; fast (1000) costs basis points |
Depositing
Two transactions on the source chain, built locally and sent from your own wallet.
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.
| Function | What 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) view | Where that deposit's funds go if it is ever ragequit. |
| Entrypoint | not deployed |
|---|---|
| PrivacyPool | not deployed |
| VeilGateway | not deployed |
| Relayer | not deployed |
| Scope | not 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.
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);
# 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.