> ## Documentation Index
> Fetch the complete documentation index at: https://developers.paxoslabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> Spin up a React + Vite starter that deposits and withdraws with Privy wallets

This walkthrough bootstraps a Vite + React project that authenticates users with Privy, initializes the Amplify SDK, and wires up deposit plus bulk-withdraw flows. Follow the steps end-to-end or cherry-pick the sections that fit your stack.

<Callout type="info">
  The snippets use the latest Privy React SDK (`@privy-io/react-auth`) APIs.
  Wait for `ready` from `usePrivy` and `useWallets` before inspecting user or
  wallet state to avoid race conditions.
</Callout>

## 1. Scaffold the Project

```bash theme={null}
pnpm create vite amplify-earn-starter --template react-ts
cd amplify-earn-starter
pnpm install
```

Install the runtime dependencies:

```bash theme={null}
pnpm add @paxoslabs/amplify-sdk viem @privy-io/react-auth @tanstack/react-query
```

Install type support if you use tests:

```bash theme={null}
pnpm add -D @types/node @types/react @types/react-dom
```

## 2. Configure Environment Variables

Create `.env.local` (ignored by Vite by default):

```bash theme={null}
VITE_PRIVY_APP_ID=your-privy-app-id
VITE_AMPLIFY_API_KEY=your-amplify-api-key
VITE_RPC_URL=https://eth-mainnet.g.alchemy.com/v2/your-key
```

Never commit this file. Provide an `.env.example` without secrets for teammates.

## 3. Set Up Global Providers

Create `src/app/providers.tsx`:

```tsx theme={null}
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { PrivyProvider } from "@privy-io/react-auth";
import type { ReactNode } from "react";

const queryClient = new QueryClient();

export function AppProviders({ children }: { children: ReactNode }) {
  if (!import.meta.env.VITE_PRIVY_APP_ID) {
    throw new Error("Missing VITE_PRIVY_APP_ID env variable");
  }

  return (
    <PrivyProvider appId={import.meta.env.VITE_PRIVY_APP_ID}>
      <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
    </PrivyProvider>
  );
}
```

Wire the providers in `src/main.tsx`:

```tsx theme={null}
import React from "react";
import ReactDOM from "react-dom/client";
import { App } from "./app/App";
import { AppProviders } from "./app/providers";

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <AppProviders>
      <App />
    </AppProviders>
  </React.StrictMode>
);
```

## 4. Initialize the Amplify SDK Once

Create `src/hooks/useAmplify.ts`:

```ts theme={null}
import { initAmplifySDK } from "@paxoslabs/amplify-sdk";
import { useEffect } from "react";

let initialized = false;

export function useAmplify() {
  useEffect(() => {
    if (initialized) {
      return;
    }
    const apiKey = import.meta.env.VITE_AMPLIFY_API_KEY;
    if (!apiKey) {
      throw new Error("Missing VITE_AMPLIFY_API_KEY env variable");
    }
    initAmplifySDK(apiKey);
    initialized = true;
  }, []);
}
```

Call the hook near the root of your app (inside `App` or a layout component).

## 5. Build Wallet & Session State

Create `src/hooks/useWalletSession.ts`:

```ts theme={null}
import { usePrivy, useWallets } from "@privy-io/react-auth";
import { useMemo } from "react";

export function useWalletSession() {
  const { ready: privyReady, authenticated, login, logout } = usePrivy();
  const { ready: walletsReady, wallets } = useWallets();

  const wallet = useMemo(
    () =>
      wallets.find((item) => item.walletClientType === "privy") ?? wallets[0],
    [wallets]
  );

  return {
    ready: privyReady && walletsReady,
    authenticated,
    login,
    logout,
    wallet,
  };
}
```

