Market Operations
Execute the fundamental lending and borrowing operations on Aave markets. This section covers supply, borrow, repay, and withdraw functionality.
Permit Overview
Some Aave operations support permit functionality using EIP-2612 signatures to authorize ERC-20 token transfers within the same transaction.
Benefits of Permit
Single Transaction: Execute operations without separate approval transactions
Exact Amounts: Sign permissions for specific amounts instead of infinite approvals
Time Limited: Permits have deadlines for enhanced security
Permit is available for ERC-20 tokens that implement EIP-2612.
Supply Assets
By supplying assets to an Aave market, users earn interest on their deposits according to the reserve's supply APY rate.
When supplying assets to an Aave market, you can use the following methods:
Method | Description | Typical Scenario | |
---|---|---|---|
Direct Supply | Supply ERC-20 or native tokens directly from the sender’s wallet. aTokens are sent to the sender’s address. | User deposits assets into their own wallet. | |
On Behalf of Another | Supply from the sender’s wallet, but send aTokens to another address. | User deposits assets from one wallet and borrow position is owned by another wallet. | |
With Permit | Use a permit signature to skip ERC-20 approval and supply in a single transaction. | User signs a permit and sends a single transaction. No ERC-20 allowance left behind. | |
With Permit On Behalf of Another | Same as permit supply, but aTokens are sent to another address. | No ERC-20 allowance needed and borrow position is owned by another wallet. |
To supply assets to an Aave market, follow these steps.
First, determine which reserve you want to supply assets to.
Let's say we choose the WETH supply reserve within one of the Ethereum markets.
Reserve
const reserve: Reserve = { __typename: "Reserve", market: { __typename: "Market", address: "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2", chainId: 1, // … }, underlyingToken: { __typename: "Currency", symbol: "WETH", name: "Wrapped Ether", address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // … }, acceptsNative: { __typename: "NativeCurrency", symbol: "ETH", name: "Ethereum", // … }, isFrozen: false, isPaused: false, permitSupported: true, userState: { __typename: "ReserveUserState", suppliable: { __typename: "TokenAmount", amount: { __typename: "DecimalValue", value: "1245.67", raw: "1245670000000000000000", decimals: 18, // … }, // … }, }, // …};
To supply to the reserve, you need to verify these conditions:
Reserve.isFrozen is false - the reserve is not frozen
Reserve.isPaused is false - the reserve is not paused
Reserve.userState.suppliable.amount.value is greater than 0 - the amount the user can supply to the reserve given their unique circumstances
Make sure you include a user address when fetching market and reserve data—otherwise Reserve.userState will be empty.
The Reserve.permitSupported flag indicates whether the underlying token supports EIP-2612 permits.
Additionally, if Reserve.acceptsNative is not null, users can choose to supply the asset as the chain's native token and it will be automatically wrapped before being sent to the reserve. This is typical for reserves of the wrapped version of the chain's native token (e.g., WETH on Ethereum).
Next, if all of these conditions are met, we can proceed with creating the execution plan for the supply operation.
- React
- TypeScript
- GraphQL
Use the useSupply hook to prepare supply transactions, optionally on behalf of another address.
If Reserve.permitSupported is true and you supply ERC-20 tokens, you can use the useSupply and useERC20Permit hooks to sign permits and prepare supply transactions. Use the useERC20Permit implementation for the wallet library of choice.
Finally, you need to handle the execution plan, and the process is the same whether or not you used a permit in the previous step.
If you provided a permit signature, the execution plan will run as a single transaction.
- React
- TypeScript
- GraphQL
Use the useSendTransaction hook for the wallet library of your choice to send the transactions in the execution plan.
Viem
import { useWalletClient } from "wagmi";import { errAsync, useSupply } from "@aave/react";import { useSendTransaction } from "@aave/react/viem";
// …
const { data: walletClient } = useWalletClient();
const [supply, supplying] = useSupply();const [sendTransaction, sending] = useSendTransaction(walletClient);
// …
// Optional: combine loading statesconst loading = supplying.loading || sending.loading;const error = supplying.error || sending.error;
// …
const execute = async () => { const result = await supply({ // … }).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("Supply failed:", result.error); } else { console.log("Supply successful with hash:", result.value); }};
Collateral Management
To manage which supplied assets are used as collateral for borrowing, follow these steps.
First, determine which user supply position you want to enable or disable as collateral.
Let's say we identified this MarketUserReserveSupplyPosition object.
MarketUserReserveSupplyPosition
const position: MarketUserReserveSupplyPosition = { __typename: "MarketUserReserveSupplyPosition", market: { __typename: "Market", address: "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2", 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, // … }, // … }, isCollateral: false, // Currently not used as collateral canBeCollateral: true, // Can be enabled as collateral // …};
To enable a position as collateral, you need to verify these conditions:
position.canBeCollateral is true - the asset can be used as collateral
position.isCollateral is false - the asset is not currently used as collateral
To disable a position as collateral, you only need to check if position.isCollateral is true.
A reserve may be disabled from being used as collateral, pending an Aave DAO vote. This prevents new collateral usage of the asset but does not impact existing user positions already using it as collateral.
Next, we can proceed with creating the transaction request for the collateral toggle operation.
- React
- TypeScript
- GraphQL
Use the useCollateralToggle hook to create the transaction request for enabling or disabling collateral.
Toggle Collateral
import { useWalletClient } from "wagmi";import { useCollateralToggle, evmAddress } from "@aave/react";
// …
const { data: walletClient } = useWalletClient();
const [toggleCollateral, toggling] = useCollateralToggle();
const execute = async () => { const result = await toggleCollateral({ market: position.market.address, underlyingToken: position.currency.address, user: evmAddress(walletClient!.account.address), chainId: position.market.chainId, });
// …};
Finally, send the transaction.
- React
- TypeScript
- GraphQL
Use the useSendTransaction hook for the wallet library of your choice to send the transaction.
Viem
import { useWalletClient } from "wagmi";import { useCollateralToggle } from "@aave/react";import { useSendTransaction } from "@aave/react/viem";
// …
const { data: walletClient } = useWalletClient();
const [toggleCollateral, toggling] = useCollateralToggle();const [sendTransaction, sending] = useSendTransaction(walletClient);
// …
// Optional: combine loading statesconst loading = toggling.loading || sending.loading;const error = toggling.error || sending.error;
// …
const execute = async () => { const result = await toggleCollateral({ // … }).andThen(sendTransaction);
if (result.isErr()) { console.error("Collateral toggle failed:", result.error); } else { console.log("Collateral toggle successful with hash:", result.value); }};
Borrow Assets
Users can borrow assets from Aave markets at the reserve’s borrow APY using supply positions as collateral.
When borrowing assets from an Aave market, you can use the following methods:
Method | Description | Typical Scenario | |
---|---|---|---|
Direct Borrow | Borrow assets from a reserve using supplied collateral | User borrows from a reserve against their own supplied collateral. | |
Credit Delegation | Borrow assets from a reserve using delegated borrow credits | User borrows from a reserve using credits from collateral supplied by a credit delegator. |
To borrow assets from an Aave market, follow these steps.
First, determine which reserve you want to borrow assets from.
Let's say we choose the WETH borrow reserve within one of the Ethereum markets.
Reserve
const reserve: Reserve = { __typename: "Reserve", market: { __typename: "Market", address: "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2", chainId: 1, // … }, underlyingToken: { __typename: "Currency", symbol: "WETH", name: "Wrapped Ether", address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // … }, acceptsNative: { __typename: "NativeCurrency", symbol: "ETH", name: "Ethereum", // … }, isFrozen: false, isPaused: false, userState: { __typename: "ReserveUserState", borrowable: { __typename: "TokenAmount", amount: { __typename: "DecimalValue", value: "15.75", raw: "15750000000000000000", decimals: 18, // … }, // … }, }, // …};
To borrow from the reserve, you need to verify these conditions:
Reserve.isFrozen is false - the reserve is not frozen
Reserve.isPaused is false - the reserve is not paused
Reserve.userState.borrowable.amount.value is greater than 0 - the amount the user can borrow from the reserve given their unique circumstances
Make sure you include a user address when fetching market and reserve data—otherwise Reserve.userState will be empty.
Additionally, if Reserve.acceptsNative is not null, users can choose to receive any borrowed assets as the chain's native token and it will be automatically unwrapped before being sent to the user's wallet. This is typical for reserves of the wrapped version of the chain's native token (e.g., WETH on Ethereum).
Next, if all of these conditions are met, we can proceed with creating the execution plan for the borrow operation.
- React
- TypeScript
- GraphQL
Use the useBorrow hook to create the execution plan for a direct borrow operation.
If you intend to leverage credit delegation, you can first check the credit delegation allowance.
Check Credit Delegation Allowance
import { useCreditDelegateeAllowance, evmAddress } from "@aave/react";
// …
const { data: allowance, loading } = useCreditDelegateeAllowance({ market: reserve.market.address, underlyingToken: reserve.underlyingToken.address, user: evmAddress("0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234"), // Delegator delegatee: evmAddress(walletClient!.account.address), // Your address chainId: reserve.market.chainId,});
if (allowance && BigInt(allowance.amount.raw) > 0n) { console.log("Available delegation allowance:", allowance.amount.value);} else { console.log("No credit delegation allowance available");}
And then, you can proceed with preparing the execution plan for the borrow operation.
Borrow with Credit Delegation
import { useWalletClient } from "wagmi";import { useBorrow, bigDecimal, evmAddress } from "@aave/react";
// …
const { data: walletClient } = useWalletClient();
const [borrow, borrowing] = useBorrow();
const execute = async () => { const result = await borrow({ market: reserve.market.address, amount: { erc20: { currency: reserve.underlyingToken.address, value: bigDecimal(2), // 2 WETH }, }, sender: evmAddress(walletClient!.account.address), chainId: reserve.market.chain.chainId, onBehalfOf: evmAddress("0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234"), // Delegator });
// …};
Finally, handle the execution plan.
- React
- TypeScript
- GraphQL
Use the useSendTransaction hook for the wallet library of your choice to send the transactions in the execution plan.
Viem
import { useWalletClient } from "wagmi";import { errAsync, useBorrow } from "@aave/react";import { useSendTransaction } from "@aave/react/viem";
// …
const { data: walletClient } = useWalletClient();
const [borrow, borrowing] = useBorrow();const [sendTransaction, sending] = useSendTransaction(walletClient);
// …
// Optional: combine loading statesconst loading = borrowing.loading || sending.loading;const error = borrowing.error || sending.error;
// …
const execute = async () => { const result = await borrow({ // … }).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("Borrow failed:", result.error); } else { console.log("Borrow successful with hash:", result.value); }};
Credit Delegation
Credit delegation allows a user to approve another user (delegatee) to borrow assets against their own supply position. The supply position must be enabled as collateral.
To delegate credit to another address, follow these steps.
First, determine which user supply position you want to delegate credit from.
Let's say we identified this MarketUserReserveSupplyPosition object.
MarketUserReserveSupplyPosition
const position: MarketUserReserveSupplyPosition = { __typename: "MarketUserReserveSupplyPosition", market: { __typename: "Market", address: "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2", 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, // … }, // … }, isCollateral: true, canBeCollateral: true, // …};
Ensure MarketUserReserveSupplyPosition.isCollateral is true, if not enable it as collateral first.
Next, we can proceed with creating the transaction request for the credit delegation operation.
- React
- TypeScript
- GraphQL
Use the useApproveBorrowCreditDelegation hook to create the transaction request for approving credit delegation.
Approve Credit Delegation
import { useWalletClient } from "wagmi";import { useApproveBorrowCreditDelegation, evmAddress } from "@aave/react";
// …
const { data: walletClient } = useWalletClient();
const [approveDelegation, approving] = useApproveBorrowCreditDelegation();
const execute = async () => { const result = await approveDelegation({ market: position.market.address, underlyingToken: position.currency.address, amount: "1000", user: evmAddress(walletClient!.account.address), delegatee: evmAddress("0x5678..."), chainId: position.market.chainId, });
// …};
Finally, send the transaction.
- React
- TypeScript
- GraphQL
Use the useSendTransaction hook for the wallet library of your choice to send the transaction.
Viem
import { useWalletClient } from "wagmi";import { useApproveBorrowCreditDelegation } from "@aave/react";import { useSendTransaction } from "@aave/react/viem";
// …
const { data: walletClient } = useWalletClient();
const [approveDelegation, approving] = useApproveBorrowCreditDelegation();const [sendTransaction, sending] = useSendTransaction(walletClient);
// …
// Optional: combine loading statesconst loading = approving.loading || sending.loading;const error = approving.error || sending.error;
// …
const execute = async () => { const result = await approveDelegation({ // … }).andThen(sendTransaction);
if (result.isErr()) { console.error("Credit delegation failed:", result.error); } else { console.log("Credit delegation successful with hash:", result.value); }};
That's it—the delegatee can now borrow against the user's supply position.
Repay Loans
By repaying borrowed assets to an Aave market, users reduce or clear their debt position.
When repaying borrowed assets to an Aave market, you can use the following methods:
Method | Description | Typical Scenario | |
---|---|---|---|
Direct Repay | Repay ERC-20 or native tokens directly from the sender's wallet. | User repays borrowed assets from their own wallet balance. | |
On Behalf of Another | Repay debt for another address. | User borrowed with one wallet and wants to repay from a different wallet. | |
With Permit | Use a permit signature to skip ERC-20 approval and repay in a single transaction. | User signs a permit and sends a single transaction. No ERC-20 allowance left behind. | |
With Permit On Behalf of Another | Same as permit repay, but the debt position is owned by another wallet. | No ERC-20 allowance needed when repaying debt for another wallet. | |
To repay borrowed assets from an Aave market, follow these steps.
First, determine which user borrow position you want to repay debt for.
Let's say we identified this MarketUserReserveBorrowPosition object.
MarketUserReserveBorrowPosition
const position: MarketUserReserveBorrowPosition = { __typename: "MarketUserReserveBorrowPosition", market: { __typename: "Market", address: "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2", chainId: 1, // … }, currency: { __typename: "Currency", symbol: "WETH", name: "Wrapped Ether", address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // … }, debt: { __typename: "TokenAmount", amount: { __typename: "DecimalValue", value: "1.5", raw: "1500000000000000000", decimals: 18, // … }, // … }, // …};
That is tied to the following Reserve object.
Reserve
const reserve: Reserve = { __typename: "Reserve", market: { __typename: "Market", address: "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2", chainId: 1, // … }, underlyingToken: { __typename: "Currency", symbol: "WETH", name: "Wrapped Ether", address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // … }, permitSupported: true, acceptsNative: { __typename: "NativeCurrency", // … },};
The Reserve.permitSupported flag indicates whether the underlying token supports EIP-2612 permits.
Additionally, if Reserve.acceptsNative is not null, users can choose to repay the borrow position as the chain's native token and it will be automatically wrapped before being sent to the reserve. This is typical for reserves of the wrapped version of the chain's native token (e.g., WETH on Ethereum).
Next, we can proceed with creating the execution plan for the repay operation.
- React
- TypeScript
- GraphQL
Use the useRepay hook to create the execution plan for repaying debt from a market reserve, optionally on behalf of another address.
If Reserve.permitSupported is true and your borrow position is an ERC-20 token, you can use the useRepay and useERC20Permit hooks to sign permits and prepare repay transactions. Use the useERC20Permit implementation for the wallet library of choice.
Finally, handle the execution plan.
- React
- TypeScript
- GraphQL
Use the useSendTransaction hook for the wallet library of your choice to send the transactions in the execution plan.
Viem
import { useWalletClient } from "wagmi";import { errAsync, useRepay } from "@aave/react";import { useSendTransaction } from "@aave/react/viem";
// …
const { data: walletClient } = useWalletClient();
const [repay, repaying] = useRepay();const [sendTransaction, sending] = useSendTransaction(walletClient);
// …
// Optional: combine loading statesconst loading = repaying.loading || sending.loading;const error = repaying.error || sending.error;
// …
const execute = async () => { const result = await repay({ // … }).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("Repay failed:", result.error); } else { console.log("Repay successful with hash:", result.value); }};
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.
First, determine which user supply position you want to withdraw assets from.
Let's say we identified this MarketUserReserveSupplyPosition object.
MarketUserReserveSupplyPosition
const position: MarketUserReserveSupplyPosition = { __typename: "MarketUserReserveSupplyPosition", market: { __typename: "Market", address: "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2", 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: "Market", address: "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2", chainId: 1, // … }, underlyingToken: { __typename: "Currency", symbol: "WETH", name: "Wrapped Ether", address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // … }, acceptsNative: { __typename: "NativeCurrency", // … },};
Next, we can proceed with creating the execution plan for the withdraw operation.
- React
- TypeScript
- GraphQL
Use the useWithdraw hook to create the execution plan for withdrawing assets from a market reserve.
If Reserve.acceptsNative is not null, you can choose to withdraw the assets using the chain's native token.
Finally, handle the execution plan.
- React
- TypeScript
- GraphQL
Use the useSendTransaction hook for the wallet library of your choice to send the transactions in the execution plan.
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 statesconst 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); }};
Advanced Operations
Health Factor Preview
The health factor is calculated only when a user has at least one active borrow position. If the user has no positions or only supply positions, the health factor will be null.
To preview how a user's health factor will change before executing a market operation (supply, borrow, repay, withdraw).
- React
- TypeScript
- GraphQL
Use the useHealthFactorPreview hook to preview the health factor after the operation.