Overview

Use Cases

Stealth Pay is infrastructure. The SDK gives you three primitives — shield, privateSend, and unshield — and cryptographic guarantees that nobody can link sender to receiver on-chain. What you build on top, and how you store or communicate note data privately, is entirely your architecture decision.

Every operation produces a commitment hash and a nullifier. These are the only things that ever touch the chain. Amounts, recipients, and salt values stay off-chain — in your database, encrypted in a message, or wherever your product stores them.

The three payment patterns

Every use case below is a combination of these three calls. Mix them to fit your product.

PatternWho calls itWhat happens on-chainWhat stays off-chain
shield(token, amount)Sender (A)Commitment inserted into Merkle tree. Tokens locked in pool.amount, salt, spending pubkey
privateSend(token, amount, B_pubkey)Sender (A)A's note nullified. New commitment for B inserted.Auto-posted to 0G Storage if zeroGStorage configured
unshield(token, amount, recipient)Note ownerNullifier published. Tokens released to recipient.Nothing — this is the exit

Real on-chain example

The following transactions were produced by the e2e test suite running against 0G Galileo testnet. They show exactly what an observer sees — and exactly what they cannot see.

Shield tx

tx hash0x9d3270efb0a458a062a84773db68ae3e8980e748f0348b415f7211d8bfeac265
from0xD91D61bd2841839eA8c37581F033C9a91Be6a5A6
to0x87fECd1AfA436490e3230C8B0B5aD49dcC1283F1(pool)
commitment0x298e3a81cdffa15ffb2de45c46b92b99f7c3e26a4b5bc0ebcdced1e89b095ee5
leaf index5
visible amountnone — hidden in commitment

Unshield tx (spend)

tx hash0xca0bdd72866cc172740563e6dea957e2fff3fc1194632295adbedaff659cc061
caller0xD91D61bd2841839eA8c37581F033C9a91Be6a5A6
recipient0xe088622BC9c8082f9A250bDC91D0CF64577FFDb9
released49.95 USDC(after 0.1% fee)
nullifierpublished on-chain — prevents double spend
link to shieldnone — ZK proof reveals no connection
An on-chain observer sees two unrelated events: a deposit into the pool and a withdrawal from the pool. The ZK proof mathematically guarantees no link can be derived between them.

Use case 1 — Private payroll

A company pays 50 employees every month. With public transfers, anyone can see every salary. With Stealth Pay, the company shields the total payroll once, then privately distributes to each employee's spending pubkey. Employees unshield to their own wallets at any time.

ts
// Company (run once per cycle)
await sdk.shield(USDC, totalPayroll);  // one public deposit

// Per employee — company stores { amount, salt } in its HR DB
for (const employee of employees) {
  await sdk.privateSend(USDC, employee.salary, employee.spendingPubkey);
  // commitment hash → store in HR system against employee record
}

// Employee (self-serve, any time)
await sdk.sync(provider);
await sdk.unshield(USDC, mySalary, myWallet);

What the company stores privately: a mapping of { employeeId → { commitment, amount, salt } }. This is standard HR data — store it in your existing payroll database. The chain only sees commitment hashes and nullifiers, not names or amounts.

What goes on-chain: one shield tx (total pool deposit) and N spend txs (one per employee), each revealing nothing about individual amounts.

Use case 2 — Confidential B2B payments

Two businesses settling invoices. Neither wants competitors to see payment amounts, frequency, or counterparty relationships.

ts
// Business A — paying an invoice
const result = await sdk.privateSend(USDC, invoiceAmount, businessB_pubkey);

// A stores privately (e.g. in their accounting system):
// { invoiceId, commitment: result.receiverCommitment, amount, salt }

// A sends to B over a private channel (Signal, encrypted email, API):
// { commitment, amount, salt }  ← "here is your payment note"

// Business B — after receiving the note details
await sdk.noteManager.trackNote(commitment, USDC, amount, salt, leafIndex);
await sdk.unshield(USDC, amount, businessB_wallet);

The "hint" is yours to design. How A communicates { amount, salt } to B is not a protocol concern — it is a product concern. Use your existing secure channel: an encrypted API webhook, a Signal message, an in-app notification. Stealth Pay provides the cryptographic guarantee; you provide the delivery mechanism.

Use case 3 — DAO treasury distribution

A DAO grants funding to builders. Public grants reveal grant sizes to competitors and create tax/regulatory exposure for recipients before they are ready to disclose.

ts
// DAO multisig shields the grants pool
await sdk.shield(USDC, grantsPool);

// Per grantee — DAO stores commitment in governance record
for (const grantee of approvedGrants) {
  const result = await sdk.privateSend(
    USDC,
    grantee.amount,
    grantee.spendingPubkey,
  );
  // record result.receiverCommitment in governance snapshot
}

// Grantee claims when ready (their timeline, not the DAO's)
await sdk.unshield(USDC, grantAmount, granteeWallet);

The DAO's governance vote approves amounts and pubkeys. The on-chain execution reveals only that funds moved through the pool — not to whom or how much per recipient.

Use case 4 — Subscription & streaming payments

A SaaS platform charges subscribers monthly without exposing customer wallet history. Each billing cycle the platform shields fees and sends to a per-customer commitment. Customers accumulate notes and withdraw in bulk.

ts
// Platform — monthly billing job
for (const subscriber of activeSubscribers) {
  await sdk.privateSend(USDC, subscriber.monthlyFee, subscriber.pubkey);
  // store commitment → subscriber record in your DB
}

// Customer — quarterly withdrawal
await sdk.sync(provider);
const balance = sdk.getPrivateBalance(USDC);
await sdk.unshield(USDC, balance.balance, customerWallet);

What builders are responsible for

The SDK handles all ZK proof generation, Merkle tree syncing, and on-chain interactions. Builders own everything off-chain:

DataWho stores itSuggested storage
commitment hashSenderYour DB — index by user/invoice ID
amount + saltSender + ReceiverEncrypted at rest; communicate over secure channel
spending privkeyEnd user onlyClient-side only — never send to your server
spending pubkeyYour platformPublic — safe to store in DB, share openly
nullifierChain (public)Already on-chain — no action needed
recipient addressReceiverOff-chain — never revealed by the protocol
The spending private key must never leave the client. If your product is a custodial wallet, generate and store it server-side with HSM-level protection. If non-custodial, derive it client-side and never transmit it.

The builder's mental model

Think of Stealth Pay the same way you think of HTTPS. You do not implement TLS — you call it. The protocol guarantees the cryptographic property. Your product is built on top: the UI, the business logic, the data storage, the user experience. The privacy guarantee is not your code's responsibility. It is the protocol's.