@permissionless-technologies/upp-sdk
v0.9.2
Published
Universal Private Pool SDK - Privacy-preserving token operations for any ERC20
Maintainers
Readme
Universal Private Pool (UPP) SDK
Privacy-preserving token operations for any ERC20 token.
Features
- Multi-token support — Shield any ERC20 into a single privacy pool
- Large anonymity set — All token users share one Merkle tree
- Compliance-ready — Pluggable ASP (Association Set Provider) with ragequit fallback
- Pluggable architecture — Bring your own account adapter, ASP provider, storage backend
- SNARK + STARK — Dual proof system: PLONK/BLS12-381 (128-bit, EIP-2537 precompiles) + Circle STARK (post-quantum)
- Web Worker proving — Off-main-thread proof generation keeps UI responsive
- Modern stack — Built on viem, TypeScript strict mode, ESM-first
Installation
npm install @permissionless-technologies/upp-sdk viemSubpath Exports
// Everything
import { ... } from '@permissionless-technologies/upp-sdk'
// Core: notes, proofs, stealth addresses, transfers
import { ... } from '@permissionless-technologies/upp-sdk/core'
// Keys: derivation, viewing keys
import { ... } from '@permissionless-technologies/upp-sdk/keys'
// React hooks (requires React 18+ and wagmi)
import { ... } from '@permissionless-technologies/upp-sdk/react'
// Indexer: note scanning, storage adapters
import { ... } from '@permissionless-technologies/upp-sdk/indexer'
// Utilities: poseidon (BLS12-381), merkle trees
import { ... } from '@permissionless-technologies/upp-sdk/utils'
// Web Worker for off-main-thread proof generation
import '@permissionless-technologies/upp-sdk/worker'Quick Start
React Application
import { UPPAccountProvider, useUPPAccount, useShield } from '@permissionless-technologies/upp-sdk/react'
import { getDeployment } from '@permissionless-technologies/upp-sdk'
const deployment = getDeployment(11155111) // Sepolia
function App() {
return (
<UPPAccountProvider chainId={11155111} ethAddress={address}>
<ShieldForm />
</UPPAccountProvider>
)
}Standalone (Node.js / Custom Frontend)
import {
getDeployment,
registerDeployment,
NoteStore,
DirectAccountAdapter,
} from '@permissionless-technologies/upp-sdk'
import { makeRpcIndexer, createAutoAdapter } from '@permissionless-technologies/upp-sdk/indexer'
// Load deployment addresses
const deployment = getDeployment(11155111)
// Or register your own chain
registerDeployment(8453, {
UniversalPrivatePool: '0x...',
ASPRegistryHub: '0x...',
verifiers: { TransferVerifier: '0x...', MergeVerifier: '0x...', WithdrawVerifier: '0x...' },
chainId: 8453,
deployBlock: 12345678,
})
// Note storage (pluggable: IndexedDB, localStorage, memory, custom)
const storage = createAutoAdapter('my-app')
const noteStore = new NoteStore(storage)
await noteStore.load()Architecture
@permissionless-technologies/upp-sdk
├── core/ # Notes, proofs, stealth addresses, transfers
├── keys/ # Key derivation (signature, direct, custom)
├── react/ # React hooks (useUPPAccount, useShield, etc.)
├── indexer/ # Note scanning + pluggable storage
├── utils/ # Poseidon (BLS12-381), Merkle trees
├── contracts/ # ABIs + Solidity interfaces
└── deployments/ # Per-chain contract addressesPluggable Interfaces
| Interface | Purpose | Built-in |
|-----------|---------|----------|
| IAccountAdapter | Key derivation source | DirectAccountAdapter, signature-based |
| IASPProvider | Compliance layer | Optional (ragequit always available) |
| StorageAdapter | Note persistence | IndexedDB, localStorage, memory |
| NoteStore | Note state management | Dedup, balance, status tracking |
Contract ABIs
import {
UNIVERSAL_PRIVATE_POOL_ABI,
ASP_REGISTRY_HUB_ABI,
TEST_STABLE_TOKEN_ABI,
} from '@permissionless-technologies/upp-sdk'Solidity interfaces are included for integration:
node_modules/@permissionless-technologies/upp-sdk/src/contracts/interfaces/
├── IUniversalPrivatePool.sol
├── IASPRegistry.sol
└── IVerifiers.solDeployments
| Chain | Chain ID | Status | |-------|----------|--------| | Anvil (local) | 31337 | Built-in | | Sepolia | 11155111 | Built-in |
Custom chains: use registerDeployment() at app initialization.
Local Development
npm install # Install dependencies
npm run build # Build (ESM + CJS)
npm run dev # Watch mode
npm test # Run tests (176 passing)
npm run deploy:local # Deploy to local AnvilCircuit Build & Deploy Pipeline
After changing circuits (.circom files), the full pipeline is:
# 1. Compile circuits (circom → wasm + r1cs)
npm run circuits:compile
# 2. Trusted setup (r1cs → zkey + vkey.json, per-circuit Powers of Tau)
npm run circuits:setup
# 3. Generate verifier info + VKeyDeployers.sol (vkey.json → Solidity deployer library)
npm run circuits:verifiers
# 4. Deploy contracts (Forge deploy + extract addresses)
npm run deploy:sepolia
# 5. Rebuild SDK (picks up new deployment addresses)
npm run buildSteps must run in order. circuits:verifiers generates VKeyDeployers.sol which
contains the verification keys baked into the on-chain verifiers. If you deploy without
regenerating this file, the verifiers will have stale keys and proofs will fail with
"Invalid proof".
Circuit artifacts (.wasm + .zkey) are hosted on a CDN at circuits.upd.io/{version}/.
The SDK caches them in IndexedDB after first download. See CIRCUIT_VERSION in
src/core/circuit-cache.ts — bump this when uploading new artifacts.
