Skip to main content
Version: develop

Actions & Extensions

The SDK uses viem's .extend() pattern to add ZKP capabilities to standard viem clients.

What's an action extension?

A function that takes a config and returns a viem extension -- a function that adds methods to a client:

import { createPublicClient, http } from 'viem'
import { zkpPublicActions } from '@cardinal-cryptography/core'

const client = createPublicClient({ chain, transport: http() })
.extend(zkpPublicActions())

// Now client has ZKP read methods
const ct = await client.getEncryptedBalanceCiphertext({ token: 'zkUSD', zkpAddress: '...' })

The five extensions

Read actions don't need keys -- anyone can query the chain:

ExtensionWhat it adds
zkpPublicActions({ tokens? })On-chain reads: encrypted balances, event logs, quorum keys, nonces

Write actions need a wallet to sign EVM transactions:

ExtensionWhat it adds
zkpEvmActions({ tokens?, account? })EVM writes: register ZK address, send to encrypted address, auto-encrypt. account is required on the bundler path (signs EIP-712 permits); the WalletClient path uses client.account implicitly.
zkpPreparedActions()Submit pre-signed transactions (routes to wallet or bundler automatically)

Decrypt and prove actions need your keys to process encrypted data locally:

ExtensionWhat it adds
zkpDecryptActions({ zkpAccount })Decrypt balance + history using your encryption key
zkpWalletActions({ zkpAccount })Prepare/sign/send encrypted transfers (needs full account)

You won't use all five directly -- the client factories pick the right combination for you.

Extension ordering

Extensions that depend on methods from other extensions must come later in the chain:

client
.extend(zkpPublicActions()) // 1. reads (no dependencies)
.extend(zkpEvmActions()) // 2. EVM writes (no dependencies)
.extend(zkpPreparedActions()) // 3. sendPreparedTransaction
.extend(zkpDecryptActions(...)) // 4. needs public read methods from step 1
.extend(zkpWalletActions(...)) // 5. needs reads from 1 + sendPrepared from 3

The client factories (createEvmClient, createDecryptClient, createFullClient) handle this ordering for you.

Custom token configuration

By default, token names like 'zkUSD' resolve to built-in contract addresses per chain. Override with custom addresses:

const client = createEvmClient({
chain,
transport: http(),
account,
tokens: { zkUSD: '0xMyCustomTokenAddress...' },
})

The tokens config is forwarded to zkpPublicActions and zkpEvmActions.