Skip to main content
Version: develop

ZK Privacy SDK

This SDK lets you add private token balances to EVM applications. Balances are encrypted on-chain -- only the owner can see amounts and transfer history. Transfers are verified by zero-knowledge proofs, so the chain can confirm validity without seeing the values.

Built on viem, the SDK is modular -- pick only the capabilities you need.

Building a mobile app? See React Native — same Solver / Prover contracts, mobile-native runtime via UniFFI + Expo Modules.

Three capability tiers

TierWhat you can doWhat you need
EVMSend tokens to encrypted addresses, register, auto-encryptWallet client
DecryptRead + decrypt balances and history+ Runtime (decryption) + read-only account
FullAll of the above + encrypted transfers+ Runtime (decryption + proving) + full account

You create one runtime that bundles the services you need: a decryption service for reading balances, and optionally a proving service for generating ZK proofs when sending transfers. Everything runs client-side -- no private data leaves your machine.

Why tiers matter

The decryption and proving services are WASM modules that lazy-load when first used. Picking the minimal tier for your use case keeps your app's bundle small:

  • EVM tier -- no WASM at all, pure RPC calls
  • Decrypt tier -- loads the decryption WASM (~small)
  • Full tier -- loads both decryption and proving WASM (~larger, but lazy-loaded on first use so it doesn't block your app startup)

If you're building a portfolio viewer, use Decrypt. If you're building an onboarding page, use EVM. Only load the prover when you actually need to send encrypted transfers.

Each tier maps to a client factory:

import { createEvmClient, createDecryptClient, createFullClient } from '@cardinal-cryptography/sdk'

Supported networks

The SDK ships with deployment defaults baked in — token addresses, bundler URL, paymaster URL, and the SharedAccount address all resolve automatically when you pass a supported viem chain. See Live Deployments for the canonical list of networks, chain IDs, and addresses.

Quick example

import { createRuntime, createFullClient } from '@cardinal-cryptography/sdk'
import { createPublicClient, http } from 'viem'
import { baseSepolia } from 'viem/chains'

const runtime = await createRuntime()
const account = runtime.createAccountFromMnemonic('your mnemonic here')

const client = await createFullClient({
client: createPublicClient({ chain: baseSepolia, transport: http() }),
zkpAccount: account,
})

// Check your encrypted balance
const balance = await client.getDecryptedBalance({ token: 'zkUSD' })

// Send a private transfer
await client.sendEncryptedTransfer({
token: 'zkUSD',
amount: 50n,
to: 'zk1recipientAddress...',
})

Next steps