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 Base mainnet:
'use client'
import { RainbowKitProvider } from '@rainbow-me/rainbowkit'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { WagmiProvider, createConfig, http } from 'wagmi'
import { base } from 'wagmi/chains'
import { walletConnect, coinbaseWallet } from 'wagmi/connectors'
const config = createConfig({
ssr: true,
chains: [base],
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),
},
})
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
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.