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:
| Method | Description | Typical Scenario |
|---|---|---|
| Direct Withdraw | Withdraw ERC-20 or native tokens directly to supplier wallet. | User withdraws supplied assets from their own wallet balance. |
| Another Recipient | Withdraw 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 Market object:
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.
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 use 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,
});
// …
};
import { useWalletClient } from "wagmi";
import { useWithdraw, 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: {
max: true,
},
},
},
sender: evmAddress(walletClient!.account.address),
chainId: position.market.chain.chainId,
});
// …
};
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
},
},
},
recipient: evmAddress("0x1234567890123456789012345678901234567890"),
sender: evmAddress(walletClient!.account.address),
chainId: position.market.chain.chainId,
});
// …
};
import { withdraw } from "@aave/client/actions";
import { bigDecimal, evmAddress } from "@aave/client";
import { client } from "./client";
import { wallet } from "./wallet";
const result = await withdraw(client, {
market: position.market.address,
amount: {
erc20: {
currency: position.currency.address,
value: {
exact: bigDecimal(2), // 2 WETH
},
},
},
sender: evmAddress(wallet.account.address),
chainId: position.market.chain.chainId,
});
// …
import { withdraw } from "@aave/client/actions";
import { evmAddress } from "@aave/client";
import { client } from "./client";
import { wallet } from "./wallet";
const result = await withdraw(client, {
market: position.market.address,
amount: {
erc20: {
currency: position.currency.address,
value: {
max: true,
},
},
},
sender: evmAddress(wallet.account.address),
chainId: position.market.chain.chainId,
});
// …
import { withdraw } from "@aave/client/actions";
import { bigDecimal, evmAddress } from "@aave/client";
import { client } from "./client";
import { wallet } from "./wallet";
const result = await withdraw(client, {
market: position.market.address,
amount: {
erc20: {
currency: position.currency.address,
value: {
exact: bigDecimal(2), // 2 WETH
},
},
},
recipient: evmAddress("0x1234567890123456789012345678901234567890"),
sender: evmAddress(wallet.account.address),
chainId: position.market.chain.chainId,
});
// …
import { withdraw } from "@aave/client/actions";
import { bigDecimal, evmAddress } from "@aave/client";
import { client } from "./client";
import { wallet } from "./wallet";
const result = await withdraw(client, {
market: position.market.address,
amount: {
native: {
value: { exact: bigDecimal(2) }, // 2 ETH
},
},
recipient: evmAddress("0x1234567890123456789012345678901234567890"),
sender: evmAddress(wallet.account.address),
chainId: position.market.chain.chainId,
});
// …
import { AaveClient } from "@aave/client";
export const client = AaveClient.create();
import { createWalletClient, http, type Hex } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";
export const wallet = createWalletClient({
account: privateKeyToAccount(process.env.PRIVATE_KEY as Hex),
chain: mainnet,
transport: http(),
});
query {
withdraw(
request: {
market: "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2"
amount: {
erc20: {
currency: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
value: {
exact: "2"
# OR
# max: true
}
}
# OR if the reserve accepts native currency
# native: {
# value: { exact: "2" }
# # OR
# # max: true
# }
}
sender: "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234"
chainId: 1
# OPTIONAL: if you want to withdraw to a different address
# recipient: "0x1234567890123456789012345678901234567890"
}
) {
__typename
... on TransactionRequest {
to
from
data
value
chainId
operation
}
... on ApprovalRequired {
reason
requiredAmount {
value
decimals
}
currentAllowance {
value
decimals
}
approval {
to
from
data
value
chainId
operation
}
originalTransaction {
to
from
data
value
chainId
operation
}
}
... on InsufficientBalanceError {
required {
value
decimals
}
available {
value
decimals
}
}
}
}
If Reserve. 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,
});
// …
};
import { useWalletClient } from "wagmi";
import { useWithdraw, 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: { max: true },
},
},
sender: evmAddress(walletClient!.account.address),
chainId: position.market.chain.chainId,
});
// …
};
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
},
},
recipient: evmAddress("0x1234567890123456789012345678901234567890"),
sender: evmAddress(walletClient!.account.address),
chainId: position.market.chain.chainId,
});
// …
};
import { withdraw } from "@aave/client/actions";
import { bigDecimal, evmAddress } from "@aave/client";
import { client } from "./client";
import { wallet } from "./wallet";
const result = await withdraw(client, {
market: position.market.address,
amount: {
native: {
value: { exact: bigDecimal(2) }, // 2 ETH
},
},
sender: evmAddress(wallet.account.address),
chainId: position.market.chain.chainId,
});
// …
import { withdraw } from "@aave/client/actions";
import { evmAddress } from "@aave/client";
import { client } from "./client";
import { wallet } from "./wallet";
const result = await withdraw(client, {
market: position.market.address,
amount: {
native: {
value: { max: true },
},
},
sender: evmAddress(wallet.account.address),
chainId: position.market.chain.chainId,
});
// …
3. Process the Withdraw Execution Plan
Finally, handle the execution plan.
- React: Use the
usehook for the wallet library of your choice to send the transactions in the execution plan.Send Transaction - TypeScript: Use
sendfor your chosen wallet library to send the transactions in the execution plan, and await indexing withWith client..wait For Transaction - GraphQL: If the response is a
Transaction, you can send it directly using the wallet associated with the from address.Request
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);
}
};
import { withdraw } from "@aave/client/actions";
import { sendWith } from "@aave/client/viem";
import { client } from "./client";
import { wallet } from "./wallet";
const result = await withdraw(client, {
// …
})
.andThen(sendWith(wallet))
.andThen(client.waitForTransaction);
if (result.isErr()) {
console.error("Withdraw failed:", result.error);
} else {
console.log("Withdraw successful:", result.value);
}
{
"data": {
"withdraw": {
"__typename": "TransactionRequest",
"to": "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2",
"from": "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234",
"data": "0x69328dec000000000000000000000000c02…",
"value": "0",
"chainId": 1,
"operation": "WITHDRAW"
}
}
}
In GraphQL, if the response is an Approval, you must first send the approval transaction. Once it succeeds, you can then send the original.
{
"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 Insufficient, inform the user that they don’t have enough balance to withdraw the requested amount.
{
"data": {
"withdraw": {
"__typename": "InsufficientBalanceError",
"required": {
"value": "2.0",
"decimals": 18
},
"available": {
"value": "1.5",
"decimals": 18
}
}
}
}