Skip to main content

Portal Deposit Addresses

A portal is a reusable, public deposit address — like a centralized-exchange deposit address, but self-custodial. The owner publishes one fixed address E; any number of senders fund it with ordinary ERC-20 transfers (no Privacy Boost software, no per-deposit ceremony); and the funds are later swept into the owner’s shielded balance. Crediting is permissionless and decoupled from the deposit, so the recipient does not have to be online for any individual deposit. Public observers see the portal address, token, amount, timing, and sender — but never which Privacy Boost account receives the funds.
Preview feature — pending external audit. Portal deposit addresses are implemented (contracts, circuit, and SDK) but have not completed external security audit, and several pieces are launch-limited (see Launch limitations). Treat the API as stable-but-unaudited and gate production use on your own review. The feature is enabled per deployment; sdk.portalDelegateAddress() returns the delegate address advertised by the server’s /info, or undefined if the server does not support portals.

How it works

A portal address E is a normal Ethereum EOA that the owner controls, upgraded via EIP-7702 so it can only do two things: sweep supported tokens into the pool, and let the owner reclaim funds resting at it. It can never spend from the pool.
 Sender(s) ──ERC-20──▶  Portal address E   (public, reusable, fixed)
                             │  sweep (permissionless)

                        escrow in PrivacyBoost ──epoch proof──▶ owner credited (owner hidden)

          owner reclaim ◀────┘  if never swept / never credited, after a cancel delay
  1. Create. The owner derives a fresh portal address E from their seed, delegates it to the shared portal implementation, and registers a public on-chain binding H that commits to their account without revealing it.
  2. Publish. The owner publishes a private registry entry (E → blind) so the server’s indexer can credit deposits to the right account. A missing or wrong entry only delays crediting — it can never send funds to the wrong account.
  3. Fund. Anyone sends standard ERC-20 to E. This is an ordinary token transfer; the sender needs nothing from Privacy Boost.
  4. Sweep. Anyone (at launch, an operator) calls the permissionless sweep, which measures the actual received balance and escrows it in the pool.
  5. Credit. The relay submits an epoch proof that credits the swept amount to the owner’s shielded balance as a new note. The owner’s identifying key stays private throughout — the proof binds the credit to the public H without revealing the account.
  6. Reclaim / withdraw. If a swept deposit is never credited, the owner reclaims it after a cancel delay. Funds that were never swept (e.g. an unsupported token) can be withdrawn directly from E.
A swept deposit only ever ends credited or reclaimed — no third party can force any other outcome, and an un-credited deposit is always recoverable.

SDK API shape

The SDK exposes the same high-level portal lifecycle across TypeScript, CLI, WASM, iOS, Android, and React Native:
  • create / createPortal derives E, gaslessly delegates and registers it, and publishes discovery.
  • list, deposits, and status expose portal and deposit state.
  • sweep / sweepPortal triggers requestPortalDeposit from the connected wallet when an app needs a self-sweep backstop.
  • reclaim / reclaimPortalDeposit reclaims an uncredited swept deposit after the cancel delay.
  • withdraw / withdrawPortal signs an escape-hatch transaction from E for raw funds resting at the portal.
The seed-derived high-level flow keeps E’s key inside core; it never crosses the binding boundary. Low-level generatePortalEoa and encodePortalWithdraw remain only as custodial escape hatches for integrations that intentionally manage their own portal EOA key.

Trust and privacy

The portal is a deposit-only extension: it can only add notes to the shared note tree, never spend or alter existing notes, and it grants no new capability to the relay or TEE.
ActorCanCannot
Ownerregister once; spend credited funds; reclaim / self-withdraw
Sendersend ERC-20 to public Elearn which account E funds; spend it
Sweepermove E’s balance into the poolredirect the credit; learn the recipient; alter the amount
Relay / proverbuild the proof from non-spend values; submit the epochchange amount/recipient; credit a different account; spend
TEEobserve deposits; derive the owner’s note via its registrysign anything; custody, move, or seize funds
A portal note joins the same shared note tree as every other deposit and transfer, so its anonymity set is the full mixed pool. The portal hides who receives, not that E was funded. Because a reusable public address makes the sender ↔ E link public, use one portal per counterparty or rotate portals when unlinkability between funders matters — these are product-layer mitigations, not cryptographic guarantees. The discovery registry (E → blind) is privacy-load-bearing and should be treated as confidential.

Launch limitations

These are known constraints of the current (pre-audit) launch; design around them:
  • status.delegated always reports false. The discovery server does not yet compute on-chain delegation status. A portal you just created is delegated regardless of what this field says — do not gate a “ready” check on it.
  • deposit.cancellableAtBlock is always 0. The reclaim-eligibility block is not yet computed by the server. Treat 0 as unknown, not “reclaimable now.”
  • Sweeping is operator-run with a zero fee. The gross swept amount equals the net credited amount today. A permissionless keeper market with a capped, per-token fee is a later step.
  • Registration is write-once per E. A portal binding cannot be changed after it is set; derive a new portal instead.
  • Single-chain. Portals credit on a chain that hosts a pool. Multi-chain portals are a planned additive extension, out of first-audit scope.

Using it from the SDK

TypeScript

CLI

iOS

Android

React Native

WASM