> ## Documentation Index
> Fetch the complete documentation index at: https://developers.paxoslabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Withdrawals

> Complete withdrawal integration examples for Privy, Wagmi, and Viem

This guide demonstrates complete withdrawal flows to redeem vault shares for underlying tokens.

<Info>
  Withdrawals follow a two-step process:

  1. **Approve** - Authorize the vault to spend your shares
  2. **Withdraw** - Redeem shares for the underlying token
</Info>

## Prerequisites

* Node.js 22+
* Amplify API key from Paxos Labs
* SDK initialized with `initAmplifySDK()`
* Vault shares to redeem

## Installation

<Tabs>
  <Tab title="Privy">
    ```bash theme={null}
    pnpm add @paxoslabs/amplify-sdk @privy-io/react-auth viem @tanstack/react-query
    ```
  </Tab>

  <Tab title="Wagmi">
    ```bash theme={null}
    pnpm add @paxoslabs/amplify-sdk wagmi viem @tanstack/react-query
    ```
  </Tab>

  <Tab title="Viem">
    ```bash theme={null}
    pnpm add @paxoslabs/amplify-sdk viem
    ```
  </Tab>
</Tabs>

## Complete Examples

<Tabs>
  <Tab title="Privy" icon="wallet">
    ### useWithdraw Hook

    ```tsx twoslash expandable {3-6,45-49,62-68} theme={null}
    // src/hooks/useWithdraw.ts
    import { useCallback, useMemo, useState } from "react";
    import { encodeFunctionData } from "viem";
    import {
      prepareApproveWithdrawTxData,
      prepareWithdrawTxData,
      type YieldType,
    } from "@paxoslabs/amplify-sdk";

    // @noErrors
    declare const usePrivy: () => { sendTransaction: (tx: any) => Promise<string> };
    declare const useWallets: () => { wallets: Array<{ address: string }> };

    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 withdraw = 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,
              slippage,
            });

            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 { withdraw, status, error, txHash };
    }
    ```

    ### Withdraw Component

    ```tsx {4,11-14,28-34} theme={null}
    // src/components/WithdrawButton.tsx
    import { useMemo } from "react";
    import { useQuery } from "@tanstack/react-query";
    import { fetchSupportedAssets, YieldType } from "@paxoslabs/amplify-sdk";
    import { mainnet } from "viem/chains";
    import { usePrivyWithdraw } from "../hooks/useWithdraw";

    // @noErrors
    declare const useWallets: () => { wallets: Array<{ address: string }> };

    export function PrivyWithdrawButton() {
      const { wallets } = useWallets();
      const wallet = useMemo(() => wallets.find(Boolean), [wallets]);
      const { withdraw, status, error, txHash } = usePrivyWithdraw();

      const { data: supportedAssets } = useQuery({
        queryKey: ["amplify-assets", mainnet.id],
        queryFn: () =>
          fetchSupportedAssets({
            yieldType: YieldType.CORE,
          }),
        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 withdraw({
          yieldType: YieldType.CORE,
          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>
      );
    }
    ```
  </Tab>

  <Tab title="Wagmi" icon="react">
    ### useWithdraw Hook

    ```tsx twoslash expandable {4-7,35-39,48-55} theme={null}
    // src/hooks/useWithdraw.ts
    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) },
      });

      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,
              slippage,
            });

            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",
      };
    }
    ```

    ### Withdraw Component

    ```tsx {4,8-13,24-31} theme={null}
    // src/components/WithdrawForm.tsx
    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 "../hooks/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.CORE }),
        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.CORE,
          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>
      );
    }
    ```
  </Tab>

  <Tab title="Viem" icon="code">
    ### Complete Script

    This example uses pure viem without React, suitable for Node.js scripts or vanilla TypeScript applications.

    <Warning>
      Never commit private keys. Use environment variables or a secrets manager in production.
    </Warning>

    ```ts twoslash expandable {2-7,22-23,36-40,60-66} theme={null}
    // withdraw.ts
    import {
      initAmplifySDK,
      fetchSupportedAssets,
      prepareApproveWithdrawTxData,
      prepareWithdrawTxData,
      YieldType,
    } from "@paxoslabs/amplify-sdk";
    import { createPublicClient, createWalletClient, http } from "viem";
    import { privateKeyToAccount } from "viem/accounts";
    import { mainnet } from "viem/chains";

    // @noErrors
    declare const process: { env: Record<string, string>, exit: (code: number) => never };

    const CONFIG = {
      apiKey: process.env.AMPLIFY_API_KEY!,
      chainId: 1,
      yieldType: YieldType.CORE,
      offerAmount: "5.0", // decimal string of the earn token to exchange
      slippage: 75, // 0.75% slippage tolerance in basis points
      rpcUrl: process.env.RPC_URL ?? "https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY",
      privateKey: process.env.PRIVATE_KEY!,
    };

    async function main() {
      // Step 1: Initialize SDK
      console.log("Initializing SDK...");
      await initAmplifySDK(CONFIG.apiKey);

      // Step 2: Setup wallet client
      const account = privateKeyToAccount(CONFIG.privateKey as `0x${string}`);

      const publicClient = createPublicClient({
        chain: mainnet,
        transport: http(CONFIG.rpcUrl),
      });

      const walletClient = createWalletClient({
        account,
        chain: mainnet,
        transport: http(CONFIG.rpcUrl),
      });

      // Step 3: Fetch supported assets to get the want asset address
      console.log("Fetching supported assets...");
      const supportedAssets = await fetchSupportedAssets({
        yieldType: CONFIG.yieldType,
      });

      const asset = supportedAssets[0];
      if (!asset) {
        throw new Error("No supported assets found for the selected yield type.");
      }

      const wantAssetAddress = asset.address as `0x${string}`;
      console.log(`Using asset: ${asset.symbol} (${wantAssetAddress})`);

      try {
        // Step 4: Approve withdrawal
        console.log("\nStep 1: Preparing approval transaction...");
        const approvalTx = await prepareApproveWithdrawTxData({
          chainId: CONFIG.chainId,
          wantAssetAddress,
          yieldType: CONFIG.yieldType,
        });

        console.log(`Approval target: ${approvalTx.address}`);
        console.log("Executing approval transaction...");
        const approvalHash = await walletClient.writeContract(approvalTx);
        console.log(`Approval tx hash: ${approvalHash}`);

        console.log("Waiting for approval confirmation...");
        const approvalReceipt = await publicClient.waitForTransactionReceipt({
          hash: approvalHash,
        });

        if (approvalReceipt.status === "success") {
          console.log(`Approval confirmed in block ${approvalReceipt.blockNumber}`);
        } else {
          throw new Error("Approval transaction failed");
        }

        // Step 5: Execute withdrawal
        console.log("\nStep 2: Preparing withdrawal transaction...");
        const tx = await prepareWithdrawTxData({
          yieldType: CONFIG.yieldType,
          wantAssetAddress,
          offerAmount: CONFIG.offerAmount,
          chainId: CONFIG.chainId,
          slippage: CONFIG.slippage,
        });

        console.log("Submitting withdrawal...");
        const withdrawalHash = await walletClient.writeContract({ ...tx, account });
        console.log(`Withdrawal sent: ${withdrawalHash}`);

        console.log("Waiting for confirmation...");
        const withdrawalReceipt = await publicClient.waitForTransactionReceipt({
          hash: withdrawalHash,
        });

        if (withdrawalReceipt.status !== "success") {
          throw new Error(`Withdrawal reverted in tx ${withdrawalHash}`);
        }

        console.log(`Withdrawal confirmed in block ${withdrawalReceipt.blockNumber}`);
        return withdrawalReceipt;
      } catch (error) {
        console.error("\nWithdrawal failed");
        if (error instanceof Error) {
          console.error(`Error: ${error.message}`);
        }
        throw error;
      }
    }

    main()
      .then(() => process.exit(0))
      .catch(() => process.exit(1));
    ```

    ### Running the Script

    ```bash theme={null}
    # Set environment variables
    export AMPLIFY_API_KEY=pxl_your_api_key
    export PRIVATE_KEY=0x...
    export RPC_URL=https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY

    # Run with tsx or ts-node
    npx tsx withdraw.ts
    ```

    ### Expected Output

    ```
    Initializing SDK...
    Fetching supported assets...
    Using asset: USDC (0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48)

    Step 1: Preparing approval transaction...
    Approval target: 0x...
    Executing approval transaction...
    Approval tx hash: 0x1234...
    Waiting for approval confirmation...
    Approval confirmed in block 12345678

    Step 2: Preparing withdrawal transaction...
    Submitting withdrawal...
    Withdrawal sent: 0x5678...
    Waiting for confirmation...
    Withdrawal confirmed in block 12345679
    ```
  </Tab>
