> ## 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

> Build a React app with deposits and withdrawals in under 30 minutes

This walkthrough bootstraps a Vite + React project that authenticates users with Privy, initializes the Amplify SDK, and wires up deposit flows using the unified deposit API.

<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.
</Info>

## 1. Scaffold the Project

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

Install the runtime dependencies:

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

## 2. Configure Environment Variables

Create `.env.local`:

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

<Warning>
  Never commit this file. Provide an `.env.example` without secrets for teammates.
</Warning>

## 3. Set Up Providers

Create `src/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 Providers({ 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 { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import { Providers } from "./providers.tsx";

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <Providers>
      <App />
    </Providers>
  </StrictMode>,
);
```

## 4. Initialize the SDK

Create `src/hooks/useAmplify.ts`:

```ts theme={null}
import { initAmplifySDK, LogLevel } from "@paxoslabs/amplify-sdk";
import { useEffect, useState } from "react";
import { mainnet } from "viem/chains";

let initialized = false;

export function useAmplify() {
  const [isReady, setIsReady] = useState(initialized);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    if (initialized) {
      setIsReady(true);
      return;
    }

    const apiKey = import.meta.env.VITE_AMPLIFY_API_KEY;
    if (!apiKey) {
      setError(new Error("Missing VITE_AMPLIFY_API_KEY env variable"));
      return;
    }

    async function init() {
      try {
        await initAmplifySDK(apiKey, {
          rpcUrls: {
            [mainnet.id]: import.meta.env.VITE_APP_ETHEREUM_MAINNET_RPC,
          },
          logLevel: LogLevel.ERROR,
          telemetry: true,
        });
        initialized = true;
        setIsReady(true);
      } catch (err) {
        setError(err instanceof Error ? err : new Error("SDK initialization failed"));
      }
    }

    init();
  }, []);

  return { isReady, error };
}
```

## 5. Create the App

Replace `src/App.tsx`:

```tsx theme={null}
import { useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { usePrivy, useWallets } from "@privy-io/react-auth";
import { encodeFunctionData } from "viem";
import { mainnet } from "viem/chains";
import {
  fetchSupportedAssets,
  prepareDepositAuthorization,
  prepareDeposit,
  isPermitAuth,
  isApprovalAuth,
  isAlreadyApprovedAuth,
  YieldType,
} from "@paxoslabs/amplify-sdk";
import { useAmplify } from "./hooks/useAmplify";

export default function App() {
  const { isReady, error: sdkError } = useAmplify();
  const { ready, authenticated, login, logout, sendTransaction } = usePrivy();
  const { wallets } = useWallets();
  const wallet = wallets[0];

  const [status, setStatus] = useState<string>("");
  const [amount, setAmount] = useState("100");

  const { data: assets } = useQuery({
    queryKey: ["assets", mainnet.id],
    queryFn: () => fetchSupportedAssets({ yieldType: YieldType.CORE }),
    enabled: isReady && authenticated,
  });

  // Loading states
  if (!ready || !isReady) return <p>Loading...</p>;
  if (sdkError) return <p>SDK Error: {sdkError.message}</p>;
  if (!authenticated) return <button onClick={login}>Connect with Privy</button>;
  if (!wallet) return <p>Connecting wallet...</p>;

  const asset = assets?.[0];
  const owner = wallet.address as `0x${string}`;

  async function handleDeposit() {
    if (!asset) return;

    const params = {
      yieldType: YieldType.CORE,
      depositAsset: asset.address as `0x${string}`,
      depositAmount: amount,
      to: owner,
      chainId: mainnet.id,
    };

    try {
      setStatus("Checking authorization...");
      const auth = await prepareDepositAuthorization(params);

      if (isPermitAuth(auth)) {
        setStatus("Please sign permit...");
        const provider = await wallet.getEthereumProvider();
        const signature = (await provider.request({
          method: "eth_signTypedData_v4",
          params: [owner, JSON.stringify(auth.permitData)],
        })) as `0x${string}`;

        setStatus("Submitting deposit...");
        const prepared = await prepareDeposit({
          ...params,
          signature,
          deadline: BigInt(auth.permitData.message.deadline),
        });

        await sendTransaction({
          chainId: prepared.txData.chainId,
          to: prepared.txData.address,
          data: encodeFunctionData({
            abi: prepared.txData.abi,
            functionName: prepared.txData.functionName,
            args: prepared.txData.args,
          }),
        });
      } else if (isApprovalAuth(auth)) {
        setStatus("Approving token...");
        await sendTransaction({
          chainId: mainnet.id,
          to: auth.txData.address,
          data: encodeFunctionData({
            abi: auth.txData.abi,
            functionName: auth.txData.functionName,
            args: auth.txData.args,
          }),
        });

        setStatus("Submitting deposit...");
        const prepared = await prepareDeposit(params);
        await sendTransaction({
          chainId: prepared.txData.chainId,
          to: prepared.txData.address,
          data: encodeFunctionData({
            abi: prepared.txData.abi,
            functionName: prepared.txData.functionName,
            args: prepared.txData.args,
          }),
        });
      } else if (isAlreadyApprovedAuth(auth)) {
        setStatus("Submitting deposit...");
        const prepared = await prepareDeposit(params);
        await sendTransaction({
          chainId: prepared.txData.chainId,
          to: prepared.txData.address,
          data: encodeFunctionData({
            abi: prepared.txData.abi,
            functionName: prepared.txData.functionName,
            args: prepared.txData.args,
          }),
        });
      }

      setStatus("Deposit complete!");
    } catch (err) {
      setStatus(`Error: ${err instanceof Error ? err.message : "Unknown"}`);
    }
  }

  return (
    <main style={{ padding: "2rem" }}>
      <h1>Amplify Starter</h1>
      <p>Connected: {wallet.address}</p>

      <div style={{ marginTop: "1rem" }}>
        <label>
          Amount ({asset?.symbol || "..."}):
          <input
            type="number"
            value={amount}
            onChange={(e) => setAmount(e.target.value)}
            style={{ marginLeft: "0.5rem" }}
          />
        </label>
      </div>

      <div style={{ marginTop: "1rem" }}>
        <button onClick={handleDeposit} disabled={!asset}>
          Deposit
        </button>
        <button onClick={logout} style={{ marginLeft: "1rem" }}>
          Disconnect
        </button>
      </div>

      {status && <p style={{ marginTop: "1rem" }}>{status}</p>}
    </main>
  );
}
```

## 6. Run the App

```bash theme={null}
pnpm dev
```

Open [http://localhost:5173](http://localhost:5173) and test the deposit flow.

## Next Steps

<CardGroup cols={2}>
  <Card title="Deposits Guide" icon="arrow-down-to-bracket" href="/v0.4.2/intro/products/earn/developers/guides/deposits">
    Complete deposit examples with hooks and components.
  </Card>

  <Card title="Withdrawals Guide" icon="arrow-up-from-bracket" href="/v0.4.2/intro/products/earn/developers/guides/withdrawals">
    Add withdrawal functionality to your app.
  </Card>

  <Card title="Smart Wallets" icon="wallet" href="/v0.4.2/intro/products/earn/developers/guides/smart-wallets">
    Enable gas sponsorship and transaction batching.
  </Card>

  <Card title="API Reference" icon="code" href="/v0.4.2/intro/products/earn/developers/api/index">
    Explore all SDK functions and types.
  </Card>
</CardGroup>
