> For the complete documentation index, see [llms.txt](https://docs.common.fi/knowledge-base/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.common.fi/knowledge-base/shielder-sdk/quickstart.md).

# Quickstart

### 1. Create a React + Typescript Project

Let's create a simple vite project with React and Typescript:

```bash
npm create vite@latest sdk-test -- --template react-ts
```

### 2. Install Dependencies

Install `shielder-sdk` and additional dependencies:

```bash
npm install @cardinal-cryptography/shielder-sdk@0.2.0-beta.10
npm install @cardinal-cryptography/shielder-sdk-crypto@0.2.0-beta.4
npm install @cardinal-cryptography/shielder-sdk-crypto-wasm@0.2.0-beta.4
npm install viem @types/node @vitejs/plugin-react
```

### 3. Configure Vite

Add following code to `vite.config.ts` :

{% code title="vite.config.ts" %}

```typescript
import * as path from "path";
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

function crossOriginIsolationMiddleware(_, res, next) {
  res.setHeader("Cross-Origin-Opener-Policy", "same-origin");
  res.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
  next();
}

const setCors = () => ({
  name: "configure-server",
  configureServer: (server) => {
    server.middlewares.use(crossOriginIsolationMiddleware);
  },
  configurePreviewServer: (server) => {
    server.middlewares.use(crossOriginIsolationMiddleware);
  },
});

// https://vite.dev/config/
export default defineConfig({
  plugins: [react(), setCors()],
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
  optimizeDeps: {
    exclude: ["@cardinal-cryptography/shielder-sdk-crypto-wasm"],
  },
});

```

{% endcode %}

### 4. Set Up WASM Cryptography Client

Create a file `src/shielderWasm.ts` , where we'll set up the wasm engine loading&#x20;

{% code title="src/shielderWasm.ts" %}

```typescript
import newAccountParamsUrl from "@cardinal-cryptography/shielder-sdk-crypto-wasm/keys/new_account/params.bin?url";
import newAccountPkUrl from "@cardinal-cryptography/shielder-sdk-crypto-wasm/keys/new_account/pk.bin?url";
import depositParamsUrl from "@cardinal-cryptography/shielder-sdk-crypto-wasm/keys/deposit/params.bin?url";
import depositPkUrl from "@cardinal-cryptography/shielder-sdk-crypto-wasm/keys/deposit/pk.bin?url";
import withdrawParamsUrl from "@cardinal-cryptography/shielder-sdk-crypto-wasm/keys/withdraw/params.bin?url";
import withdrawPkUrl from "@cardinal-cryptography/shielder-sdk-crypto-wasm/keys/withdraw/pk.bin?url";
import { initWasmWorker } from "@cardinal-cryptography/shielder-sdk-crypto-wasm";

// Helper function to fetch proving/verifying keys
async function fetchArrayBuffer(url: string): Promise<Uint8Array> {
  return fetch(url)
    .then((r) => r.arrayBuffer())
    .then((b) => new Uint8Array(b));
}

// Lazy-loaded WASM crypto client
export const wasmCryptoClientRead = (async () => {
  const newAccountParams = await fetchArrayBuffer(newAccountParamsUrl);
  const newAccountPk = await fetchArrayBuffer(newAccountPkUrl);
  const depositParams = await fetchArrayBuffer(depositParamsUrl);
  const depositPk = await fetchArrayBuffer(depositPkUrl);
  const withdrawParams = await fetchArrayBuffer(withdrawParamsUrl);
  const withdrawPk = await fetchArrayBuffer(withdrawPkUrl);
  return initWasmWorker(
    navigator.hardwareConcurrency,
    {
      paramsBuf: newAccountParams,
      pkBuf: newAccountPk,
    },
    {
      paramsBuf: depositParams,
      pkBuf: depositPk,
    },
    {
      paramsBuf: withdrawParams,
      pkBuf: withdrawPk,
    }
  );
})();

```

{% endcode %}

### 5. Initialize the Shielder SDK Client

Create `src/shielder.ts:`

{% code title="src/shielder.ts:" %}

```typescript
import {
  createShielderClient,
  type ShielderOperation,
} from "@cardinal-cryptography/shielder-sdk";
import { createPublicClient, http } from "viem";
import { baseSepolia } from "viem/chains";
import { wasmCryptoClientRead } from "./shielderWasm";

const chain = baseSepolia;

const shielderContractAddress =
  "0x2098a5f59DAB63F1a2aB7C0715DA437D1efB012B" as `0x${string}`;

const relayerUrl = "https://shielder-relayer-v2.test.azero.dev/base-testnet";

export const publicClient = createPublicClient({
  chain,
  transport: http(),
});

// Simple in-memory key-value storage
const shielderStorage: Map<string, string> = new Map();

// 66-character (0x prefix + 64 hex symbols) of account private key
const shieldedAccountPrivateKey = "0x..." as `0x${string}`;

export async function initializeShielderClient() {
  return createShielderClient({
    shielderSeedPrivateKey,
    chainId: BigInt(chain.id),
    // Note: cast publicClient to 'any' for compatibility
    publicClient: publicClient as any,
    contractAddress: shielderContractAddress,
    relayerUrl,
    storage: {
      getItem: async (key: string) => {
        return shielderStorage.get(key) || null;
      },
      setItem: async (key: string, value: string) => {
        shielderStorage.set(key, value);
      },
    },
    cryptoClient: await wasmCryptoClientRead,
    callbacks: {
      onAccountNotOnChain: async (
        error: unknown,
        stage: string,
        operation: ShielderOperation
      ) => {
        console.error("Account not on chain:", error, stage, operation);
      },
      onSdkOutdated: async (
        error: unknown,
        stage: string,
        operation: ShielderOperation
      ) => {
        console.error("SDK outdated:", error, stage, operation);
      },
    },
  });
}
```

{% endcode %}

### 6. Verify the Setup

In your main file (e.g. `src/main.tsx` or `src/App.tsx`):

```typescript
import { initializeShielderClient } from "./shielder";
import { nativeToken } from "@cardinal-cryptography/shielder-sdk";

(async () => {
  // Initialize the SDK client
  const shielder = await initializeShielderClient();
  // Sync the account state from chain
  await shielder.syncShielder();
  
  // Get the native token representation
  const token = nativeToken();
  
  // Query your current account state (balance, nonce.)
  const accountState = await shielder.accountState(token);

  // Log the result to console (should be null at this point)
  console.log("Account:", accountState);
})();
```

At this point, your app is connected to the Shielder network, has synced the private account, and can query its state.<br>

Next steps:

* To shield tokens (i.e. deposit), see: [Shielding Tokens](/knowledge-base/shielding-tokens.md)
* To withdraw tokens back to a public address, see: [Withdrawing Tokens](/knowledge-base/withdrawing-tokens.md)

Both operations require working with token approvals, gas fees, and relayer coordination.

We recommend you handle those through user-facing components in your app UI.

Explore the guides to implement full privacy flow.
