UPP — Universal Private PoolSDK Reference
React Hooks
UPPAccountProvider and useUPPAccount — React integration for the UPP SDK.
React Hooks
# React hooks are included in the main package
npm install @permissionless-technologies/upp-sdkUPPAccountProvider
Wrap your app with the provider to make UPP state available to all components:
import { UPPAccountProvider } from '@permissionless-technologies/upp-sdk/react'
function App() {
return (
<UPPAccountProvider>
<YourApp />
</UPPAccountProvider>
)
}The provider handles key derivation, note scanning, and client lifecycle. It requires a connected wagmi wallet.
useUPPAccount
import { useUPPAccount } from '@permissionless-technologies/upp-sdk/react'
function PrivateBalance() {
const {
notes, // Note[] | undefined
isScanning, // boolean
isReady, // boolean — keys derived, client initialized
shield, // (params) => Promise<{commitment, txHash}>
transfer, // (params) => Promise<{...}>
withdraw, // (params) => Promise<{txHash}>
merge, // (params) => Promise<{commitment, txHash}>
scan, // () => Promise<Note[]>
stealthAddress, // string | undefined — your 0zk1... address
} = useUPPAccount()
if (!isReady) return <div>Connecting...</div>
if (isScanning) return <div>Scanning notes...</div>
const balance = notes
?.filter(n => !n.spent)
.reduce((sum, n) => sum + n.amount, 0n) ?? 0n
return (
<div>
<p>Private balance: {balance.toString()}</p>
<button onClick={() => shield({ token: UPD_ADDRESS, amount: 100n * 10n**18n })}>
Shield 100 UPD
</button>
</div>
)
}Full Example
import { UPPAccountProvider, useUPPAccount } from '@permissionless-technologies/upp-sdk/react'
import { parseUnits, formatUnits } from 'viem'
const UPD_ADDRESS = '0x...'
function ShieldForm() {
const { shield, isReady } = useUPPAccount()
const handleShield = async () => {
try {
const { txHash } = await shield({
token: UPD_ADDRESS,
amount: parseUnits('100', 18),
})
console.log('Shielded:', txHash)
} catch (e) {
console.error(e)
}
}
return (
<button disabled={!isReady} onClick={handleShield}>
Shield 100 UPD
</button>
)
}
function TransferForm() {
const { transfer } = useUPPAccount()
const handleTransfer = async (recipientAddress: string) => {
const { txHash } = await transfer({
amount: parseUnits('50', 18),
to: recipientAddress, // 0zk1... stealth meta-address
})
console.log('Transferred:', txHash)
}
return (
<input
placeholder="0zk1... recipient address"
onKeyDown={e => e.key === 'Enter' && handleTransfer(e.currentTarget.value)}
/>
)
}
export function App() {
return (
<UPPAccountProvider>
<ShieldForm />
<TransferForm />
</UPPAccountProvider>
)
}useCircuitCache
Manage circuit artifact downloads and IndexedDB caching. Circuit proving keys are large (148–709 MB) and are downloaded lazily on first use, then cached persistently.
import { useCircuitCache } from '@permissionless-technologies/upp-sdk/react'
function CircuitManager() {
const {
isCached, // (circuit) => boolean
preload, // (circuit) => Promise<void>
downloadProgress, // DownloadProgress | null
isDownloading, // boolean
status, // Map<UPPCircuitType, CircuitCacheStatus>
evict, // (circuit) => Promise<void>
evictAll, // () => Promise<void>
version, // string — current CIRCUIT_VERSION
} = useCircuitCache()
return (
<div>
<h3>Circuit Cache (v{version})</h3>
<button
onClick={() => preload('transfer')}
disabled={isCached('transfer') || isDownloading}
>
{isCached('transfer') ? 'Transfer ✓' : 'Download Transfer (148 MB)'}
</button>
{downloadProgress && (
<p>
Downloading {downloadProgress.circuitType}.{downloadProgress.artifact}:
{' '}{downloadProgress.percent}%
</p>
)}
</div>
)
}Proof hooks (usePoolTransfer, useWithdraw, useSwap) automatically use the cache — if the circuit is already cached, proof generation starts instantly without re-downloading.