This React example pairs wagmi with prepareWithdrawTxData to offer immediate redemptions. The custom hook handles transaction preparation, submission, and receipt tracking so UI components can focus on user experience.
Overview
- Fetch vault metadata (optionally cached with React Query or SWR).
- Prepare withdrawal calldata with slippage tolerance.
- Submit the transaction using wagmi’s
useWriteContract.
- Surface transaction state with
useWaitForTransactionReceipt.
Prerequisites
- React 18+
- wagmi >= 2.0.0 and viem >= 2.0.0
- @paxoslabs/amplify-sdk installed and initialized (
initAmplifySDK called in a provider)
- React Query (optional) for data fetching
- Connected wallet with vault shares to redeem
Withdrawal Hook
import { useCallback, useEffect, useState } from "react";
import { useWaitForTransactionReceipt, useWriteContract } from "wagmi";
import {
prepareApproveWithdrawTxData,
prepareWithdrawTxData,
type YieldType,
} from "@paxoslabs/amplify-sdk";
import type { Address } from "viem";
interface WithdrawParams {
yieldType: YieldType;
offerAmount: string;
wantAssetAddress: Address;
chainId: number;
userAddress: Address;
slippage?: number;
}
export function useWithdraw() {
const [error, setError] = useState<Error | null>(null);
const [status, setStatus] = useState<
"idle" | "approving" | "submitting" | "confirming" | "success"
>("idle");
// Wagmi hooks for writing contracts
const {
writeContractAsync,
data: hash,
isPending: isSubmitting,
} = useWriteContract();
// Wait for transaction confirmation
const { isLoading: isConfirming } = useWaitForTransactionReceipt({
hash,
confirmations: 1,
query: { enabled: Boolean(hash) },
});
/**
* Execute withdraw transaction
*/
const withdraw = useCallback(
async ({
yieldType,
offerAmount,
wantAssetAddress,
chainId,
userAddress,
slippage = 75,
}: WithdrawParams) => {
setError(null);
try {
// Step 1: Approve withdrawal
setStatus("approving");
const approval = await prepareApproveWithdrawTxData({
chainId,
wantAssetAddress,
yieldType,
});
await writeContractAsync({
...approval,
account: userAddress,
});
// Step 2: Execute withdrawal
setStatus("submitting");
const tx = await prepareWithdrawTxData({
yieldType,
offerAmount,
wantAssetAddress,
chainId,
});
const transactionHash = await writeContractAsync({
...tx,
account: userAddress,
});
setStatus("confirming");
return transactionHash;
} catch (err) {
const error = err instanceof Error ? err : new Error(String(err));
setError(error);
setStatus("idle");
throw error;
}
},
[writeContractAsync]
);
useEffect(() => {
if (hash && !isSubmitting && !isConfirming) {
setStatus("success");
}
}, [hash, isSubmitting, isConfirming]);
return {
withdraw,
status,
hash,
error,
isIdle: status === "idle",
isSubmitting,
isConfirming,
isSuccess: status === "success",
};
}
Component Usage
import { useAccount } from "wagmi";
import { useQuery } from "@tanstack/react-query";
import { fetchSupportedAssets, YieldType } from "@paxoslabs/amplify-sdk";
import { mainnet } from "viem/chains";
import { useWithdraw } from "./useWithdraw";
export function WithdrawForm() {
const { address, chainId } = useAccount();
const { withdraw, status, error, hash } = useWithdraw();
const { data: supportedAssets } = useQuery({
queryKey: ["amplify-assets", chainId],
queryFn: () =>
fetchSupportedAssets({ yieldType: YieldType.PRIME }),
enabled: Boolean(address),
});
const asset = supportedAssets?.[0];
if (!address) {
return <p>Connect a wallet to redeem shares.</p>;
}
if (!asset) {
return <p>Loading supported assets…</p>;
}
async function handleWithdraw() {
await withdraw({
yieldType: YieldType.PRIME,
offerAmount: "5.0",
wantAssetAddress: asset.address as `0x${string}`,
chainId: mainnet.id,
userAddress: address,
slippage: 75,
});
}
return (
<section>
<button
disabled={status === "approving" || status === "submitting" || status === "confirming"}
onClick={handleWithdraw}
>
{status === "approving"
? "Approving…"
: status === "submitting"
? "Submitting…"
: status === "confirming"
? "Confirming…"
: "Withdraw"}
</button>
{hash && (
<p>
Transaction hash:{" "}
<a
href={`https://etherscan.io/tx/${hash}`}
target="_blank"
rel="noreferrer"
>
{hash}
</a>
</p>
)}
{error && <p role="alert">Withdraw failed: {error.message}</p>}
</section>
);
}
prepareWithdrawTxData throws APIError when liquidity is
unavailable or the vault is unsupported. Surface the endpoint and
statusCode fields so users understand why the operation failed.