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-sdk

UPPAccountProvider

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.

On this page