opaqueDocs

Opaque documentation

Private payments, explained.

How the wallet keeps what you pay apart from who you are, how to use it on Arc and the XRP Ledger, and how to build on it: dapps, deployers and contracts owned by a post-quantum key.

What Opaque is

A self-custodial wallet whose money moves through a shared pool.

When you deposit, your funds become notes: fixed-size pieces held by a pool contract (on Arc) or a pool account (on the XRP Ledger). A note is a commitment on chain and a secret only your wallet knows.

When you pay, your wallet proves it owns one of eight same-size notes in the pool without saying which one, and the pool pays the recipient from its own account. On chain, the payment comes from the pool, never from you. The proof is built in your browser and the note secret never leaves the page.

Requests travel through three relays, each of which only knows the hop before and after it, so the services that answer them don't learn your IP address.

ArcPrivate USDC on Circle's chain, mainnet and testnet. Notes of 1, 2, 5, 10, 20, 50 and 100 USDC. Your account is a smart account signed by a hash-based, post-quantum key.
XRP LedgerPrivate XRP, RLUSD, USDC and an MPT token on testnet, built on Batch and Credentials with no smart contracts. Every dapp gets a fresh account the pool funds.

Get started

Three ways in, one wallet: the same recovery phrase opens all of them.

  • Website: opaque.credit/app (Arc mainnet), /testnet/app (Arc testnet), /testnet/xrpl (XRP Ledger testnet).
  • Browser extension: download the zip, open chrome://extensions, turn on Developer mode, and load the unzipped folder. One extension covers every network; it opens in Chrome's side panel.
  • Phone: open the website and add it to your home screen.
  1. Create a wallet. Write down the 12-word recovery phrase it shows, then type three of the words back to prove it.
  2. Set a password. It encrypts everything the wallet stores in this browser. See Password.
  3. Back it up with a passkey (optional, recommended). Clearing the browser then costs you nothing. See Passkey backup.
  4. Get test funds. Arc testnet: USDC from faucet.circle.com to your address. XRP Ledger testnet: Get test XRP on Home gives 95 XRP straight into notes.
  5. Deposit, then send. Your funds become notes; a send pays from the pool.
Mainnet is real money on unaudited contracts. The Arc mainnet wallet warns on every visit. Start on testnet.

Recovery phrase and accounts

One phrase, many accounts, every chain.

Your 12 words are the wallet. Every account, address, note and app account is drawn from them, so importing the phrase anywhere (another browser, the extension, a phone) finds the same accounts and looks up their notes on chain.

  • Accounts: Accounts → Add account makes Account 2, 3 and so on. Each has its own addresses and notes, and holds money on every chain.
  • Account key: Accounts → Show account key gives one account alone, as opq1…. Import it with Import account in another Opaque wallet to bring just that account. Whoever holds it can spend that account.
  • Rescan: Rescan the ledger looks for notes another device made.
The account key is not an Ethereum private key or an XRPL seed. Don't put it in a .env; use a deployer or the Contracts screen instead.

Password

Everything secret in the browser is encrypted under it, as MetaMask's vault is.

  • The phrase, account keys, notes, app accounts and Arc's signing keys are sealed with AES-256-GCM under a random key, and the password (PBKDF2-SHA256, 600,000 rounds) protects that key.
  • Opaque locks after 15 idle minutes, when the tab closes, or with Lock now. Show recovery phrase, Show account key and a deployer's key ask for the password again, even while unlocked.
  • A wallet made before passwords asks you to set one the next time it opens.
  • Forgot it? Opaque can't recover it. Forgot your password? on the lock screen removes the wallet from this browser; your phrase or passkey brings it back.

Passkey backup

Clear your browser, lose your laptop, and still get everything back.

The wallet encrypts your phrase, imported account keys and browser-only notes on your device, under a key only your passkey (Touch ID, Windows Hello, a phone or a password manager) can produce. Opaque stores the ciphertext, and can't open it.

To restore, choose Restore with a passkey when Opaque opens. Accounts → Remove backup deletes Opaque's copy. The backup keeps itself current within a minute of new notes arriving, without asking for the passkey again.

Deposit, send, receive

On Arc

  • Deposit: USDC at your account's address becomes notes, in the fewest whole notes that make the amount. On testnet, auto-deposit does it for you. Each deposit keeps 0.2 USDC at the address for gas.
  • Send: enter an Arc address and a whole amount. Each note is its own payment with its own proof. Wait for privacy holds the payment until its pool has fresh deposits around it, which makes it harder to match to yours by timing.
  • Receive: share your address. What arrives is public until you deposit it.

