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