Redeem shares in one transaction by pairing prepareWithdrawTxData with Privy’s wallet connection hooks. This pattern keeps signer UX inside Privy while letting the Amplify SDK handle calldata.
Overview
- Fetch the target vault (for example with
fetchVaults and React Query).
- Prepare withdrawal calldata with slippage protection.
- Submit the transaction via Privy’s
sendTransaction.
- Track optimistic state and surface any
APIError details.
Prerequisites
- React 18+
- @privy-io/react-auth >= 3
- @paxoslabs/amplify-sdk
- Vault metadata available in your component (e.g., from
fetchVaults)
Withdraw Hook
import { useCallback, useMemo, useState } from "react";
import { encodeFunctionData } from "viem";
import {
prepareApproveWithdrawTxData,
prepareWithdrawTxData,
type YieldType,
} from "@paxoslabs/amplify-sdk";
import { usePrivy, useWallets } from "@privy-io/react-auth";
interface WithdrawParams {
yieldType: YieldType;
offerAmount: string;
wantAssetAddress: `0x${string}`;
chainId: number;
slippage?: number;
}
export function usePrivyWithdraw() {
const { sendTransaction } = usePrivy();
const { wallets } = useWallets();
const wallet = useMemo(() => wallets.find(Boolean), [wallets]);
const [txHash, setTxHash] = useState<`0x${string}` | null>(null);
const [status, setStatus] = useState<
"idle" | "approving" | "withdrawing" | "success"
>("idle");
const [error, setError] = useState<Error | null>(null);
const run = useCallback(
async ({
yieldType,
offerAmount,
wantAssetAddress,
chainId,
slippage = 75,
}: WithdrawParams) => {
if (!wallet) {
throw new Error("Connect a Privy wallet before withdrawing.");
}
setError(null);
setTxHash(null);
try {
// Step 1: Approve withdrawal
setStatus("approving");
const approval = await prepareApproveWithdrawTxData({
chainId,
wantAssetAddress,
yieldType,
});
await sendTransaction({
chainId,
to: approval.address,
data: encodeFunctionData({
abi: approval.abi,
functionName: approval.functionName,
args: approval.args,
}),
});
// Step 2: Execute withdrawal
setStatus("withdrawing");
const tx = await prepareWithdrawTxData({
yieldType,
wantAssetAddress,
offerAmount,
chainId,
});
const { abi, functionName, args } = tx;
const hash = await sendTransaction({
chainId: tx.chainId,
to: tx.address,
data: encodeFunctionData({ abi, functionName, args }),
});
setTxHash(hash as `0x${string}`);
setStatus("success");
} catch (err) {
const message = err instanceof Error ? err : new Error(String(err));
setError(message);
setStatus("idle");
throw err;
}
},
[wallet, sendTransaction]
);
return { run, status, error, txHash };
}
Component Usage
import { useMemo } from "react";
import { useWallets } from "@privy-io/react-auth";
import { useQuery } from "@tanstack/react-query";
import { fetchSupportedAssets, YieldType } from "@paxoslabs/amplify-sdk";
import { mainnet } from "viem/chains";
import { usePrivyWithdraw } from "./usePrivyWithdraw";
export function PrivyWithdrawButton() {
const { wallets } = useWallets();
const wallet = useMemo(() => wallets.find(Boolean), [wallets]);
const { run, status, error, txHash } = usePrivyWithdraw();
const { data: supportedAssets } = useQuery({
queryKey: ["amplify-assets", mainnet.id],
queryFn: () =>
fetchSupportedAssets({
yieldType: YieldType.PRIME,
}),
enabled: Boolean(wallet),
});
const asset = supportedAssets?.[0];
if (!wallet) {
return <p>Connect with Privy to withdraw.</p>;
}
if (!asset) {
return <p>Loading supported assets…</p>;
}
async function handleClick() {
await run({
yieldType: YieldType.PRIME,
offerAmount: "5.0",
wantAssetAddress: asset.address as `0x${string}`,
chainId: mainnet.id,
slippage: 75,
});
}
return (
<section>
<button onClick={handleClick} disabled={status !== "idle" && status !== "success"}>
{status === "approving" && "Approving…"}
{status === "withdrawing" && "Withdrawing…"}
{status === "success" && "Withdraw Again"}
{status === "idle" && "Withdraw"}
</button>
{txHash && (
<p>
Withdrawal sent:{" "}
<a
href={`https://etherscan.io/tx/${txHash}`}
target="_blank"
rel="noreferrer"
>
{txHash}
</a>
</p>
)}
{error && <p role="alert">Withdraw failed: {error.message}</p>}
</section>
);
}
prepareWithdrawTxData throws APIError when the vault or asset is
unsupported. Log the endpoint and statusCode to help users fall back to
alternate flows.