</Tabs>

## What the Code Does

1. **Approves the vault** - Authorizes the vault contract to spend your shares
2. **Prepares withdrawal** - Builds transaction data with slippage protection
3. **Executes withdrawal** - Redeems shares for underlying tokens
4. **Tracks status** - Updates UI with current operation state

## Customization

### Slippage Tolerance

Slippage is specified in basis points (1 bp = 0.01%). Default is 75 (0.75%).

```ts twoslash {3} theme={null}
// @noErrors
declare const params: any;
declare function prepareWithdrawTxData(params: any): Promise<any>;
// ---cut---
const tx = await prepareWithdrawTxData({
  ...params,
  slippage: 100, // 1% slippage tolerance
});
```

### Partial Withdrawals

Specify the exact amount of vault shares to redeem:

```ts twoslash {3} theme={null}
// @noErrors
declare const params: any;
declare function withdraw(params: any): Promise<any>;
// ---cut---
await withdraw({
  ...params,
  offerAmount: "10.5", // Redeem 10.5 vault shares
});
```

## Troubleshooting

| Issue                    | Solution                                          |
| ------------------------ | ------------------------------------------------- |
| "SDK not initialized"    | Call `initAmplifySDK()` before using hooks        |
| "Connect a wallet first" | Ensure user has connected wallet                  |
| Insufficient shares      | Check vault share balance before withdrawing      |
| Slippage exceeded        | Increase slippage tolerance or try smaller amount |
| Liquidity unavailable    | Vault may have withdrawal limits, try later       |

<Info>
  `prepareWithdrawTxData` throws `APIError` when liquidity is unavailable or the vault is unsupported. Surface the `endpoint` and `statusCode` fields to help users understand failures.
</Info>

For more details on the withdrawal API, see [prepareWithdrawTxData](/v0.3.0/intro/products/earn/developers/api/prepareWithdrawTxData).
