> ## 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.

# Privacy (STRK20 shield/unshield)

> The confidential-balance engine inside @chipi-stack/core: companion accounts, gas-tank funding, proving, and gateway submission.

The engine behind [Private Balances](/sdk/guides/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](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/).

## Headless example

```ts theme={null}
import {
  ChipiProofProvider,
  derivePoolKeypair,
  deriveCompanionKeypair,
  deriveCompanionAddress,
  requestGasFunding,
  waitForFunding,
  execShield,
  GAS_FUND_STRK,
} from "@chipi-stack/core";
import { RpcProvider } from "starknet";

const pool = await derivePoolKeypair({ anchorSecret, seedDomainTag: wallet.address });
const companion = await deriveCompanionKeypair(anchorSecret);
const companionAddress = deriveCompanionAddress({
  publicKey: companion.publicKey,
  classHash: COMPANION_CLASS_HASH,
});

const provider = new RpcProvider({ nodeUrl: "https://api.cartridge.gg/x/starknet/mainnet" });
await requestGasFunding(companionAddress, {
  baseUrl: "https://api.chipipay.com/v1",
  apiKey: apiPublicKey,
  getBearerToken: () => getToken(),
});
await waitForFunding(provider, companionAddress, { strk: GAS_FUND_STRK });
const result = await execShield(
  {
    provider,
    chainId: "0x534e5f4d41494e",
    companionAddress,
    companionKey: companion.privateKey,
    classHash: COMPANION_CLASS_HASH,
    poolKey: BigInt(pool.privateKey),
    provingProvider: new ChipiProofProvider({
      baseUrl: "https://api.chipipay.com/v1",
      apiKey: apiPublicKey,
      getBearerToken: () => getToken(),
      chainId: "0x534e5f4d41494e",
    }),
  },
  USDC_ADDRESS,
  1_000_000n,
);
```

`execShieldMany` batches several tokens into ONE proof with ONE 4-STRK pool
fee (mainnet-proven). `execUnshield` withdraws to the companion; deliver
onward with `sweepCompanion({ strict: true })`.

## 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](/sdk/guides/private-balances).
