Skip to main content
Shielding (deposits) is temporarily paused. After StarkWare’s July 2026 STRK20 pool v2.0 upgrade, shield / execShield into the shared privacy pool requires a screener attestation issued by StarkWare’s hosted proving service — Chipi is finalizing that access. The additional_data plumbing in this release carries that attestation once available. unshield and getPrivateBalance work today; shields will revert until access lands. We’ll announce on X (@hichipipay) when shielding is back on.
The engine behind Private Balances and the React hooks. Use this layer directly for headless/server flows; apps should prefer the hooks.

The companion account

The StarkWare pool requires its user_addr to implement SNIP-6 is_valid_signature, which advanced account classes (like Chipi’s SHHH wallets) deliberately do not expose. Instead of changing an audited account class, each user gets a companion: a lightweight standard account (any SNIP-6 class, e.g. OpenZeppelin 0x061dac032f228abef9c6626f995015233097ae253a7f72d68552db02f2971b8f) that acts as the pool identity.
  • Derived, not created: deriveCompanionKeypair(anchor) + deriveCompanionAddress({publicKey, classHash}) are deterministic from the user’s owner-independent anchor. Cache the address; never the keys.
  • Counterfactual: it exists on-chain only after first use. execShield deploys it automatically (self-paid from gas-tank-fronted STRK, salt = pubkey so the deployed address equals the derived one).
  • Never holds user funds at rest: deposits pass through it into the pool; withdrawals pass back out to the user’s real wallet; leftover fronted STRK sweeps back to the gas tank.
Why this design over changing the account class: see our write-up Adding confidential payments to an advanced smart account — two ways to use the StarkWare privacy pool, and why we picked the companion.

Headless example

Multi-token operations

Shield several tokens at once

execShieldMany batches N deposits into ONE apply_actions — one proof, one transaction, one 4-STRK pool fee for the whole batch (mainnet-proven by the S5 smoke, scripts/multi-token-smoke.mjs). It takes the same CompanionCtx as execShield:
Two things execShieldMany handles for you:
  • Approvals are aggregated. When STRK itself is in the batch, its deposit approval and the pool’s 4-STRK fee approval share one allowance slot — separate approves would overwrite each other, so they are summed into a single approval.
  • Single-item batches delegate to the proven single-token path — calling it with one deposit is exactly execShield.
Funding is the same pre-step as the single-token example above (requestGasFunding + waitForFunding), with one addition: the companion must hold every deposit token before the call, and when STRK is one of the deposits it needs GAS_FUND_STRK plus the STRK being shielded — the fronted envelope is gas/fee money, never the deposit.

Unshielding several tokens

There is no unshieldMany yet: each withdrawal is its own proof, its own transaction, and its own 4-STRK pool fee. Two operational constraints, both found on mainnet by the S5 smoke:
  1. Refuel before every withdrawal. The gateway validates ~7.8 STRK of max resource bounds per transaction, so gas funded once runs dry after the first withdrawal.
  2. Wait for the prover’s view between operations. Proving runs against head − 12; state written moments ago is invisible to it for ~13 blocks (~5–6 minutes). Withdrawals share the user’s note channel, so the second compile must see the pool state the first one just changed. Submitting too early fails with the prover’s state_too_fresh hint (retry_after_seconds in the error body) — wait and retry.
In an interactive app you usually won’t hard-wait between withdrawals — run them sequentially, catch the state_too_fresh failure, tell the user what already came back (it IS back), and retry the remaining token a few minutes later. Money is never stranded: a failed withdrawal leaves the notes in the pool, spendable on the next attempt.

Trust model

Keys derive client-side and are never stored server-side, but delegated proving includes the pool key in each proof request: Chipi’s private prover sees it per operation. Privacy holds against the public, not the proving operator. Details in the guide.