Documentation
SDK
The privacy layer for Robinhood Chain, as a package. Give any app or agent shielded balances, private payments, and selective disclosure — without building a proving stack from scratch.
What Gloam gives your app
Robinhood Chain is public: every balance, size, and move is visible. Gloam is the shielded chamber on top of it, and @gloamtrade/sdk is that chamber as a dependency. The Gloam vault app is the reference implementation, not a special case — the same core runs in your app.
- Shielded balances. Deposit ETH or tokenized stocks into a private balance only the holder can see or spend.
- Private payments. Send inside the vault to a receive tag, with on-chain encrypted discovery.
- Selective disclosure. Let a holder prove one balance to a chosen party — an auditor, a counterparty — revealing nothing else. See the guide.
- Cash out. Unshield to a public balance with a real browser- or node-generated Groth16 proof.
Install
The SDK ships TypeScript source, consumed through your bundler (NexttranspilePackages, Vite, tsx) — the same way the reference app uses it. snarkjs is an optional peer, needed only when you generate proofs (shield, unshield, disclosure).
npm install @gloamtrade/sdk viem
npm install snarkjs # optional peer, for provingThe model
Gloam is a shielded pool. A private balance is a note:
commitment = Poseidon(secret, amount, asset)
nullifier = Poseidon(secret, commitment)Shielding inserts the commitment as a leaf in a depth-20 incremental Merkle tree; the secret is the only spend authority and never leaves the client. Spending (send, cash out, trade) proves in zero knowledge that you know the secret for a commitment in the tree, and publishes the nullifier so it cannot be spent twice — without revealing which note. The chain verifies a Groth16 proof, never your identity or size. Nothing is a mock; if a path cannot be private, it waits.
Quickstart: shield privately
The hardened pool enforces a proof at deposit (audit C1), so plain shield() reverts. buildShieldBoundIntent mints the note and generates the shield proof; you sign the resolved call.
import { buildShieldBoundIntent, artifactProver } from "@gloamtrade/sdk";
import { parseEther } from "viem";
const intent = await buildShieldBoundIntent({
amountWei: parseEther("0.001"),
// file paths (node) or URLs (browser) to the shield circuit artifacts
prover: artifactProver({ wasm: "shield.wasm", zkey: "shield_final.zkey" }),
});
// intent.exec is a ready shieldBound(asset, amount, commitment, proof) call.
// intent.note.secret is the spend key — PERSIST IT.
await wallet.writeContract({
address: intent.exec.poolAddress,
abi: shieldPoolAbi,
functionName: intent.exec.fn, // "shieldBound"
args: intent.exec.args,
value: intent.exec.valueWei, // amount for native ETH, 0n for tokens
});A complete runnable agent is in examples/agent-shield. See Build a private agent.
Intents: plan and exec
Every action is an intent with two layers. The plan is portable and safe to log or hand to an agent (symbol, amount, no secrets). The exec resolves the on-chain call (wei amounts, resolved addresses, proof args). The SDK and the agent server share one intent shape.
| Builder | Resolves |
|---|---|
buildShieldBoundIntent | Mints a note + shield proof → shieldBound(...) (the live deposit path) |
buildShieldIntent | Plain shield(...) — only for pools without a shield verifier |
buildUnshieldIntent | Witness + proof → unshield(...) (cash out) |
buildPrivateSendIntent | Transfer witness → transfer(...) to a receive tag |
API surface
Notes
import { makeBoundNotePoseidon, noteNullifierPoseidon } from "@gloamtrade/sdk";
const note = await makeBoundNotePoseidon(amountWei, assetAddress);
// { secret, commitment, secretField, commitmentField }
const nullifier = await noteNullifierPoseidon(note.secretField, note.commitmentField);Prover (injected)
Proving is environment-bound, so it is passed in. artifactProver binds snarkjs to your circuit artifacts (paths in node, URLs in the browser); proveGroth16 is the one-shot form.
import { artifactProver, proveGroth16 } from "@gloamtrade/sdk";
const prover = artifactProver({ wasm, zkey });
const { proofBytes, publicSignals } = await prover(circomInput);Merkle
A depth-20 incremental tree and a circom path builder (buildPoseidonMerklePath) for spend proofs. Rebuild the tree from the pool's Shielded events, then prove membership.
Rates & privacy
Pure sealed-rate math (exactSealedAmounts) and the size-privacy floor policy (publicAmountOutMin), proven against the circuit equality amountOut·rateOut = amountSwap·rateIn.
Constants
SEALED_VAULT (the hardened pool), SHIELD_VERIFIER, NATIVE_ASSET, and the chain ids. The default SEALED_VAULT is the hardened C1/C2/C3 pool — never the retired 0x4F38 pool.
Integration patterns
- Private balances in your app. Shield with
buildShieldBoundIntent, persistnote.secretin your own store keyed by commitment, cash out with the unshield builder. - Private payments. Send to a receive tag; the recipient scans the encrypted
GloamPayMemoinbox and opens the note. See Private pay. - Compliance without opacity. A holder proves one balance to an auditor via a disclosure, revealing nothing else.
- Autonomous agents. The same core runs server-side, so an AI agent can hold and move value privately under policy. See Agents.
Public inputs are pinned
The SDK pins the exact public-input ordering the deployed verifiers expect, so an integrator cannot drift out of proof compatibility:
- shield,
[commitment, amount, asset] - unshield,
[root, nullifier, asset, amount, to] - transfer,
[root, nullifier, newCommitment0, newCommitment1] - sealedSwap, nine signals ending in
amountOutMin, rateIn, rateOut
Guardrails
Robinhood Chain testnet 46630 only, with dev-ceremony keys. Real privacy only, never a mock success. Note secrets are the sole spend authority — persist and protect them. Mainnet waits for a production ceremony and an external audit; see the production gate.