Skip to main content
Version: 0.3.0

Action Extensions

Viem action extensions that add ZKP capabilities to clients.

zkpPublicActions

On-chain read operations. Works with any PublicClient.

import { zkpPublicActions } from '@cardinal-cryptography/core'
client.extend(zkpPublicActions({ tokens?: TokenMap }))
MethodParametersReturnsDescription
getEncryptedBalanceCiphertext{ token, zkpAddress }ElGamalCiphertextRead encrypted balance
getAutoEncryptZkpAddress{ token, address }ZkpAddress | nullReverse lookup: EVM address to ZK address
getQuorumPublicKey{ token }GrumpkinPublicKeyQuorum encryption public key
getPermitNonce{ token, owner }bigintNext permit nonce for controller auth
getAutoEncrypt{ token, address }booleanIs auto-encrypt enabled?
getEncryptedBalanceLogs{ token, zkpAddress, fromBlock, toBlock?, events? }EncryptedBalanceLog[]Raw event history. fromBlock is required; caller paginates with successive [fromBlock, toBlock] windows to stay under hosted-RPC block-range caps.
getTokenName{ token }stringToken contract name (for EIP-712 domain)

zkpEvmActions

EVM write transactions. Requires WalletClient or BundlerLike.

import { zkpEvmActions } from '@cardinal-cryptography/core'
client.extend(zkpEvmActions({ tokens?: TokenMap, account?: LocalAccount }))

