Supply Assets
Learn how to earn interest on Aave v4 and use assets as collateral for borrowing.
Supplying assets to Aave v4 allows you to:
Receive reserve shares representing your deposit
Earn variable supply APY on your reserve shares
Use reserve shares as collateral for borrowing
Withdraw assets and earnings anytime
Supplying assets as collateral while having an open borrow position may increase the position's health factor.
Supplying
Supplying assets to Aave can be broken down into the following steps:
Identify the Reserve
Preview the impact of the supply operation (optional)
Supply the assets
Identify the Reserve
The first step is to filter the supply reserves to those marked with canSupply: true.
Reserves List
const reserves: Reserve[] = [ { id: "SGVsbG8h", onChainId: "42", canSupply: true, supplyCap: "2000000000.000000", // 2B USDC // … }, { id: "V29ybGQh", onChainId: "43", canSupply: false, // cannot supply to this reserve supplyCap: "1000000000.000000", // 1B USDC // … }, // …];The canSupply flag confirms that the reserve is active: it isn’t frozen, it isn’t paused, and the supply cap has not been reached.
From the remaining supplyable reserves, choose one that meets these criteria:
Collateral Enabled: The reserve can be used as collateral (canUseAsCollateral) if you plan to borrow against it.
Suppliable Amount: The userState.suppliable value is sufficient for the amount you want to supply.
Example Reserve Data
const reserve: Reserve = { id: "SGVsbG8h", onChainId: "42", supplyCap: "2000000000.000000", // 2B USDC canSupply: true, canUseAsCollateral: true, chain: { chainId: 1, name: "Ethereum", }, spoke: { address: "0x123…", // … }, status: { frozen: false, paused: false, }, userState: { suppliable: { amount: { onChainValue: "1000000000", decimals: 6, value: "1000.000000", // Can supply up to 1,000 USDC }, // … }, // … }, // …};Make sure you include a user address when fetching reserve data—otherwise userState will be null.
Preview Supply
Preview the impact of a supply operation before committing to it.
- React
- TypeScript
- GraphQL
- Solidity
Use the usePreview hook (or the imperative usePreviewAction variant) to preview the impact of the supply operation on the user's position.
See below for examples of SupplyRequest objects.
You can also specify a different currency to return fiat amounts in.
The PreviewUserPosition shows the impact of the supply operation by comparing current and after states, with the table below outlining key fields and how to interpret them.
| Current → After | Impact | |
|---|---|---|
| healthFactor?.current → healthFactor?.after: BigDecimal | Higher is better | |
| riskPremium.current → riskPremium.after: PercentNumber | Lower is better | |
| netApy.current → netApy.after: PercentNumber | Higher is better | |
| netCollateral.current → netCollateral.after: FiatAmount | Higher is better | |
| netBalance.current → netBalance.after: FiatAmount | Updated balance |
Step-by-Step
Now that we know how to identify a reserve to supply to, and you know how to preview the impact of a supply operation, let's see how to supply assets to this reserve.
- React
- TypeScript
- GraphQL
- Solidity
To supply assets to an Aave reserve with AaveKit React, follow these steps.
First, instantiate the useSendTransaction hook for the wallet library of your choice.
Viem
import { useWalletClient } from "wagmi";import { useSendTransaction } from "@aave/react/viem";
// …
const { data: wallet } = useWalletClient();const [sendTransaction] = useSendTransaction(wallet);Finally, handle the result.
Example
const execute = async () => { const result = await supply(/* … */);
if (result.isErr()) { switch (result.error.name) { case "CancelError": // The user cancelled the operation return;
case "SigningError": console.error( `Failed to sign the transaction: ${result.error.message}` ); break;
case "TimeoutError": console.error(`Transaction timed out: ${result.error.message}`); break;
case "TransactionError": console.error(`Transaction failed: ${result.error.message}`); break;
case "ValidationError": console.error( `Insufficient balance: ${result.error.cause.required.value} required.` ); break;
case "UnexpectedError": console.error(result.error.message); break; } return; }
console.log("Supply successful with hash:", result.value);};Collateral Management
Manage how your supplied assets are used as collateral for borrowing. The process can be broken down into three steps:
Identify the supply position
Preview the impact of changing collateral status
Toggle the collateral status
Identify the Supply Position
First, identify the supply position you want to modify from the user's supply positions.
UserSupplyItem
const supplyPosition: UserSupplyItem = { reserve: { id: "SGVsbG8h", onChainId: "42", chain: { chainId: 1, name: "Ethereum", }, spoke: { address: "0x123…", // … }, canUseAsCollateral: true, // … }, isCollateral: false, principal: { amount: { value: "42.000000", }, // … }, interest: { amount: { value: "4.200000", }, // … },};The corresponding reserve needs to have canUseAsCollateral: true to be possible to use as collateral.
Preview Changes
Preview the impact of changing collateral status before executing the transaction.
Disabling a supplied asset as collateral may reduce the position's health factor. In some cases, this may put the position at risk of being liquidated.
- React
- TypeScript
- GraphQL
- Solidity
Use the usePreview hook (or the imperative usePreviewAction variant) to preview the impact of collateral status changes on the user's position.
Where the SetUserSupplyAsCollateralRequest can be as follows:
SetUserSupplyAsCollateralRequest
const request: SetUserSupplyAsCollateralRequest = { sender: evmAddress("0x123…"), // User's address reserve: supplyPosition.reserve.id, enableCollateral: true, // false to disable collateral};You can also specify a different currency to return fiat amounts in.
You can also specify a different currency to return fiat amounts in.
The PreviewUserPosition shows the impact of the collateral status change by comparing current and after states, with the table below outlining key fields and how to interpret them.
| Current → After | Impact | |
|---|---|---|
| healthFactor?.current → healthFactor?.after: BigDecimal | Higher is better | |
| riskPremium.current → riskPremium.after: PercentNumber | Lower is better | |
| netCollateral.current → netCollateral.after: FiatAmount | Higher is better |
Step-by-Step
Toggle the collateral status of any supplied asset using the following steps.
- React
- TypeScript
- GraphQL
- Solidity
To enable or disable a supplied asset as collateral, follow these steps.
First, instantiate the useSendTransaction hook for the wallet library of your choice.
Viem
import { useWalletClient } from "wagmi";import { useSendTransaction } from "@aave/react/viem";
// …
const { data: wallet } = useWalletClient();const [sendTransaction] = useSendTransaction(wallet);Use the useSetUserSupplyAsCollateral hook to prepare the transaction request.
Then, update the collateral status.
Set Collateral
import { evmAddress } from "@aave/react";
const execute = async () => { const result = await setAsCollateral({ sender: evmAddress(wallet.account.address), // User's address reserve: supplyPosition.reserve.id, enableCollateral: true, });};
// …Finally, handle the result.
Example
const execute = async () => { const result = await setAsCollateral(/* … */);
if (result.isErr()) { switch (result.error.name) { case "CancelError": // The user cancelled the operation return;
case "SigningError": console.error( `Failed to sign the transaction: ${result.error.message}` ); break;
case "TimeoutError": console.error(`Transaction timed out: ${result.error.message}`); break;
case "TransactionError": console.error(`Transaction failed: ${result.error.message}`); break;
case "UnexpectedError": console.error(result.error.message); break; } return; }
console.log("Collateral set successful with hash:", result.value);};Advanced Usage
Network Fee
This experimental AaveKit React hook currently works only with Viem or Wagmi integrations. Support for additional wallet libraries may be added later.
Estimate the network cost of any action using the same PreviewAction you pass to the usePreview hook.
Let's consider the following example:
PreviewAction
import { type PreviewAction } from "@aave/react";
const action: PreviewAction = { supply: { sender: evmAddress("0x123…"), // User's address reserve: supplyPosition.reserve.id, amount: { erc20: { value: bigDecimal(42), // USDC }, }, },};Use the useNetworkFee hook to estimate both the network fee for the provided action and its fiat equivalent.
Viem
import { type PreviewAction, Currency } from "@aave/react";import { useNetworkFee } from "@aave/react/viem";
function NetworkFee({ action }: { action: PreviewAction }) { const { data: fee, loading, error, } = useNetworkFee({ query: { estimate: action }, currency: Currency.Eur, });
if (loading) return <p>Loading fee…</p>; if (error) return <p>Error: {error.message}</p>;
return ( <p> Network Fee: {fee.amount.value.toDisplayString(2)} {fee.token.info.symbol} <span> ≈{fee.fiatAmount.symbol} {fee.fiatAmount.value.toDisplayString(2)} </span> </p> );}Native Tokens
When the Reserve's underlying token is the wrapped version of the chain's native token (e.g., WETH on Ethereum), you can supply the asset as the chain's native token using the Native Token Gateway.
Use the reserve.asset.underlying.isWrappedNativeToken flag to determine if the underlying token is a wrapped native token. The Native Gateway address is available from the chain details.
WETH Reserve
const reserve: Reserve = { id: "SGVsbG8h", onChainId: "42", supplyCap: "2000000000.000000", // 2B WETH canSupply: true, canUseAsCollateral: true, asset: { underlying: { address: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", info: { name: "Wrapped Ether", symbol: "WETH", decimals: 18, // … }, isWrappedNativeToken: true, // … }, // … }, spoke: { address: "0x123…", // … }, chain: { chainId: 1, name: "Ethereum", nativeGateway: "0xabc…", }, // …};Specify the amount in the amount field as a native value.
- React
- TypeScript
- GraphQL
- Solidity
Supply Native
const execute = async () => { const result = await supply({ sender: evmAddress(wallet.account.address), // User's address reserve: reserve.id, amount: { native: bigDecimal(42), // 42 ETH }, });
// …};ERC-20 Permits
Supply operations support ERC-20 permit functionality using EIP-2612 signatures to authorize ERC-20 token transfers within the same transaction. The permit approach has a few benefits:
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
Use the asset.underlying.permitSupported flag to determine if the underlying token supports EIP-2612 permits.
USDC Reserve
const reserve: Reserve = { id: "SGVsbG8h", onChainId: "42", supplyCap: "2000000000.000000", // 2B USDC canSupply: true, canUseAsCollateral: true, asset: { underlying: { address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", info: { name: "USD Coin", symbol: "USDC", decimals: 6, // … }, permitSupported: true, // … }, // … }, chain: { chainId: 1, name: "Ethereum", signatureGateway: "0xabc…", }, // …};If the underlying token supports EIP-2612 permits, you can use this alternate approach to supply ERC-20 tokens.
- React
- TypeScript
- GraphQL
The process outlined here assumes you have familiarity with the standard supply operation flow.
First, instantiate the useERC20Permit hook for the wallet library of your choice.
Viem
import { useWalletClient } from "wagmi";import { useERC20Permit } from "@aave/react/viem";
// …
const { data: wallet } = useWalletClient();const [signERC20Permit] = useERC20Permit(wallet);Then, use generate the ERC-20 Permit signature for the desired supply operation.
Sign ERC-20 Permit
const execute = async () => { const result = await signERC20Permit({ supply: { sender: evmAddress(wallet.account.address), // User's address reserve: reserve.id, amount: { value: bigDecimal(42), // 42 USDC }, }, });
// …};Finally, execute the supply operation providing the permit signature as part of the ERC-20 amount input.
Supply ERC-20
const [supply, { loading, error }] = useSupply((plan) => { switch (plan.__typename) { case "TransactionRequest": return sendTransaction(plan); case "ApprovalRequired": return sendTransaction(plan.transaction); }});
const execute = async () => { const result = await signERC20Permit({ sender: evmAddress(wallet.account.address), // User's address reserve: { reserveId: reserve.id, chainId: reserve.chain.chainId, spoke: reserve.spoke.address, }, amount: { value: bigDecimal(42), // 42 USDC }, }).andThen((permitSig) => supply({ sender: evmAddress(wallet.account.address), // User's address reserve: { reserveId: reserve.id, chainId: reserve.chain.chainId, spoke: reserve.spoke.address, }, amount: { erc20: { permitSig, value: bigDecimal(42), // 42 USDC }, }, }) );
// …};Handle the result object as described in the standard supply operation flow.
While the useSupply handler supports both TransactionRequest and ApprovalRequired cases, this particular supply operation will always execute only the TransactionRequest path.