On the XRP Ledger

  • Receive privately: your account has a reusable address per asset (the pool's address plus a tag). Anything paid to it becomes notes in your wallet, and the payer sees only the pool.
  • Deposit tags: under Deposit, a one-time tag for an exact amount.
  • Send: to any XRPL address. The pool pays it in one Batch the attesters sign.

Swap

  • Arc: USDC and EURC (cirBTC too on mainnet), routed by LI.FI. The quote is asked through the relays. The swap itself is public, at your account's address, signed by your post-quantum key. What you get becomes private once it is USDC in your notes.
  • XRP Ledger: private pool-to-pool. Your notes in one pool pay out as another asset through the ledger's AMM. The attesters refuse a swap that settles far below the market.

Apps on the XRP Ledger

Connecting to an XRPL dapp gives it a fresh account of its own, funded by the pool, so the dapp never sees your main account and two dapps can't link you. That account's master key is off, and its regular key signs once and is replaced. Apps lists each one; closing it returns what's left to the pool under your tag.

Contracts, owned by a post-quantum key

Deploy and manage contracts on Arc with no classical key anywhere.

The Contracts screen on Arc deploys bytecode from your account through the standard CREATE2 factory (0x4e59b44847b379578588920cA78FbF26c0B4956C, live on Arc mainnet and testnet), and calls contracts from it. Your account signs each one with its hash-based FORS+C key, sent through the relays. Breaking classical signatures gives an attacker nothing.

  1. Use an account for this alone. Accounts → Add account, then from another account Send it 2 USDC from your notes. On chain the pool paid it, so its contracts aren't tied to you. On testnet, turn auto-deposit off (the screen offers it) so the USDC stays as gas.
  2. Build the bytecode. Through the factory, a constructor's msg.sender is the factory, so pass the owner as a constructor argument: the account address the screen shows.
    BYTECODE=$(forge inspect MyContract bytecode)
    ARGS=$(cast abi-encode "constructor(address)" 0xYourOpaqueAccount)
    echo "$BYTECODE${ARGS#0x}"          # paste this into Contracts → Deploy
  3. Deploy. The screen shows the address before you send. Each new salt gives a new address.
  4. Manage it. Call a contract takes the address and calldata, signed by the same key:
    cast calldata "setFee(uint256)" 5   # paste into Contracts → Calldata
Bytecode up to the chain's limit (48 KB) works: a deploy too large for one relay message crosses in chunks and is reassembled.

Deployers

Plain addresses for forge, hardhat and scripts, funded privately.

Accounts → Deployers → New deployer derives a fresh Arc address from your account. Fund privately opens Send with it filled in; Show key (after your password) gives PRIVATE_KEY=0x… for a project's .env. Your phrase brings every deployer back.

forge create src/MyContract.sol:MyContract --rpc-url https://rpc.testnet.arc.io --private-key $PRIVATE_KEY

A deployer is an ordinary secp256k1 key, so it is not post-quantum once it has signed. Keep only gas on it, use one per project, and don't send leftovers back to your account. For a contract that needs an owner, use Contracts instead, or pass that account as the owner.

Connect a dapp

  • XRP Ledger, in the extension: Opaque answers as GemWallet and Crossmark, so dapps that support either work unchanged. Approvals open in the extension's own window.
  • xrpl-connect: an adapter for xrpl-connect opens Opaque's connect page.
  • WalletConnect: paste a dapp's wc: link, or scan its QR code, at /testnet/xrpl-connect. The relay socket goes through Opaque's service, so WalletConnect sees Opaque's server, not you.

Supported: signAndSubmit (DEX offers, AMM deposits and more), sign-only for dapps that submit themselves, and signMessage. Opaque never connects to MetaMask or reads a browser wallet.

Privacy: who sees what

What is hidden, what is public, and the limits of this build.

WhatWho can see it
Which note paidNobody: the proof shows one of eight same-size notes, not which
That you deposited, and how muchEveryone: a deposit is public, from your address
The recipient and amount of a paymentEveryone, on chain, as a payment from the pool
Your IP addressThe first relay only. Services, the credential authority and LI.FI see the last relay
Your note secrets, phrase and keysOnly your device. The passkey backup is ciphertext
The opened payment (recipient and spend)Arc: a Chainlink CRE workflow in a secure enclave. XRP Ledger testnet: an ordinary server process

Limits today

  • One relay operator. Opaque runs all six relays. They hide you from the services, but Opaque, running every hop, could link them.
  • A ring of eight. On testnet many notes in a pool are test deposits. Keep Wait for privacy on, and don't send right after depositing the same amount.
  • Outside the wallet. Your own RPC calls (forge, hardhat) reveal your IP to the RPC provider. Contract source and verification metadata can identify you.
  • Linking by habit. Reusing a deployer across projects, or sending a deployer's leftovers back to your account, links them.

Post-quantum: what holds

PartHowQuantum-safe
Your Arc accountERC-4337 account; FORS+C (hash-based) signatures checked on chain by PQKeyRegistry, keys rotated before they wear outYes
Contracts from the Contracts screenDeployed and owned by that accountYes
Notes and the ring proofHash commitments; a hash-based (MPC-in-the-head) proofYes
XRP Ledger pool and app accountsEach key signs once and is replaced; only its hash is public before thenBy timing
DeployersOrdinary secp256k1 keysNot after first use

By timing means an attacker with a quantum computer gets about one ledger close (a few seconds) to break a key before it's retired. That's a strong practical defense, not a mathematical guarantee.

Fees

NetworkPaying from notesOther costs
ArcNone: the recipient gets the full noteGas for deposits, swaps and contract calls, in USDC, from your account's address
XRP Ledger (XRP)0.2001 XRP per note, kept by the pool for ledger costsAMM fee on swaps; whole notes only, so a remainder under one unit is carried
XRP Ledger (tokens)0.5 per noteSame as above

Networks and addresses

NetworkChainExplorer
Arc Mainnet5042, https://rpc.mainnet.arc.ioexplorer.arc.io
Arc Testnet5042002, https://rpc.testnet.arc.iotestnet.arcscan.app
XRP Ledger Testnetnetwork 1, wss://s.altnet.rippletest.net:51233testnet.xrpl.org

Arc contracts

ContractMainnetTestnet
USDC0x3600000000000000000000000000000000000000 (6 decimals; also the 18-decimal gas token)
PQKeyRegistry0x33a9d3e188835e8287f55a1a8cb7bd6554ed2faf0x6eb5b42373191121d31dfc4b5c8571c4eaf58e8f
PQ account factory0x5f8bd9531a08b6c06eeb41d9f617038e38d402060x13beaec42922e3f63fa0dbe5bba270edf46ab214
Pool, 1 USDC0xb753058ec7a8cf29d96288a5e5ae5c01c23221ea0x7e01b8a883b4dacc9063326a2e7d3eef0d679b63
Pool, 10 USDC0xe3d1c899d2700559403341db5dc26577d26690260x2770e5c2f491fcf0c8a411f6c2c8064214e1e284
Pool, 100 USDC0x91c5d56b77b2e1453647d37ca6350194a4c42bd40x0d8dff9b4903a1b550037bac773e9757f1f4d823
CREATE2 factory0x4e59b44847b379578588920cA78FbF26c0B4956C

XRP Ledger testnet pools

AssetPool accountNote sizes
XRPrw5r7pX37iAtLEh1VZdciHaFpduftUtjYe1, 2, 5 … 1,000,000
RLUSDrPNFdbacMgCk6diE8wtyBxh3ggro2QLWK51, 2, 5 … 1,000,000
USDCrfyGTnEH8fLeucbBxxpy24dsgXoFQnHJMZ1, 2, 5 … 1,000,000
tMPTr9J16ivxW5WcED3u5rscm4AhW6W2fPo9L91, 2, 5 … 1,000,000

Each XRPL pool's master key is off; three attesters sign its payouts, any two together.

Note sizes on the XRP Ledger run 1, 2 and 5 of every power of ten, from 1 to 1,000,000. A deposit splits so that every whole amount up to it can be paid exactly: each note is at most half of what is left. 95 XRP becomes 20, 20, 20, 10, 10, 5, 5, 2, 2 and 1 (10 notes), 2,400 XRP 15 notes and 1,000,000 XRP 24. Notes are spent whole with no change, so the fewest, biggest notes would leave most amounts unpayable: 50 + 20 + 20 + 5 can't make 10. Arc deposits split the same way. A size takes deposits once seven others are in it, so a note always has a ring to hide in. Until a large size has seven deposits, a big deposit splits into the largest sizes that do. On testnet, the new sizes start with seven decoy deposits from Opaque.

Questions

If I clear my browser, do I lose my money?

No, if you have your recovery phrase or a passkey backup. Your notes are found again on chain from the phrase. Only an old Browser account (from before recovery phrases) lives solely in the browser: on the XRP Ledger the passkey backup holds its notes; on Arc, its backup file does.

Why can I only send whole amounts?

Each note is spent whole, with no change: change would need a new note, linking the payment to you. The wallet picks the fewest notes that make the amount exactly.

Is my balance private?

Your notes' total is known only to your wallet. What sits at your address, before you deposit it, is public like any address.

Can Opaque freeze or take my funds?

Opaque never holds your keys or note secrets. A payment needs a credential for its recipient; the credential authority could refuse one (for example, a sanctioned address) but can't spend your notes.