account is required on the bundler path (and ignored on the WalletClient path, where viem's client.account plays the same role). It's used to sign EIP-712 permits for publicToEncryptedTransferWithAuth.

MethodParametersReturnsDescription
preparePublicToEncryptedTransfer{ token, amount, zkpAddress }PreparedTransactionPrepare public-to-encrypted transfer (plain variant, WalletClient path)
preparePublicToEncryptedTransferWithAuth{ token, amount, zkpAddress, owner, deadline, nonce? }UnsignedTransactionPrepare permit variant — returned shape needs signing before submission
prepareSetAutoEncrypt{ token, enabled, zkpAddress }PreparedTransactionPrepare auto-encrypt initial bind (sets the EPK and the enabled flag). Reverts on-chain if the address has already bound an EPK.
prepareToggleAutoEncrypt{ token, enabled }PreparedTransactionPrepare auto-encrypt on/off flip for an already-bound address. Does not change the bound EPK.
sendPublicToEncryptedTransfer{ token, amount, zkpAddress, account?, nonce?, deadline? }0x${string}Auto-routes: plain variant on WalletClient, WithAuth on bundler (prepare → sign → send under the hood)
sendSetAutoEncrypt{ token, enabled, zkpAddress, account?, nonce?, deadline? }0x${string}Auto-routes: plain variant on WalletClient, WithAuth on bundler. Initial bind only — reverts on-chain if the address has already bound an EPK. account?, nonce?, deadline? apply to the bundler path only.
sendToggleAutoEncrypt{ token, enabled, account?, nonce?, deadline? }0x${string}Auto-routes: plain variant on WalletClient, WithAuth on bundler. Flips the enabled flag for an already-bound address; on the bundler path the SDK reads the stored EPK from chain to construct the digest.
signAuthorizationUnsignedTransactionPreparedTransactionSign an EIP-712 permit with the bound account. Named distinctly from zkpWalletActions.signTransaction so both can coexist on a full client.
toSignedTransaction(UnsignedTransaction, HexSignature)PreparedTransactionAssemble a PreparedTransaction from an externally-produced signature (custody, HSM, etc.)

Permit prepare/sign/submit flow

publicToEncryptedTransferWithAuth takes an EIP-712 permit as its last argument. Use preparePublicToEncryptedTransferWithAuth (returns UnsignedTransaction) + signAuthorization + sendPreparedTransaction. For external signers (custody, Ledger, Fireblocks), use toSignedTransaction to wrap an externally-produced signature.

See Transaction Lifecycle — EVM transactions with an EIP-712 permit for the three-step code example.


erc3009Actions

EIP-3009 transferWithAuthorization — plain ERC-20 transfers via a recipient-bound off-chain authorization. The signed digest commits to to, so anyone (the SharedAccount bundler in practice) may forward the call without being able to redirect funds. Bundler-only — on a WalletClient call transfer directly.

import { erc3009Actions } from '@cardinal-cryptography/core'
client.extend(erc3009Actions({ tokens?: TokenMap, account?: LocalAccount }))

account is the EOA whose tokens are transferred — also the EIP-712 signer (from = account.address). Required for sendTransferWithAuthorization; prepare* and reads don't need it.

MethodParametersReturnsDescription
prepareTransferWithAuthorization{ token, from, to, value, validAfter?, validBefore?, nonce? }UnsignedTransactionBuild EIP-712 typed data + calldata. Caller signs typedData and assembles a PreparedTransaction via signTransaction / toSignedTransaction.
sendTransferWithAuthorization{ token, to, value, validAfter?, validBefore?, nonce? }0x${string}prepare → sign with the bound account → submit via the bundler.
getAuthorizationState{ token, authorizer, nonce }booleanHas (authorizer, nonce) been consumed (used or canceled)? Mirrors EIP-3009's authorizationState view.

Defaults: validAfter = 0n, validBefore = 20 min ahead, nonce is a fresh random bytes32.


zkpDecryptActions

Decrypt balances and history. Requires a client with zkpPublicActions methods.

import { zkpDecryptActions } from '@cardinal-cryptography/core'
client.extend(zkpDecryptActions({ zkpAccount: ZkpReadAccount }))
MethodParametersReturnsDescription
getDecryptedBalance{ token, zkpAccount? }bigintRead + decrypt balance
getDecryptedBalanceLogs{ token, fromBlock, toBlock?, events?, zkpAccount? }DecryptedBalanceLog[]Decrypt history. fromBlock is required (forwarded to getEncryptedBalanceLogs).

The optional zkpAccount parameter overrides the account from config (useful for multi-account views).


zkpWalletActions

Prepare, sign, and send encrypted transfers. Requires a client with zkpPublicActions + zkpPreparedActions methods.

import { zkpWalletActions } from '@cardinal-cryptography/core'
client.extend(zkpWalletActions({ zkpAccount: ZkpAccount }))
MethodParametersReturnsDescription
prepareEncryptedTransfer{ token, amount, to, deadline?, nonce?, onProgress?, zkpAccount? }UnsignedTransactionPrepare private transfer
prepareEncryptedToPublicTransfer{ token, amount, recipient, deadline?, nonce?, onProgress?, zkpAccount? }UnsignedTransactionPrepare encrypted-to-public transfer
sendEncryptedTransferSame as prepare0x${string}Full flow: prepare + sign + send
sendEncryptedToPublicTransferSame as prepare0x${string}Full flow: prepare + sign + send
prepareRegisterEpk{ token, controller, onProgress?, zkpAccount? }PreparedTransactionBind a controller EVM address to this account's EPK on the token contract. Required first transaction for every new ZKP account.
sendRegisterEpkSame as prepare0x${string}Full flow: prove + submit
signTransactionUnsignedTransactionPreparedTransactionSign with the controller key (ZKP actions). Permit signing lives under zkpEvmActions.signAuthorization.
getDecryptedBalance{ token, zkpAccount? }bigintInherited from decrypt actions
getDecryptedBalanceLogs{ token, ... }DecryptedBalanceLog[]Inherited from decrypt actions

zkpPreparedActions

Submit pre-signed transactions. Handles routing between WalletClient and BundlerClient.

import { zkpPreparedActions } from '@cardinal-cryptography/core'
client.extend(zkpPreparedActions())
MethodParametersReturnsDescription
sendPreparedTransactionPreparedTransaction0x${string}Submit a signed transaction