Skip to main content

Relaying

Transfers, withdrawals, and payment-request fulfillments all reach the chain through the same submission step. A relayer performs that submission on your behalf and takes a fee, so your wallet never appears as the submitter and never needs ETH for gas. Because the mechanics are identical across the operations, they are documented once here, and each operation's own page covers only its execution paths.

The relay cycle

  1. Get a signed quote. You ask a relayer what it will charge, and it answers with a signed commitment: relay this transaction at this fee, valid until this expiry. The signature makes the quote binding, and the fee is denominated in the asset you are sending, not ETH. Quotes are short-lived (typically 60 to 90 seconds), so if proof generation outlasts one, request a fresh quote and rebuild.
  2. Bind the processor. Each proof names the single address allowed to submit it, the processor, and the PoolVault rejects a submission from anyone else. Bind the processor the relayer publishes in its configuration (RelayerInfo.processorAddress) rather than assuming one, because it is deployment-dependent. On the current Sepolia deployment that address resolves to the PrivacyPoolRelay contract, which calls the pool and takes the relayer's fee from the public payout.
  3. Submit. The relayer submits your transaction exactly as prepared, or not at all. It pays the gas, so your wallet never appears as the submitter.

What a relayer can and cannot do

A relayer cannot steal or redirect funds, and it never learns the sender, the recipient, or a shielded transfer's amount. It does see the proof's shape, the encrypted payloads, the asset and its own fee, and request metadata such as your IP address. It can refuse or delay a transaction, in which case you can choose a different relayer.

Submitting yourself, self-relay, removes the relayer from the flow: you bind your own wallet as the processor and submit from it, paying the gas. It is not a privacy-preserving path, because your wallet is then publicly linked to the pool transaction as its submitter.

Other processors and routes

The plain relay is one of several processors a proof can bind. The relayer decides which contract to call from the request, and publishes every address an integrator needs in its discovery endpoint, GET /v1/details: contracts.relay (PrivacyPoolRelay), contracts.relaySwaps (present only where swaps are supported), processors (its allowed_processors allowlist, the yield routers), and yield[] (per wrapper: share token, underlying, router, zap, decimals). Read those rather than hand-copying addresses; a wrong processor bound into a proof is unrecoverable without re-proving.

OperationBind as processorRelayer routesOn-chain call
Transfer, withdrawPrivacyPoolRelayquote/.../transfer or quote/.../receive, then relay/.../transfer or relay/.../withdrawalrelay
Register and withdrawPrivacyPoolRelayrelay/.../register-and-withdrawalregisterAndRelay
Stealth withdrawPrivacyPoolRelayrelay/.../relay-and-announcerelayAndAnnounce
Batch withdrawPrivacyPoolRelayquote/.../batch, then relay/.../batchrelayBatch
Yield withdrawthe wrapper's PPRouterthe withdrawal routes, with processorAddress set to the routerPPRouter.withdrawToUnderlying
Withdraw and swap, reshieldRelaySwapsquote/.../swap, then relay/.../relay-swapRelaySwaps.execute, or executeUnwrapped for a yield share

All routes are under /v1/{quote,relay}/evm/{chainId}/. A request naming a processorAddress outside the relayer's allowed_processors is rejected. For a yield share the relayer prices the fee through its Underlying quoter alias, scaling the underlying's price by the wrapper's live previewRedeem, so the committed fee is in shares and tracks accrued yield.

In this group