Privacy

SDK Quickstart

Integrate UPP privacy into your app

Private Preview

The UPP SDK is in private preview. Join our Telegram for access.

Installation

# Coming soon - contact us for early access
npm install [coming soon]

Setup

import { createUPPClient } from '@upd/upp-sdk'
import { createWalletClient, http } from 'viem'
import { sepolia } from 'viem/chains'

// 1. Create wallet client
const walletClient = createWalletClient({
  chain: sepolia,
  transport: http(),
})

// 2. Initialize UPP client
const upp = await createUPPClient({
  walletClient,
  // SDK will prompt for signature to derive keys
})

// Keys are derived from wallet signature - no extra seed phrases

Core Operations

Shield (Deposit)

Move public tokens into the private pool:

// Approve UPP to spend your tokens
await upp.approve({ token: UPD_ADDRESS, amount: 1000n * 10n**18n })

// Shield tokens - creates an encrypted note
const { commitment } = await upp.shield({
  token: UPD_ADDRESS,
  amount: 1000n * 10n**18n,
})

console.log('Shielded! Note commitment:', commitment)

Transfer (Private Send)

Send to another user within the pool:

// Get recipient's stealth meta-address (they share this publicly)
const recipientAddress = '0zk1...'

// Transfer - proves you own the note without revealing which one
const { recipientCommitment, changeCommitment } = await upp.transfer({
  amount: 500n * 10n**18n,
  to: recipientAddress,
})

// Recipient gets 500 UPD, you get change note

Check Balance

Scan for your notes and compute balance:

// Scans encrypted notes, decrypts yours
const notes = await upp.scan()

const balance = notes
  .filter(n => n.token === UPD_ADDRESS && !n.spent)
  .reduce((sum, n) => sum + n.amount, 0n)

console.log('Private balance:', balance)

Unshield (Withdraw)

Move back to public:

const { txHash } = await upp.unshield({
  amount: 200n * 10n**18n,
  to: '0x...recipient',
  aspId: 1, // Which ASP to use for compliance proof
})

React Integration

import { UPPProvider, useUPP } from '@upd/upp-sdk/react'

function App() {
  return (
    <UPPProvider>
      <PrivateBalance />
    </UPPProvider>
  )
}

function PrivateBalance() {
  const { balance, isScanning, shield, transfer } = useUPP()

  if (isScanning) return <div>Scanning notes...</div>

  return (
    <div>
      <p>Private balance: {formatUnits(balance, 18)} UPD</p>
      <button onClick={() => shield({ amount: parseUnits('100', 18) })}>
        Shield 100 UPD
      </button>
    </div>
  )
}

Key Concepts

Stealth Addresses

Recipients share a stealth meta-address (0zk1...). Senders derive unique one-time addresses for each payment. Even the recipient's public address doesn't appear on-chain.

// Generate your stealth meta-address to receive payments
const myStealthAddress = upp.getStealthMetaAddress()
// Share this publicly - senders use it to create unlinkable payments

Viewing Keys

Export viewing keys for auditors:

const auditExport = await upp.exportViewingKeys({
  notes: [note1, note2], // Specific notes to share
})

// Send auditExport to auditor - they can decrypt these notes only

Note Management

Each shield/transfer creates notes (encrypted UTXOs). The SDK tracks these automatically:

const notes = await upp.getNotes()

// Each note has:
// - amount: token amount
// - token: which token
// - commitment: on-chain identifier
// - spent: whether it's been used

Error Handling

try {
  await upp.transfer({ amount, to })
} catch (e) {
  if (e.code === 'INSUFFICIENT_BALANCE') {
    // Not enough private balance
  } else if (e.code === 'ASP_PROOF_FAILED') {
    // Origin not in ASP allowlist
  } else if (e.code === 'PROOF_GENERATION_FAILED') {
    // ZK proof failed - check inputs
  }
}

Next Steps

On this page