How it works
A Private Send moves funds in two on-chain steps separated by a shielded pool. The wallet first deposits the user’s funds into the pool; the privacy layer then withdraws to the recipient from the pool. The deposit and the withdrawal are not publicly linked. When the user enables privacy on a send and chooses a token, amount, and recipient, the wallet requests a prepared order from the privacy enclave. The enclave returns a signed order that includes the privacy fee and the deposit calldata. The wallet verifies the response signature, the request nonce, the deposit destination against an allowlist of known privacy-layer contracts, and reconciles the amount before asking the user to sign. For ERC-20 tokens, the user signs an approval transaction before the deposit. Native-token sends skip this step. The user then signs the deposit, which moves the funds from their wallet into the shielded pool. Inside the pool the funds are represented as private notes; the deposit and the later withdrawal are not publicly linked. The privacy layer then issues a private withdrawal that delivers the funds to the recipient. The amount debited from the user’s wallet equals the requested send amount plus the privacy fee. The fee is disclosed before the user confirms. Once the deposit has been submitted, the wallet polls the order for delivery status. A top-level failure reported by the privacy layer surfaces immediately as a failed send rather than waiting for the polling window to time out. The send lifecycle includes an explicit verification gate before the deposit. If any verification step fails, Private Send is disabled for that transaction and the wallet falls back to the normal transfer path.Privacy modes
The wallet exposes two modes. Both break the link between the sender’s wallet and the public delivery; they differ in whether the recipient and amount are also shielded. Sender-private. The default mode described in How it works. The sender’s wallet is unlinkable from the delivery transaction. The recipient address and amount remain visible on the delivering transaction. Fully-shielded transfers. The recipient receives funds at a one-time stealth address derived from their viewing key, and the delivering transaction does not expose the final recipient or the amount. The only divergence from the sender-private flow is the final delivery leg: the private withdrawal targets a stealth address rather than a plain recipient address. Anonymity-set considerations are stronger in this mode because no recipient or amount metadata is exposed on chain.What is and isn’t private
| Property | Sender-private | Fully-shielded |
|---|---|---|
| Link from sender wallet to delivery | Broken (shielded) | Broken (shielded) |
| Recipient address | Visible on delivery | Shielded (stealth address) |
| Amount | Visible on delivery | Shielded |
| User custody of funds | Retained | Retained |
| Anonymity set | Grows with pool usage | Grows with pool usage |
Security and verification model
Private Send’s integrity rests on a layered trust chain. Four cryptographic layers establish what code is running, that it is the code we reviewed, and that requests and responses are bound to that code. Two client-side controls layer defense in depth on top. Enclave attestation establishes what code is running. The privacy provider’s API runs inside a hardware-backed Trusted Execution Environment (TEE). The enclave exposes an attestation endpoint that returns a token signed by the cloud provider’s Confidential Computing attestation service. The token cryptographically commits to the exact container image digest running in the enclave: any change to the server code changes the digest and invalidates the attestation. The wallet requests this token with a fresh nonce, so the attestation cannot be replayed. Attestation validation is complete before the wallet trusts the token: the wallet checks the token issuer, that the token has not expired, that the workload identifies as the expected Confidential Space software, and that the workload runs in the stable (not debug or preview) support profile. Attestation and JWKS fetches are bounded by the wallet’s shared request timeout so a stalled dependency cannot hang verification. Source-to-digest provenance establishes that the running code is the code we reviewed. The enclave’s source is published, and every production build is signed via a transparency-log-backed supply-chain attestation (Sigstore and cosign, with a Rekor transparency-log entry per build). Our backend independently verifies that the attested image digest was built from the published source by the expected CI pipeline, and maintains a signed allowlist of approved image digests. The wallet only trusts attestations whose digest is on that allowlist. Response signing establishes that an API response actually came from the enclave and was not tampered with. On attestation, the enclave generates an ephemeral signing keypair and binds its public key into the attestation token as the token audience, proving the key was generated inside the enclave. Every API response carries a signature over the response body in a response-signature header, which the wallet verifies against that attested key. The enclave also echoes the request’s per-request nonce inside the signed response, so a previously captured response cannot be replayed. Response-signature verification applies uniformly: failure responses and non-2xx error bodies must also be signature-verified and nonce-matched before the wallet trusts or surfaces their contents, so an unverified error payload cannot leak into the UI. When the enclave’s response-signing key legitimately rotates, the wallet retries verification once against a freshly attested key and a reloaded allowlist so a cached key does not cause a valid response to be rejected. If both attempts fail, the response is rejected. Request signing and EIP-712 binding establish that the enclave acts only on what the user authorized. At session creation the wallet generates a session-scoped signing keypair (separate from the wallet key), registers its public key with the enclave, and signs every subsequent request payload with it in a request-signature header. The enclave rejects any request whose signature does not match. For sensitive transactions, the wallet additionally signs an EIP-712 typed-data struct that binds the exact parameters: destination address, token, chain, and a single-use nonce. If any field is altered in transit, the reconstructed struct does not match and the enclave rejects the request. This turns request tampering into a prevented failure rather than a detected-after-the-fact one. When the connected wallet is an OMS smart wallet rather than an externally owned account, the wallet validates the session and typed-data signatures through smart-wallet signature validation (ERC-1271 style) instead of skipping the check. Order-status requests are additionally bound to the specificorderId they poll, so a signed status request cannot be replayed against a different order.
Destination allowlisting and amount reconciliation are client-side controls. Before submitting any transaction, the wallet validates that the deposit destination and any approval target match a known privacy-layer contract, and reconciles the amount it is about to move against what the user requested plus the disclosed fee. A response that tried to redirect funds or inflate the amount is rejected client-side before the user signs.
Value-integrity checks run unconditionally, independent of the per-chain address allowlist. A native-token deposit must carry a value equal to the signed amountIn; an ERC-20 deposit must carry no native value and must declare a usable approval target. The signed deposit amount returned by the privacy layer must match the amount the user signed plus the quoted fee, or the send is aborted before the wallet requests wallet approval or submits the deposit. The relayer preflight is bound to the specific order it was prepared for, and the wallet refuses to submit a preflight whose order identifier does not match the verified order at submit time, so a stale preflight cannot deposit against a different order than the one the user authorized.
Fail-closed gating is the final client-side control. If any verification step fails (bad attestation, an image digest not on the allowlist, a signature mismatch, or the verification service being unreachable), Private Send is disabled for that transaction and the wallet falls back to the normal (non-private) transfer path or surfaces the privacy option as unavailable. If the trusted image-digest allowlist is temporarily unreachable, the privacy toggle is disabled rather than opened against an unknown allowlist. There is no “unverified privacy” mode. The feature can also be remotely disabled if a concern is identified.
| Layer | Guarantees | Mechanism |
|---|---|---|
| Enclave attestation | The privacy service runs unmodified, known code | Confidential Computing attestation token (nonce-bound) over the image digest |
| Source provenance | That code is the build we reviewed | Transparency-log-backed (Sigstore and cosign) signature; verified digest allowlist |
| Response signing | Responses are genuinely from the enclave, untampered, non-replayable | Enclave-attested ephemeral key signs each response; per-request nonce echoed |
| Request signing | Requests reach the enclave unaltered | Session-scoped key signs each request payload |
| Transaction binding | The enclave acts only on user-authorized parameters | EIP-712 typed-data struct binding destination, token, chain, nonce |
| Client controls | Funds cannot be redirected or inflated even on a provider flaw | Contract allowlist, amount reconciliation, fail-closed gating |
Recovery
Because a Private Send is a two-step deposit-then-deliver flow, an interrupted or failed delivery can leave funds resting in the shielded pool. The wallet provides a recovery flow that returns those funds to the user’s own wallet. Funds in this state are never lost: they remain in the pool, associated with the user, and recoverable. Recovery is non-custodial. The destination is the user’s own connected wallet, and it is bound into the EIP-712 typed-data struct that authorizes the recovery, so the destination cannot be redirected. Recovery requires a connected wallet: it is a signed operation, not a read. The wallet validates that the recovery session and withdrawal signatures were produced by the expected recovery wallet before submitting a recovery API call, so a mismatched signer cannot initiate recovery. Recovery sessions are scoped by chain and wallet address, and switching between wallets in the app resets Private Send recovery state so recoverable balances belonging to one wallet are not shown under another. A recovery submission that hits the client timeout is treated as a failed submission and surfaced as such rather than left hanging.Supported networks and tokens
Private Send is available on Ethereum, Polygon, Base, Arbitrum, and Optimism. The wallet surfaces availability dynamically based on the selected token and chain: the privacy option only appears when the chosen combination is supported. See Supported networks for the canonical wallet-wide list of chains.Related
Security
Key security, enclave-bound authentication, and the non-custodial guarantee.
Smart sessions
Scoped transaction permissions so users approve once.
Wallet operations
Send transactions, sign messages, and check balances.
Recovery
Multi-auth linking and timed recovery for user wallet access.