Shielded Pool¶
The Dilithia shielded pool lets users make private transactions using zero-knowledge proofs. Deposited tokens become unlinkable to the depositor's public address. Withdrawals require a STARK proof (generated by the wallet) that proves ownership without revealing the deposit.
Overview¶
| Operation | Layer | Requires Wallet? |
|---|---|---|
| Deposit | Wallet | Yes |
| Withdraw | Wallet | Yes (generates STARK proof) |
| Check commitment root | ChainClient | No |
| Check nullifier spent | ChainClient | No |
| Compliance proof | Wallet | Yes |
Deposit Flow¶
Depositing moves tokens from your public balance into the shielded pool. The wallet:
- Generates a random secret and nonce
- Computes a Poseidon hash commitment:
commit = Poseidon(secret, nonce, amount) - Stores the secret/nonce locally (needed for future withdrawal)
- Submits a transaction that adds the commitment to the on-chain Merkle tree
import { connect, shieldedDeposit, createChainClient } from "@dilithia/browser-sdk";
const session = await connect();
const chain = createChainClient(session.rpcUrl);
const deposit = await shieldedDeposit(500);
console.log("Commitment:", deposit.commitment);
const receipt = await chain.waitForReceipt(deposit.txHash);
console.log("Deposit confirmed at block", receipt.blockHeight);
Back up your wallet
The secret and nonce are stored in the wallet extension's local storage. If you lose access to the wallet, you lose access to your shielded funds. Always keep a backup of your wallet.
Withdraw Flow¶
Withdrawing moves tokens from the shielded pool back to a public address. The wallet:
- Loads the secret/nonce for the specified commitment
- Generates a STARK proof (~200 ms via WASM prover) proving ownership
- Computes a nullifier to prevent double-spending
- Signs and submits the withdrawal transaction
import { shieldedWithdraw, createChainClient, connect } from "@dilithia/browser-sdk";
const session = await connect();
const chain = createChainClient(session.rpcUrl);
// Withdraw commitment at index 0, amount 500, to dili1alice
const withdrawal = await shieldedWithdraw(0, 500, "dili1alice");
console.log("Nullifier:", withdrawal.nullifier);
console.log("Proof:", withdrawal.proof);
const receipt = await chain.waitForReceipt(withdrawal.txHash);
console.log("Withdrawal confirmed at block", receipt.blockHeight);
The commitmentIndex parameter refers to the index within your local list of shielded commitments (managed by the wallet).
Check Shielded Balance¶
The wallet tracks your unspent commitments locally. This data is not queryable on-chain without the secret.
Note
shieldedBalance() is a wallet-level function documented in the README. It returns commitments tracked by the wallet extension's local storage.
On-Chain Queries via ChainClient¶
You can inspect the shielded pool's public on-chain state without a wallet using the ChainClient:
Commitment Root¶
Get the current Merkle root of the commitment tree:
import { createChainClient } from "@dilithia/browser-sdk";
const chain = createChainClient("https://rpc.dilithia.network/rpc");
const root = await chain.getCommitmentRoot();
console.log("Commitment root:", root);
Nullifier Status¶
Check whether a nullifier has been spent (used in a withdrawal):
const spent = await chain.isNullifierSpent(withdrawal.nullifier);
if (spent) {
console.log("This commitment has already been withdrawn.");
}
This is useful for verifying withdrawal proofs or building block explorers.
Compliance Proofs¶
Compliance proofs let users prove statements about their shielded activity without revealing the underlying data. This enables regulatory compliance while preserving privacy.
Proof Types¶
| Type | Description | Parameters |
|---|---|---|
"not_on_sanctions" |
Prove the account is not on a sanctions list | { sanctionsList: string } |
"tax_paid" |
Prove taxes have been paid on shielded activity | { jurisdiction: string, year: number } |
"balance_range" |
Prove the shielded balance falls within a range | { min: number, max: number } |
Example: Balance Range Proof¶
Prove that your shielded balance is between 0 and 10,000 DILI without revealing the exact amount:
import { shieldedComplianceProof } from "@dilithia/browser-sdk";
const { proof, publicInputs } = await shieldedComplianceProof("balance_range", {
min: 0,
max: 10000,
});
// Send proof to a verifier (e.g., your backend or a smart contract)
await fetch("/api/verify-compliance", {
method: "POST",
body: JSON.stringify({ proof, publicInputs }),
});
Example: Sanctions Check¶
const { proof, publicInputs } = await shieldedComplianceProof("not_on_sanctions", {
sanctionsList: "ofac_sdn_2024",
});
Example: Tax Compliance¶
const { proof, publicInputs } = await shieldedComplianceProof("tax_paid", {
jurisdiction: "US",
year: 2025,
});
Sponsored Shielded Operations¶
Both deposits and withdrawals support gas sponsorship via a paymaster. See the Gas Sponsor guide for details.
// Sponsored deposit
const deposit = await shieldedDeposit(500, { paymaster: "dili1sponsor" });
// Sponsored withdrawal
const withdrawal = await shieldedWithdraw(0, 500, "dili1alice", {
paymaster: "dili1sponsor",
});
This is particularly useful for withdrawals, where the recipient may not yet have a public balance to pay gas.