Skip to main content

ZkpClient

Shared chain and proving pipeline for all wallets. Create one via a factory, then call wallet() to get a [ZkpWallet](../zkp-wallet/).

const client = createWebClient({ chainId: 8453 });
await client.preload(); // optional — eagerly load solver + prover
const wallet = client.wallet({ spendingKey, walletClient });

wallet(config)

Creates a ZkpWallet connected to a specific account.

// External wallet (MetaMask, WalletConnect, etc.)
const wallet = client.wallet({ spendingKey, walletClient });

// Local account (mobile, server, embedded wallet)
const wallet = client.wallet({ spendingKey, account });

One client can create multiple wallets for different accounts. Each wallet holds its own spending key and signing context but shares the client's solver, prover, and chain connection.

Lifecycle

MethodRole
preload(asset?)Eagerly load solver and/or prover
prewarmProver()Run a dummy proof to warm JIT
destroy()Teardown

preload(asset?)

Both solver (discrete-log decryption) and prover (ZK proof generation) are WASM modules that load lazily on first use. preload() lets you trigger that initialization early — e.g., while showing a loading screen.

  • preload() — loads both solver and prover in parallel
  • preload('solver') — solver only (enough for reading balances)
  • preload('prover') — prover only (enough for generating proofs)

If you skip preload(), the first getBalance() call will initialize the solver, and the first transfer() call will initialize the prover. The user just waits a bit longer on that first operation.

prewarmProver()

The first proof is always slower due to WASM JIT compilation, memory allocation, and backend setup. prewarmProver() runs a dummy proof so that cost is paid upfront rather than on the user's first real transfer().

Useful when you have idle time between client creation and the first transaction — e.g., the user is filling out a form, browsing balances, or waiting on a UI flow. Fire it in the background so the first real proof feels instant.

If the user immediately calls transfer() after preload(), prewarming is pointless — the first real proof pays the JIT cost anyway.

Note: This is WASM-specific. Native prover backends (e.g. MoPro on mobile) don't have JIT warmup — prewarmProver() is a no-op in those environments.

destroy()

Tears down the solver and prover backends, freeing WASM memory. After destroy(), all wallets created from this client become invalid — the app is responsible for discarding them. Calling destroy() twice is safe (idempotent).

The client is a composition root — it wires services together and hands them to wallets. Token configuration and chain details are accessed through the wallet, not the client directly.

For direct construction without a factory (custom backends, native platforms) → Advanced: ZkpClient internals.