Client API
createUPPClient — the main entry point for shielding, transferring, and withdrawing tokens.
createUPPClient
The main entry point for UPP operations.
import { createUPPClient } from '@permissionless-technologies/upp-sdk'
const upp = await createUPPClient({
publicClient, // viem PublicClient
walletClient, // viem WalletClient (connected to user's wallet)
chainId?, // optional — defaults to walletClient.chain.id
rpcUrl?, // optional — custom RPC for note indexing
})On creation, the SDK prompts the user's wallet for a signature to derive their private keys. No key material leaves the browser.
Methods
upp.shield(params)
Deposit ERC20 tokens into the privacy pool.
const { commitment, txHash } = await upp.shield({
token: Address, // ERC20 token contract address
amount: bigint, // Amount in token's smallest unit
proofSystem?: 'snark' | 'stark', // default: 'snark'
})| Returns | Type | Description |
|---|---|---|
commitment | Hex | On-chain note commitment |
txHash | Hex | Transaction hash |
upp.transfer(params)
Send privately to another user. Consumes one note, creates two (recipient + change).
const { recipientCommitment, changeCommitment, txHash } = await upp.transfer({
amount: bigint, // Amount to send
to: string, // Recipient's stealth meta-address (0zk1...)
note?: Note, // Specific note to spend (optional — SDK picks one)
proofSystem?: 'snark' | 'stark',
})upp.withdraw(params) / upp.unshield(params)
Exit the pool to a public address.
const { txHash } = await upp.withdraw({
amount: bigint, // Amount to withdraw
to: Address, // Recipient public address
aspId: bigint, // ASP ID to use for compliance proof
note?: Note, // Specific note to spend (optional)
})Ragequit
If your origin is not in any ASP, you can still withdraw to your own address (the original depositor address). The SDK handles this automatically when to === origin.
upp.merge(params)
Combine two notes into one. The caller becomes the new origin.
const { commitment, txHash } = await upp.merge({
notes: [note1, note2], // Exactly 2 notes to merge
})upp.scan()
Scan the blockchain for notes belonging to this account.
const notes = await upp.scan()
// Returns Note[] — all notes (including spent)
const unspent = notes.filter(n => !n.spent)
const balance = unspent.reduce((sum, n) => sum + n.amount, 0n)upp.getNotes()
Return cached notes (from last scan, without re-scanning).
const notes = upp.getNotes()upp.getStealthMetaAddress()
Get your stealth meta-address to share with senders.
const stealthAddress = upp.getStealthMetaAddress()
// '0zk1sepolia1...'upp.exportViewingKeys(params)
Export viewing keys for auditors with graduated disclosure (4-tier key hierarchy).
// Tier 1: Per-note keys (minimal disclosure — specific notes only)
const auditExport = await upp.exportViewingKeys({
notes: [note1, note2],
})
// Tier 2: Master viewing key (all notes, no spend authority)
const fullExport = await upp.exportViewingKeys({
notes: allNotes,
includeMasterKey: true,
})
// Tier 3: Include nullifier key (outbound transfer tracking)
const fullExportWithNullifiers = await upp.exportViewingKeys({
notes: allNotes,
includeMasterKey: true,
includeNullifierKey: true,
})The export includes a nonce chain and EIP-712 signed note count for completeness verification. The output JSON is consumable by the upp-audit CLI. See Viewing Keys & Key Hierarchy and Audit & Compliance.
upp.approve(params)
Approve the UPP contract to spend tokens (required before shield).
await upp.approve({
token: Address,
amount: bigint,
})Transaction Encoding
All pool operations use struct-based parameters internally. The SDK provides encode*Tx helpers that map hook build data to viem-compatible { abi, functionName, args } objects, so your app never touches ABI encoding:
import { encodeTransferTxFromBuildData } from '@permissionless-technologies/upp-sdk'
const buildData = await upp.transfer({ amount, to })
await writeContractAsync({
address: poolAddress,
...encodeTransferTxFromBuildData(buildData),
})See the Fee System page for the full list of encoding helpers.
Note Type
interface Note {
amount: bigint
token: Address
commitment: Hex
nullifier: Hex
leafIndex: number
spent: boolean
blinding: bigint
origin: Address
sender: Address
}