UPP — Universal Private PoolSDK Reference

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'
})
ReturnsTypeDescription
commitmentHexOn-chain note commitment
txHashHexTransaction 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 specific notes (for auditors).

const auditExport = await upp.exportViewingKeys({
  notes: [note1, note2],
})
// Send auditExport to auditor — they can decrypt these notes only

upp.approve(params)

Approve the UPP contract to spend tokens (required before shield).

await upp.approve({
  token: Address,
  amount: bigint,
})

Note Type

interface Note {
  amount: bigint
  token: Address
  commitment: Hex
  nullifier: Hex
  leafIndex: number
  spent: boolean
  blinding: bigint
  origin: Address
  sender: Address
}

On this page