Demo Project
A complete example project demonstrating how to use the Privacy Cash EVM SDK in a Next.js frontend:
https://github.com/Privacy-Cash/base-sdk-demo-interface
Installation
npm install privacycash-evm wagmi viem @rainbow-me/rainbowkit @tanstack/react-query --save
Requires Node.js 20+. The SDK is written in TypeScript and includes type definitions.
Wallet Provider Setup
Wrap your app with Wagmi and RainbowKit providers configured for the supported EVM mainnets:
'use client'
import { RainbowKitProvider } from '@rainbow-me/rainbowkit'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { WagmiProvider, createConfig, http } from 'wagmi'
import { base, mainnet } from 'wagmi/chains'
import { walletConnect, coinbaseWallet } from 'wagmi/connectors'
const config = createConfig({
ssr: true,
chains: [base, mainnet],
connectors: [
coinbaseWallet({ appName: 'Your App' }),
walletConnect({ projectId: process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID! }),
],
transports: {
[base.id]: http(process.env.NEXT_PUBLIC_BASE_RPC_URL),
[mainnet.id]: http(process.env.NEXT_PUBLIC_ETH_RPC_URL),
},
})
const queryClient = new QueryClient()
export function Providers({ children }: { children: React.ReactNode }) {
return (
<QueryClientProvider client={queryClient}>
<WagmiProvider config={config}>
<RainbowKitProvider initialChain={base}>
{children}
</RainbowKitProvider>
</WagmiProvider>
</QueryClientProvider>
)
}
Deriving Encryption Key
Before a user can interact with Privacy Cash, they must sign an off-chain message. This signature is used to derive their private encryption key and UTXO keypair — neither key ever leaves the client.
import { useSignMessage } from 'wagmi'
const SIGN_MESSAGE = 'Privacy Money account sign in'
const LS_KEY_PREFIX = 'evm_sign_'
function saveSignature(address: string, signature: string) {
localStorage.setItem(`${LS_KEY_PREFIX}${address}`, signature)
}
function getStoredSignature(address: string): string | null {
return localStorage.getItem(`${LS_KEY_PREFIX}${address}`)
}
// In your component:
const { signMessage } = useSignMessage()
const { address } = useAccount()
function handleSignIn() {
signMessage(
{ message: SIGN_MESSAGE },
{
onSuccess(signature) {
saveSignature(address!, signature)
},
}
)
}
Auto-prompt the user to sign when they connect their wallet:
useEffect(() => {
if (!isConnected || !address) return
const stored = getStoredSignature(address)
if (!stored) handleSignIn()
}, [isConnected, address])
Circuit Key File
Place the circuit2.zkey file in your Next.js public/ folder. Pass the base path (without extension) to each SDK function:
public/
circuit2.zkey ← download from the SDK repo
keyBasePath: '/circuit' // resolves to /circuit2.zkey at runtime
Selecting a Network
Pass the active EVM network to SDK calls. Base is the default when network is omitted, but explicit selection keeps multi-chain apps predictable.
import { BASE_NETWORK, ETH_NETWORK, getBalance } from 'privacycash-evm'
import { useChainId } from 'wagmi'
function BalanceLoader({ signature, address }: { signature: string, address: string }) {
const chainId = useChainId()
const network = chainId === 1 ? ETH_NETWORK : chainId === 8453 ? BASE_NETWORK : undefined
async function refreshBalance() {
if (!network) throw new Error('Switch to Base or Ethereum mainnet.')
return getBalance({
signature,
address,
network,
})
}
return null // Render your balance UI here.
}
Common Issues
- Signature re-prompt on every page refresh — Store the signature in
localStorage keyed by address as shown above.
walletClient temporarily undefined — Use connector.getProvider() as a fallback when useWalletClient() is unavailable during initial mount.
- Wrong chain data — Pass
network: BASE_NETWORK or network: ETH_NETWORK to each SDK call after checking the connected wallet chain.