The `usePrivy` and `useWallets` hooks come directly from the official [Privy React SDK](https://docs.privy.io/basics/react/installation). Wait for `ready` before inspecting `wallets`.

## 6. Create a Starter App

Add `src/app/App.tsx`:

```tsx theme={null}
import { useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { encodeFunctionData } from "viem";
import { mainnet } from "viem/chains";
import {
  fetchVaults,
  prepareApproveDepositTokenTxData,
  prepareApproveWithdrawTxData,
  prepareWithdrawTxData,
  prepareDepositPermitSignature,
  prepareDepositTxData,
  prepareDepositWithPermitTxData,
  YieldType,
} from "@paxoslabs/amplify-sdk";
import { usePrivy } from "@privy-io/react-auth";
import { useAmplify } from "../hooks/useAmplify";
import { useWalletSession } from "../hooks/useWalletSession";

export function App() {
  useAmplify();
  const { ready, authenticated, login, logout, wallet } = useWalletSession();
  const { sendTransaction } = usePrivy();
  const [status, setStatus] = useState<string | null>(null);

  const { data: vaults } = useQuery({
    queryKey: ["core-vaults", mainnet.id],
    enabled: authenticated,
    queryFn: () =>
      fetchVaults({
        chainId: mainnet.id,
        yieldType: YieldType.CORE,
      }),
  });
  const coreVault = vaults?.[0];

  if (!ready) {
    return <p>Loading Privy…</p>;
  }

  if (!authenticated || !wallet) {
    return <button onClick={login}>Connect with Privy</button>;
  }

  if (!coreVault) {
    return <p>Loading vault metadata…</p>;
  }

  const owner = wallet.address as `0x${string}`;
  const depositToken = coreVault.depositTokenAddressId as `0x${string}`;
  const chainId = coreVault.chainId;

  async function approveAndDeposit() {
    setStatus("Preparing approval…");
    const approval = await prepareApproveDepositTokenTxData({
      chainId,
      depositAmount: "100",
      depositToken,
      yieldType: coreVault.yieldType,
    });

    setStatus("Sending approval…");
    await sendTransaction({
      chainId: approval.chainId,
      to: approval.address,
      data: encodeFunctionData({
        abi: approval.abi,
        functionName: approval.functionName,
        args: approval.args,
      }),
    });

    setStatus("Submitting deposit…");
    const deposit = await prepareDepositTxData({
      chainId,
      depositAmount: "100",
      depositToken,
      recipientAddress: owner,
      yieldType: coreVault.yieldType,
    });

    await sendTransaction({
      chainId: deposit.chainId,
      to: deposit.address,
      data: encodeFunctionData({
        abi: deposit.abi,
        functionName: deposit.functionName,
        args: deposit.args,
      }),
    });
    setStatus("Deposit sent");
  }

  async function depositWithPermit() {
    setStatus("Creating permit signature…");
    const permit = await prepareDepositPermitSignature({
      yieldType: coreVault.yieldType,
      depositToken,
      depositAmount: "100",
      recipientAddress: owner,
      chainId,
    });

    const provider = await wallet.getEthereumProvider();
    const signature = (await provider.request({
      method: "eth_signTypedData_v4",
      params: [
        owner,
        JSON.stringify({
          domain: permit.domain,
          types: permit.types,
          primaryType: permit.primaryType,
          message: permit.message,
        }),
      ],
    })) as `0x${string}`;

    setStatus("Submitting depositWithPermit…");
    const tx = await prepareDepositWithPermitTxData({
      yieldType: coreVault.yieldType,
      recipientAddress: owner,
      depositToken,
      depositAmount: "100",
      chainId,
      signature,
      deadline: permit.message.deadline,
    });

    await sendTransaction({
      chainId: tx.chainId,
      to: tx.address,
      data: encodeFunctionData({
        abi: tx.abi,
        functionName: tx.functionName,
        args: tx.args,
      }),
    });
    setStatus("Permit deposit sent");
  }

  async function approveAndWithdraw() {
    setStatus("Preparing withdrawal…");

    const approval = await prepareApproveWithdrawTxData({
      chainId,
      depositAmount: "100",
      depositToken,
      yieldType: coreVault.yieldType,
    });

    setStatus("Sending approval…");
    await sendTransaction({
      chainId: approval.chainId,
      to: approval.address,
      data: encodeFunctionData({
        abi: approval.abi,
        functionName: approval.functionName,
        args: approval.args,
      }),
    });

    const bulk = await prepareWithdrawTxData({
      yieldType: coreVault.yieldType,
      shareAmount: "1.0",
      wantAssetAddress: depositToken,
      chainId,
      userAddress: owner,
      slippage: 75,
    });

    await sendTransaction({
      chainId: bulk.chainId,
      to: bulk.address,
      data: encodeFunctionData({
        abi: bulk.abi,
        functionName: bulk.functionName,
        args: bulk.args,
      }),
    });
    setStatus("Withdrawal sent");
  }

  return (
    <main>
      <h1>Amplify Earn Starter</h1>
      <p>{owner}</p>
      {status && <p>{status}</p>}
      <div>
        <button onClick={approveAndDeposit}>Approve & Deposit</button>
        <button onClick={depositWithPermit}>Deposit with Permit</button>
        <button onClick={approveAndWithdraw}>Withdraw</button>
      </div>
      <button onClick={logout}>Disconnect</button>
    </main>
  );
}
```

## 7. Recommended Enhancements

* Replace hard-coded addresses with vault selections sourced from the API.
* Add React Query mutations around each helper to display optimistic states and capture errors.
* Surface `APIError` payloads in telemetry and show a fallback UI for unsupported assets.
* Extract transaction logic into reusable hooks (`useDeposit`, `useWithdraw`).
* Add analytics around wallet connection and transaction success rates.

## 8. Starter Repository Layout

```text theme={null}
amplify-earn-starter/
├─ src/
│  ├─ app/
│  │  ├─ App.tsx
│  │  └─ providers.tsx
│  ├─ hooks/
│  │  ├─ useAmplify.ts
│  │  └─ useWalletSession.ts
│  └─ main.tsx
├─ .env.example
└─ README.md
```

Clone this structure in your org repo and extend it with routing, feature flags, and analytics as needed. Tie everything back to the [Developer Guide](./developer-guide) for deployment practices and keep [Functions](./functions/index) handy as you add more flows.\*\*\*
