Skip to main content
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-query

Set up the provider

TypeScript
// 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:
TypeScript
// 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>
  );
}

Read-only mode

Use readonlyWallet when the user isn’t connected yet. It lets fetch hooks run without a signer - mutation hooks will throw if called.
TypeScript
import { readonlyWallet } from "@cypher-zk/sdk";

const client = new CypherClient({
  connection,
  wallet: readonlyWallet(somePublicKey),
  cluster: "devnet",
});

Access the client directly

Inside any component, useCypherClient() returns the client from context:
TypeScript
import { useCypherClient } from "@cypher-zk/sdk/react";

function MyComponent() {
  const client = useCypherClient();
  // client.markets.all(), client.actions.placeBet(...), etc.
}
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.

What’s next