> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chipipay.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Private Balances (shield / unshield)

> Add confidential balances to your app: users shield USDC into the STRK20 privacy pool so only they can see what they keep. Self-custodial, gasless for the end user.

<Warning>
  **Shielding (deposits) is temporarily paused.** After StarkWare's July 2026
  STRK20 pool **v2.0** upgrade, depositing into the shared privacy pool requires
  a screener attestation issued by StarkWare's hosted proving service — Chipi is
  finalizing that access. **Unshielding and private-balance reads work today;
  new shields will revert until access lands.** Don't ship a shield flow to
  production yet — we'll announce on X ([@hichipipay](https://x.com/hichipipay))
  the moment shielding is back on.
</Warning>

Production-proven engine (Chipi's consumer wallet "Modo privado", mainnet). Mental
model for your users: **checking/savings** — visible money is for spending,
private money is what they keep, invisible to everyone but them.

## The five rules

1. **Key handling**: privacy keys derive client-side from the user's anchor
   (passkey encryptKey / PRF) and are never stored server-side. Cache the
   companion **address**, never the keys. Note the proving trust model below —
   it bounds what "private" may honestly mean in your copy.
2. **One passkey gesture per shield/unshield** — that IS the security model.
   Set the expectation: *"You confirm with your fingerprint or Face ID — the
   same as when you send money."*
3. **Browsers need a gateway proxy** (`gatewaySubmitUrl`): the Starknet
   sequencer gateway has no CORS. Install the `private-balance-card` registry
   item to get the proxy route, or copy it from the example.
4. **Capture the StarkWare pool ToS** at the user's first shield.
5. **Fees**: fixed 4 STRK per op, fronted by Chipi's gas tank (metered per API
   key) — your user never holds STRK. Show it in USD via `getShieldFee`.

## The trust model, honestly

Keys derive client-side and are never stored server-side — but STRK20's
**delegated proving requires the pool spending key inside each proof
request**, so Chipi's proving service (a private prover Chipi operates; the
same model as StarkWare's hosted prover) sees it per operation and must be
trusted not to retain or use it. Privacy holds against the **public** —
chain observers, other users — not against the proving operator. Do not
tell your users "nobody, including Chipi, can see your balance." Local
proving is the roadmap item that removes this trust.

## What is the "companion"?

Each user gets a lightweight standard account (the **companion**) as their
pool identity, derived deterministically from their anchor and deployed
automatically on first shield. You only supply a standard SNIP-6 account
class hash (`classHash`, e.g. OpenZeppelin `0x061dac03…`); everything else is
handled. Why a companion instead of changing the account class: see
[our design write-up on the Starknet forum](https://community.starknet.io/t/adding-confidential-payments-to-an-advanced-smart-account-two-ways-to-use-the-starkware-privacy-pool-and-why-we-picked-the-companion/),
and the [Core SDK privacy page](/sdk/core/privacy) for the architecture.

## Quickstart

```tsx theme={null}
import { useShield, usePrivateBalance, derivePrivacyAccount } from "@chipi-stack/nextjs";

const keys = await derivePrivacyAccount({
  anchorSecret,                  // 32 bytes from your passkey rail
  seedDomainTag: wallet.address, // per-wallet domain separation
  classHash: COMPANION_CLASS_HASH,
});

const { shieldAsync } = useShield();
await shieldAsync({
  config: {
    apiPublicKey: process.env.NEXT_PUBLIC_CHIPI_API_KEY!,
    getBearerToken: () => getToken(),          // a FUNCTION: tokens die mid-flow
    gatewaySubmitUrl: "/api/starknet-gateway", // your CORS proxy
  },
  keys,
  token: USDC_ADDRESS,
  amount: 1_000_000n, // base units
  fundDeposit: (companion, amount) => transferGasless(companion, amount),
  onStep: setPhase,   // prepare | fund | deposit | prove | finish — drive a REAL loader
});

const { byToken } = usePrivateBalance({ mode: "ledger", events: recordedEvents });
```

Record every settled op in your own store — your ledger is the display source
of truth (the chain won't cleanly surface confidential moves). Failures never
strand money: a failed shield parks funds in the companion and the next
attempt sweeps them in; say "your money is safe" in error copy.

## Multiple tokens

The pool holds any ERC-20, and the engine batches deposits:

* **Shielding N tokens costs ONE proof and ONE 4-STRK fee** — batch with the
  core-level [`execShieldMany`](/sdk/core/privacy#multi-token-operations)
  (mainnet-proven). The `useShield` hook is per-token by design; for a
  multi-token shield call the core engine directly.
* **Unshielding is per token** (no `unshieldMany` yet): each withdrawal is
  its own proof and its own 4-STRK fee, and consecutive withdrawals must
  wait \~5–6 minutes for the prover's view to catch up (they share the note
  channel). Run them sequentially, surface partial results honestly — what
  came back IS back, the rest stays private and retryable — and never call
  it a failure. Full pattern with the wait/refuel details on the
  [Core SDK privacy page](/sdk/core/privacy#unshielding-several-tokens).

Product tip from Chipi's wallet: apply a small per-token dust floor (\~\$0.25)
before adding a coin to the batch, and SHOW excluded coins greyed out with the
reason — a silently skipped token reads as lost money to the user.

Full headless example (also the release smoke): `examples/private-balance` in
the SDK repo; multi-token evidence: `scripts/multi-token-smoke.mjs` (S5).
Agent skill: `chipi-private-balances`.
