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
Copy
Ask AI
pnpm create vite amplify-starter --template react-ts
cd amplify-starter
pnpm install
Copy
Ask AI
pnpm add @paxoslabs/amplify-sdk viem @privy-io/react-auth @tanstack/react-query
2. Configure Environment Variables
Create.env.local:
Copy
Ask AI
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
Createsrc/providers.tsx:
Copy
Ask AI
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>
)
}
src/main.tsx:
Copy
Ask AI
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
Createsrc/hooks/useAmplify.ts:
Copy
Ask AI
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
Replacesrc/App.tsx:
Copy
Ask AI
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
Copy
Ask AI
pnpm dev
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.