Skip to main content
Redeem vault shares in a single transaction using prepareWithdrawTxData and viem’s wallet client. This script fetches the target vault, prepares the calldata with a slippage safeguard, submits the transaction, and waits for confirmation.

Overview

  • Discover a vault that supports withdrawals.
  • Prepare withdrawal calldata with slippage tolerance.
  • Submit and confirm the transaction with viem.
  • Handle failures by surfacing the APIError payload.

Prerequisites

  • Node.js >= 20
  • viem >= 2.0.0
  • @paxoslabs/amplify-sdk installed and initialized (initAmplifySDK elsewhere in your app)
  • RPC URL (Alchemy, Infura, etc.)
  • Private key with gas and vault shares to redeem
Never commit private keys. Use environment variables or a secrets manager in production.

Complete Code Example

/**
 * Viem Withdrawal Example
 *
 * Demonstrates complete withdrawal flow using viem (vanilla TypeScript):
 * 1. Approve withdrawal
 * 2. Execute withdraw transaction
 */
import {
  fetchSupportedAssets,
  prepareApproveWithdrawTxData,
  prepareWithdrawTxData,
  YieldType,
} from "@paxoslabs/amplify-sdk";
import { createPublicClient, createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";

const CONFIG = {
  chainId: 1,
  yieldType: YieldType.PRIME,
  offerAmount: "5.0", // decimal string of the earn token to exchange
  slippage: 75, // Optional: 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 ?? "0x...",
};

async function withdraw() {
  // Step 1: 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),
  });

  // Fetch supported assets to get the want asset address
  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}`;

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

    console.log(`Approval target: ${approvalTx.address}`);
    console.log();

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

    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");
    }
    console.log();

    // Step 3: Prepare and execute withdrawal transaction
    console.log("Step 2: Preparing withdrawal transaction...");
    const tx = await prepareWithdrawTxData({
      yieldType: CONFIG.yieldType,
      wantAssetAddress,
      offerAmount: CONFIG.offerAmount,
      chainId: CONFIG.chainId,
    });

    console.log("Submitting transaction...");
    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();
    console.error("=".repeat(60));
    console.error("✗ Withdrawal failed");
    console.error("=".repeat(60));

    if (error instanceof Error) {
      console.error(`Error: ${error.message}`);
    } else {
      console.error("Unknown error:", error);
    }

    throw error;
  }
}

withdraw()
  .then(() => {
    process.exit(0);
  })
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });
prepareWithdrawTxData throws APIError when a vault, token, or chain is unsupported. Log the endpoint and cause fields to help users fall back to alternative flows when liquidity is unavailable.