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.
The three payment patterns
Every use case below is a combination of these three calls. Mix them to fit your product.
| Pattern | Who calls it | What happens on-chain | What 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 owner | Nullifier 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
0x9d3270efb0a458a062a84773db68ae3e8980e748f0348b415f7211d8bfeac2650xD91D61bd2841839eA8c37581F033C9a91Be6a5A60x87fECd1AfA436490e3230C8B0B5aD49dcC1283F1(pool)0x298e3a81cdffa15ffb2de45c46b92b99f7c3e26a4b5bc0ebcdced1e89b095ee55Unshield tx (spend)
0xca0bdd72866cc172740563e6dea957e2fff3fc1194632295adbedaff659cc0610xD91D61bd2841839eA8c37581F033C9a91Be6a5A60xe088622BC9c8082f9A250bDC91D0CF64577FFDb949.95 USDC(after 0.1% fee)published on-chain — prevents double spendUse 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.
// 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.
// 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.
// 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.
// 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:
| Data | Who stores it | Suggested storage |
|---|---|---|
| commitment hash | Sender | Your DB — index by user/invoice ID |
| amount + salt | Sender + Receiver | Encrypted at rest; communicate over secure channel |
| spending privkey | End user only | Client-side only — never send to your server |
| spending pubkey | Your platform | Public — safe to store in DB, share openly |
| nullifier | Chain (public) | Already on-chain — no action needed |
| recipient address | Receiver | Off-chain — never revealed by the protocol |
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.