Docs
What VERX hides, and what it does not.
VERX encrypts amounts using Solana's Token-2022 confidential transfer extension. A balance becomes an ElGamal ciphertext that only your viewing key opens. Moving one requires a proof that the amounts add up and lie in range, checked by ZkE1Gama1Proof11111111111111111111111111111, a program built into the validator.
~ devnet · audited: false · own program deployed: none
This runs against a Token-2022 mint created for testing, whose mint authority is a throwaway key. There is no mainnet deployment and nothing here has been audited by anyone.
It is not a mixer
- VERX does not mix funds and has no shared pool.
- It does not break the link between a wallet and its history.
- It hides amounts. It does not hide who paid whom.
- Nothing here has been audited, and the site does not claim otherwise.
How to use it
Five steps, and what each one costs
Connect a wallet that can sign messages
Phantom, Solflare or Backpack, set to devnet. The wallet must be able to sign a MESSAGE, not just transactions: the viewing keys come from one message signature. A wallet without that cannot open a vault at all, and the app says so rather than failing later.
Unlock the vault with one signature
You sign the 18 bytes solana-conf-bal/v1. That signature derives your ElGamal keypair and your AES key, in the browser. Nothing is sent anywhere, and nothing is stored: a reload asks again. Treat that signature like a private key, because it is one: anyone who gets you to sign those bytes can rebuild your viewing keys forever.
Create the confidential account, once
This writes your ElGamal public key into your token account. The app asks your wallet for the derivation signature a SECOND time first and refuses to continue unless both agree: a wallet that signs differently each time would write a key its own next session could not read, and no backup could recover it.
Move a public balance into the confidential one
One transaction, one approval. The deposit amount is VISIBLE on chain; what becomes private is everything that happens to it afterwards. The credit arrives as pending and is not spendable until you apply it, which is the protocol stopping a sender from flooding your account with credits.
Four transactions, or three
A confidential send is 5 transactions and the same number of approvals; a withdrawal is 4. The proofs are built in your browser and staged in temporary accounts first, which is why there is more than one. Those accounts hold 0.01213104 SOL of rent while the flow runs and give it back when the last transaction closes them. Every one of them belongs to your wallet from the moment it exists, so if you close the tab halfway you can close them later and recover the rent: the Vault screen offers it.
Privacy, exactly
Exactly what each step hides
| step | hidden | visible to anyone reading the chain |
|---|---|---|
| Connect wallet | the balance | Everything. Nothing is private until an amount is encrypted. |
| Private vault | the balance | That the account exists, and that it is yours. |
| Shield amounts | nothing | The deposit itself, and its amount, at the moment it is made. |
| Private send | the amount | That you paid that recipient, and when. The amount is not. |
| Withdraw | nothing | The withdrawal and its amount. |
| Activity | the balance | Everything in it: these are public transactions. What is NOT in it is anything another machine did. |
A confidential transfer hides HOW MUCH. It does not hide WHO: both token accounts, the fee payer and the slot are ordinary public data. Shielding an unusual amount and withdrawing the same unusual amount later also links the two for anyone watching.
Keys, backup and recovery
Your wallet is the backup
- Your viewing keys are derived from your wallet signature, so your wallet IS the backup. The same wallet on another machine derives the same keys and reads the same balance. There is no separate seed to write down, and VERX could not give you one.
- Nothing is stored in the browser except a list of transactions this browser sent. No key, no signature, no ciphertext. A reload costs one signature.
- Losing the wallet loses the viewing keys, and with them the ability to read or spend the confidential balance. A confidential balance cannot be recovered from a seed phrase alone by any other tool: it needs the same derivation.
- Switching accounts in your wallet locks the vault immediately. A vault belongs to one account.
- Any script running on this origin can ask your wallet to sign the derivation message. The page ships no third-party code and a content security policy stops it talking to anywhere but the RPC, which raises the cost of an injection. It does not make one harmless: approve that prompt only when you asked for it.
Signature phishing, and where the browser stops helping
Where the browser stops helping
- The derivation signature is the viewing key. Any website can ask your wallet to sign the same 18 bytes. If you approve it somewhere else, that site can read this balance for as long as it exists, and there is no way to rotate the key: it comes from your wallet.
- So read the prompt. VERX asks for a MESSAGE that says exactly solana-conf-bal/v1, never a transaction, and only on a page you opened yourself. A prompt for those bytes on a page you did not navigate to is an attack.
- This page ships no third-party scripts and its content security policy lets it talk to nothing but the RPC, which raises the cost of an injected script. It does not remove it: the RPC must be reachable, and an RPC accepts arbitrary strings. Script injection on this origin is still fatal.
- A browser extension with permission to read this page can read anything in it. So can anyone with your unlocked machine. The vault keys live in memory only, which bounds how long they are there, not who can reach them while they are.
- Nothing is stored and nothing is logged: no key, no signature, no ciphertext, in no store, which is measured on every run of npm run probe-app.
What it cannot do
What it cannot do
| limit | detail |
|---|---|
| Only devnet, and only against this one test mint | Shield, send and withdraw all run from the browser now, but against a Token-2022 mint created for testing whose authority is a throwaway key. No mainnet deployment exists, nothing has been audited, and a real mint would have to be configured with the confidential extension at creation time. |
| A multi transaction flow asks for one approval per transaction | A transfer is five transactions and a withdrawal is four, because the proofs are staged in accounts before they are cited. Signing them all up front would be one prompt, and it was the first design, but a blockhash lives about sixty seconds and these have to land in order: a flow that expired halfway would leave accounts open and a balance mid-move. Correctness won, so each transaction gets a fresh blockhash and the screens say how many approvals to expect. |
| A confidential balance must stay under 2^32 base units | Measured: an ElGamal ciphertext of 4,294,967,295 decrypts in 0.09s and one of 4,294,967,296 fails outright, because reading a balance is a discrete log. A first devnet run shielded 250,000 tokens at 6 decimals, 58x over that, and the balance became permanently unreadable. With 6 decimals the ceiling is 4,294.967295 tokens per account. |
| Wrapped SOL cannot be shielded | So11111111111111111111111111111111111111112 is owned by the CLASSIC Token program and carries no extensions. Confidential transfers need a Token-2022 mint created WITH the extension, so VERX uses its own mint. |
Supported assets
One mint, and why
One devnet mint, created for testing with the confidential extension enabled: Aw4gxqPKJrrFpVqT5K7z5ndXWQrjWXwymGm8XDdSGFdz, six decimals.
- Wrapped SOL cannot be shielded: So111…112 belongs to the classic Token program and carries no extensions. A mint must be created WITH the confidential extension to hold a confidential balance, which cannot be added later.
- No mainnet asset is supported, because nothing is deployed to mainnet. The proof program itself does run there: measured 2026-10-16 on mainnet and devnet.
- A confidential balance tops out at 281,474,976,710,655 base units while it is pending, because a pending balance is read from a 16 bit and a 32 bit ciphertext. The available balance is read by AES and has no such ceiling.
Status of each part
What is live, and what is not
| feature | status | still visible on chain |
|---|---|---|
| Connect wallet | live | Everything. Nothing is private until an amount is encrypted. |
| Private vault | live | That the account exists, and that it is yours. |
| Shield amounts | live | The deposit itself, and its amount, at the moment it is made. |
| Private send | live | That you paid that recipient, and when. The amount is not. |
| Withdraw | live | The withdrawal and its amount. |
| Activity | live | Everything in it: these are public transactions. What is NOT in it is anything another machine did. |
| Private swap | planned | Not determined. This is why it is not called live. |
| Lending | planned | Not determined. |
`live` means a browser has done it against devnet and the chain was read back afterwards: 10 signatures, verified at slot 511,962,211. `planned` means nothing is written.
Built on
What it is built on
| library or program | version | role |
|---|---|---|
| @solana-program/token-2022 | 0.20.0 | the Token-2022 confidential transfer instructions |
| @solana/zk-sdk | 0.5.4 | proof generation, as WASM from the Rust solana-zk-sdk |
| @solana/kit | 8.5.0 | rpc, transaction planning and compilation |
| @solana/web3.js | 1.99.0 | the wire format wallets speak, for signing only |
| TokenzQdBNbL… | Token-2022 | the confidential transfer extension itself |