Skip to Content
docs

Withdraw Assets on Aave V3

Withdraw supplied assets from Aave V3 markets.

Withdraw Assets

Users can withdraw assets from Aave markets using their previously supplied reserves.

When withdrawing assets from an Aave market, you can use the following methods:

MethodDescriptionTypical Scenario
Direct WithdrawWithdraw ERC-20 or native tokens directly to supplier wallet.User withdraws supplied assets from their own wallet balance.
Another RecipientWithdraw supplied assets to another address.User supplied with one wallet and wants to withdraw to a different wallet.

To withdraw supplied assets from an Aave market, follow these steps.

1. Identify the Supply Position

First, determine which user supply position you want to withdraw assets from.

Given this MarketUserReserveSupplyPosition object:

MarketUserReserveSupplyPosition
const position: MarketUserReserveSupplyPosition = {
  __typename: "MarketUserReserveSupplyPosition",
  market: {
    __typename: "MarketInfo",
    address: "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2",
    chain: { chainId: 1 },
    // …
  },
  currency: {
    __typename: "Currency",
    symbol: "WETH",
    name: "Wrapped Ether",
    address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
    // …
  },
  balance: {
    __typename: "TokenAmount",
    amount: {
      __typename: "DecimalValue",
      value: "5.0",
      raw: "5000000000000000000",
      decimals: 18,
      // …
    },
    // …
  },
  // …
};

That is tied to the following Reserve object.

Reserve
const reserve: Reserve = {
  __typename: "Reserve",
  market: {
    __typename: "MarketInfo",
    address: "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2",
    chain: { chainId: 1 },
    // …
  },
  underlyingToken: {
    __typename: "Currency",
    symbol: "WETH",
    name: "Wrapped Ether",
    address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
    // …
  },
  acceptsNative: {
    __typename: "NativeCurrency",
    // …
  },
};

2. Prepare the Withdraw Execution Plan

Next, we can proceed with creating the execution plan for the withdraw operation.

Use the useWithdraw hook in React, the withdraw action in TypeScript, or the withdraw query in GraphQL to create the execution plan for withdrawing assets from a market reserve.

import { useWalletClient } from "wagmi";
import { useWithdraw, bigDecimal, evmAddress } from "@aave/react";

// …

const { data: walletClient } = useWalletClient();

const [withdraw, withdrawing] = useWithdraw();

const execute = async () => {
  const result = await withdraw({
    market: position.market.address,
    amount: {
      erc20: {
        currency: position.currency.address,
        value: {
          exact: bigDecimal(2), // 2 WETH
        },
      },
    },
    sender: evmAddress(walletClient!.account.address),
    chainId: position.market.chain.chainId,
  });

  // …
};

If Reserve.acceptsNative is not null, you can choose to withdraw the assets as the chain’s native token.

import { useWalletClient } from "wagmi";
import { useWithdraw, bigDecimal, evmAddress } from "@aave/react";

// …

const { data: walletClient } = useWalletClient();

const [withdraw, withdrawing] = useWithdraw();

const execute = async () => {
  const result = await withdraw({
    market: position.market.address,
    amount: {
      native: {
        value: { exact: bigDecimal(2) }, // 2 ETH
      },
    },
    sender: evmAddress(walletClient!.account.address),
    chainId: position.market.chain.chainId,
  });

  // …
};

3. Process the Withdraw Execution Plan

Finally, handle the execution plan.

  • React: Use the useSendTransaction hook for the wallet library of your choice to send the transactions in the execution plan.
  • TypeScript: Use sendWith for your chosen wallet library to send the transactions in the execution plan, and await indexing with client.waitForTransaction.
  • GraphQL: If the response is a TransactionRequest, you can send it directly using the wallet associated with the from address.
Viem
import { useWalletClient } from "wagmi";
import { errAsync, useWithdraw } from "@aave/react";
import { useSendTransaction } from "@aave/react/viem";

// …

const { data: walletClient } = useWalletClient();

const [withdraw, withdrawing] = useWithdraw();
const [sendTransaction, sending] = useSendTransaction(walletClient);

// …

// Optional: combine loading states
const loading = withdrawing.loading || sending.loading;
const error = withdrawing.error || sending.error;

// …

const execute = async () => {
  const result = await withdraw({
    // …
  }).andThen((plan) => {
    switch (plan.__typename) {
      case "TransactionRequest":
        // Single transaction execution
        return sendTransaction(plan);

      case "ApprovalRequired":
        // Approval + transaction sequence
        return sendTransaction(plan.approval).andThen(() =>
          sendTransaction(plan.originalTransaction)
        );

      case "InsufficientBalanceError":
        return errAsync(
          new Error(`Insufficient balance: ${plan.required.value} required.`)
        );
    }
  });

  if (result.isErr()) {
    console.error("Withdraw failed:", result.error);
  } else {
    console.log("Withdraw successful with hash:", result.value);
  }
};

In GraphQL, if the response is an ApprovalRequired, you must first send the approval transaction. Once it succeeds, you can then send the originalTransaction.

ApprovalRequired
{
  "data": {
    "withdraw": {
      "__typename": "ApprovalRequired",
      "reason": "Insufficient allowance for aToken transfer",
      "requiredAmount": {
        "value": "2.0",
        "decimals": 18
      },
      "currentAllowance": {
        "value": "0.0",
        "decimals": 18
      },
      "approval": {
        "to": "0x4d5F47FA6A74757f35C14fD3a6Ef8E3C9BC514E8",
        "from": "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234",
        "data": "0x095ea7b3000000000000000000000000878…",
        "value": "0",
        "chainId": 1,
        "operation": null
      },
      "originalTransaction": {
        "to": "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2",
        "from": "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234",
        "data": "0x69328dec000000000000000000000000c02…",
        "value": "0",
        "chainId": 1,
        "operation": "WITHDRAW"
      }
    }
  }
}

In GraphQL, if the response is an InsufficientBalanceError, inform the user that they don’t have enough balance to withdraw the requested amount.

InsufficientBalanceError
{
  "data": {
    "withdraw": {
      "__typename": "InsufficientBalanceError",
      "required": {
        "value": "2.0",
        "decimals": 18
      },
      "available": {
        "value": "1.5",
        "decimals": 18
      }
    }
  }
}