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

1. Scaffold the Project

pnpm create vite amplify-starter --template react-ts
cd amplify-starter
pnpm install
Install the runtime dependencies:
pnpm add @paxoslabs/amplify-sdk viem @privy-io/react-auth @tanstack/react-query

2. Configure Environment Variables

Create .env.local:
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
Never commit this file. Provide an .env.example without secrets for teammates.

3. Set Up Providers

Create src/providers.tsx:
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:
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:
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:
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 {
  getVaultsByConfig,
  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')

  // Discover the vault first — vault.name is used in all deposit calls
  const { data: vaults } = useQuery({
    queryKey: ['vaults', mainnet.id],
    queryFn: () =>
      getVaultsByConfig({ yieldType: YieldType.CORE, chainId: mainnet.id }),
    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 vault = vaults?.[0]
  const owner = wallet.address as `0x${string}`
  const depositAsset = vault?.vault.baseTokenAddress as
    | `0x${string}`
    | undefined

  async function handleDeposit() {
    if (!vault || !depositAsset) return

    const params = {
      vaultName: vault.name,
      depositAsset,
      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: auth.permitData.message.deadline,
        })

        await sendTransaction({
          chainId: prepared.txData.chainId,
          to: prepared.txData.address,
          data: encodeFunctionData({
            abi: prepared.txData.data.abi,
            functionName: prepared.txData.data.functionName,
            args: prepared.txData.data.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>
      {vault && <p>Vault: {vault.name}</p>}

      <div style={{ marginTop: '1rem' }}>
        <label>
          Amount:
          <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={!vault}>
          Deposit
        </button>
        <button onClick={logout} style={{ marginLeft: '1rem' }}>
          Disconnect
        </button>
      </div>

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

6. Run the App

pnpm dev
Open http://localhost:5173 and test the deposit flow.

Next Steps

Deposits Guide

Complete deposit examples with hooks and components.

Withdrawals Guide

Add withdrawal functionality to your app.

Smart Wallets

Enable gas sponsorship and transaction batching.

API Reference

Explore all SDK functions and types.