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
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";
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, {
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 {
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
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.