The React integration lives in @cypher-zk/sdk/react. It wraps a CypherClient instance in context so every component can call hooks without passing the client around manually.
Install dependencies
bun add @cypher-zk/sdk @solana/web3.js @tanstack/react-querynpm install @cypher-zk/sdk @solana/web3.js @tanstack/react-queryyarn add @cypher-zk/sdk @solana/web3.js @tanstack/react-querypnpm add @cypher-zk/sdk @solana/web3.js @tanstack/react-querySet up the provider
Next.js app router
// src/providers/cypher-provider.tsx
"use client";
import { useMemo } from "react";
import { Connection } from "@solana/web3.js";
import { CypherClient, readonlyWallet } from "@cypher-zk/sdk";
import { CypherProvider as BaseCypherProvider } from "@cypher-zk/sdk/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { useWallet } from "@solana/wallet-adapter-react";
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
retry: false,
},
},
});
export function CypherProvider({ children }: { children: React.ReactNode }) {
const { wallet, publicKey, signTransaction, signAllTransactions } = useWallet();
const client = useMemo(() => {
const connection = new Connection(
process.env.NEXT_PUBLIC_RPC_URL!,
"confirmed"
);
const w =
wallet && publicKey && signTransaction && signAllTransactions
? { publicKey, signTransaction, signAllTransactions }
: readonlyWallet(publicKey ?? /* fallback pk */);
return new CypherClient({ connection, wallet: w, cluster: "devnet" });
}, [wallet, publicKey, signTransaction, signAllTransactions]);
return (
<QueryClientProvider client={queryClient}>
<BaseCypherProvider client={client}>
{children}
</BaseCypherProvider>
</QueryClientProvider>
);
}Add to your root layout:
// src/app/layout.tsx
import "@/lib/buffer-polyfill";
import { CypherProvider } from "@/providers/cypher-provider";
export default function RootLayout({ children }) {
return (
<html>
<body>
<CypherProvider>{children}</CypherProvider>
</body>
</html>
);
}Vite / CRA
// src/main.tsx
import { Connection } from "@solana/web3.js";
import { CypherClient, keypairToWallet } from "@cypher-zk/sdk";
import { CypherProvider } from "@cypher-zk/sdk/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const connection = new Connection("https://api.devnet.solana.com", "confirmed");
const client = new CypherClient({
connection,
wallet: keypairToWallet(myKeypair), // or wallet adapter
cluster: "devnet",
});
const queryClient = new QueryClient({
defaultOptions: { queries: { refetchOnWindowFocus: false, retry: false } },
});
ReactDOM.createRoot(document.getElementById("root")!).render(
<QueryClientProvider client={queryClient}>
<CypherProvider client={client}>
<App />
</CypherProvider>
</QueryClientProvider>
);Read-only mode
Use readonlyWallet when the user isn't connected yet. It lets fetch hooks run without a signer. Mutation hooks throw a ReadonlyWalletError (with code === "READONLY_WALLET") so the UI can detect the case and show a "connect wallet" prompt instead of a generic error.
import { readonlyWallet } from "@cypher-zk/sdk";
const client = new CypherClient({
connection,
wallet: readonlyWallet(somePublicKey),
cluster: "devnet",
});See Errors → ReadonlyWalletError for the full type and handling pattern.
Tuning RPC throughput
The SDK batches every bulk read (client.markets.byIds, client.marketQuestions.fetchMany, useMarketQuestions, …) into chunks of ≤100 pubkeys with bounded concurrency and exponential-backoff retries. Defaults are conservative enough for free-tier RPCs. On a paid plan (Helius, Triton) you can raise concurrency for snappier list views:
const client = new CypherClient({
connection,
wallet,
cluster: "devnet",
rpcOptions: { concurrency: 10 }, // default 5
});See CypherClientOptions.rpcOptions for the full shape.
Access the client directly
Inside any component, useCypherClient() returns the client from context:
import { useCypherClient } from "@cypher-zk/sdk/react";
function MyComponent() {
const client = useCypherClient();
// client.markets.all(), client.actions.placeBet(...), etc.
}Note:
useCypherClient()throws if called outside<CypherProvider>. Make sure the hook is inside the provider tree.
Building with AI
If you use an AI coding agent (Claude, Cursor, etc.), the cypher-skill gives it deep context about the SDK - provider wiring, hook patterns, encryption flows, and common pitfalls.
Add cypher-skill to your project - covers CypherProvider setup, all React hooks, onProgress patterns, x25519 secret persistence, and market phase gating.
npx skills add cypher-zk/cypher-skill
What's next
- Query hooks - fetch markets, positions, global state
- Mutation hooks - place bets, claim, resolve