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:

  1. Identify the Reserve

  2. Preview the impact of the supply operation (optional)

  3. 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: BigDecimal(2000000000.0), // 2B USDC    // …  },  {    id: "V29ybGQh",    onChainId: "43",    canSupply: false, // cannot supply to this reserve    supplyCap: BigDecimal(1000000000.0), // 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 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: BigDecimal(2000000000.0), // 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: BigDecimal(1000.0), // 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.

Use the usePreview hook (or the imperative usePreviewAction variant) to preview the impact of the supply operation on the user's position.

import { type SupplyRequest, usePreview } from "@aave/react";
function SupplyPreview({ request }: { request: SupplyRequest }) {  const { data, error, loading } = usePreview({    action: {      supply: request,    },  });
  if (loading) return <div>Loading…</div>;  if (error) return <div>Error: {error.message}</div>;
  // data: PreviewUserPosition  return (    <div>      <h3>Health Factor:</h3>      <p>From: {data.healthFactor.current?.value.toFixed(2) ?? "N/A"}</p>      <p>To: {data.healthFactor.after?.value.toFixed(2) ?? "N/A"}</p>
      <h3>User Risk Premium:</h3>      <p>From: {data.riskPremium.current.normalized.toFixed(2)}%</p>      <p>To: {data.riskPremium.after.normalized.toFixed(2)}%</p>
      <h3>Net APY:</h3>      <p>From: {data.netApy.current.normalized.toFixed(2)}%</p>      <p>To: {data.netApy.after.normalized.toFixed(2)}%</p>
      <h3>Net Collateral:</h3>      <p>From: {data.netCollateral.current.value.toDisplayString(2)}</p>      <p>To: {data.netCollateral.after.value.toDisplayString(2)}</p>
      <h3>Borrowing Power:</h3>      <p>From: {data.borrowingPower.current.value.toDisplayString(2)}</p>      <p>To: {data.borrowingPower.after.value.toDisplayString(2)}</p>    </div>  );}

See below for examples of SupplyRequest objects.

const request: SupplyRequest = {  sender: evmAddress("0x123…"), // User's address  reserve: reserve.id,  chainId: reserve.chain.chainId,  amount: {    erc20: {      value: bigDecimal(42), // USDC    },  },};

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.

FieldImpact
healthFactor.[current → after]: BigDecimal|nullHigher is better
(null if not applicable)
netApy.[current → after]: PercentNumberHigher is better
netCollateral.[current → after]: ExchangeAmountHigher is better
netBalance.[current → after]: ExchangeAmountUpdated balance
projectedEarning.[current → after]: ExchangeAmountProjected earnings
borrowingPower.[current → after]: ExchangeAmountBorrowing power

Supplying assets as collateral updates the Dynamic Config just for the reserve being supplied as collateral.

The otherConditions field is an array of objects describing the resulting dynamic config changes.

  • CollateralFactorVariation – Collateral factor change

  • LiquidationFeeVariation – Liquidation fee change

  • MaxLiquidationBonusVariation – Maximum liquidation bonus change

You can also specify a different currency to return fiat amounts in.

import { Currency } from "@aave/react";
const { data, error, loading } = usePreview({  action: {    supply: request,  },  currency: Currency.Eur,});

Step-by-Step

Now that you know how to identify a reserve and preview the impact of a supply operation, let's supply assets to a reserve.

Supplying ERC-20 tokens requires token approval. You can choose between two approaches:

  • Transaction-based approval — Sends a separate ERC-20 approve() transaction before the supply transaction (2 transactions total).

  • Permit-based approval — Signs an EIP-2612 permit to approve and supply in a single transaction. More gas-efficient, but requires the token to support permits.

To supply assets to an Aave reserve with AaveKit React, follow these steps.

1

Configure Wallet Integration

First, instantiate the hooks for the wallet library of your choice:

  • useSendTransaction — used to send ERC-20 approval and supply transactions

  • useSignTypedData — used to sign ERC-20 permits when available

Viem
import { useWalletClient } from "wagmi";import { useSendTransaction, useSignTypedData } from "@aave/react/viem";
// …
const { data: wallet } = useWalletClient();const [sendTransaction] = useSendTransaction(wallet);const [signTypedData] = useSignTypedData(wallet);

2

Define the Supply Flow

Then, use the useSupply hook to prepare the supply operation.

import { useSupply } from "@aave/react";
const [supply, { loading, error }] = useSupply((plan) => {  switch (plan.__typename) {    case "TransactionRequest":      return sendTransaction(plan);
    case "Erc20Approval":      // If token supports EIP-2612 permits, sign permit (recommended)      if (plan.bySignature) {        return signTypedData(plan.bySignature);      }      // use traditional approval transaction      return sendTransaction(plan.byTransaction);
    case "PreContractActionRequired":      return sendTransaction(plan.transaction);  }});

In the Erc20Approval case, the bySignature field is only available if the token supports EIP-2612 permits.

For tokens like USDT on Ethereum Mainnet that require an allowance reset, the hook calls your callback twice: once to reset the allowance to 0, then again to set the new value. bySignature will be null for these approvals.

3

Execute the Supply Operation

Then, execute the supply operation.

import { bigDecimal, evmAddress } from "@aave/react";
const execute = async () => {  const result = await supply({    sender: evmAddress(wallet.account.address), // User's address    reserve: reserve.id,    amount: {      erc20: {        value: bigDecimal(42), // 42 USDC      },    },  });
  // …};

4

Handle the Result

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:",          `required: ${result.error.cause.required.value.toDisplayString(2)}`,          `available: ${result.error.cause.available.value.toDisplayString(2)}`,        );        break;
      case "UnexpectedError":        console.error(result.error.message);        break;    }    return;  }
  console.log("Supply successful with hash:", result.value);};

Collateral Management

Manage how user supplies are used as collateral for borrowing. The process can be broken down into three steps:

  1. Identify the supply position

  2. Preview the impact of changing collateral status

  3. 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: BigDecimal(42.0),    },    // …  },  interest: {    amount: {      value: BigDecimal(4.2),    },    // …  },};

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.

Use the usePreview hook (or the imperative usePreviewAction variant) to preview the impact of collateral status changes on the user's position.

import {  type SetUserSuppliesAsCollateralRequest,  usePreview,} from "@aave/react";
function CollateralPreview({  request,}: {  request: SetUserSuppliesAsCollateralRequest;}) {  const { data, error, loading } = usePreview({    action: {      setUserSuppliesAsCollateral: request,    },  });
  if (loading) return <div>Loading…</div>;  if (error) return <div>Error: {error.message}</div>;
  // data: PreviewUserPosition  return (    <div>      <h3>Health Factor:</h3>      <p>From: {data.healthFactor?.current ?? "N/A"}</p>      <p>To: {data.healthFactor?.after ?? "N/A"}</p>
      <h3>User Risk Premium:</h3>      <p>From: {data.riskPremium.current.normalized.toFixed(2)}%</p>      <p>To: {data.riskPremium.after.normalized.toFixed(2)}%</p>
      <h3>Net Collateral:</h3>      <p>From: {data.netCollateral.current.value.toDisplayString(2)}</p>      <p>To: {data.netCollateral.after.value.toDisplayString(2)}</p>
      <h3>Borrowing Power:</h3>      <p>From: {data.borrowingPower.current.value.toDisplayString(2)}</p>      <p>To: {data.borrowingPower.after.value.toDisplayString(2)}</p>    </div>  );}

Where the SetUserSuppliesAsCollateralRequest can be as follows:

const request: SetUserSuppliesAsCollateralRequest = {  sender: evmAddress("0x123…"), // User's address  changes: [    {      reserve: supplyPosition.reserve.id,      enableCollateral: true, // false to disable collateral    },  ],};

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.

FieldImpact
healthFactor.[current → after]: BigDecimal|nullHigher is better
(null if not applicable)
riskPremium.[current → after]: PercentNumberLower is better
netApy.[current → after]: PercentNumberHigher is better
netCollateral.[current → after]: ExchangeAmountHigher is better
borrowingPower.[current → after]: ExchangeAmountBorrowing power
otherConditions: UserPositionConditionVariation[]Dynamic config changes

Enabling collateral updates the Dynamic Config just for the reserve being enabled, while disabling collateral updates the Dynamic Config for all reserves in which the user has supplies or borrows within the same user position.

The otherConditions field is an array of objects describing the resulting dynamic config changes.

  • CollateralFactorVariation – Collateral factor change

  • LiquidationFeeVariation – Liquidation fee change

  • MaxLiquidationBonusVariation – Maximum liquidation bonus change

You can also specify a different currency to return fiat amounts in.

import { Currency } from "@aave/react";
const { data, error, loading } = usePreview({  action: {    setUserSuppliesAsCollateral: request,  },  currency: Currency.Eur,});

Step-by-Step

Toggle the collateral status of any supplied asset using the following steps.

Enabling collateral updates the Dynamic Config—Collateral Factor, Liquidation Fee, and Max Liquidation Bonus—for the supplied asset. Disabling collateral is a risk-increasing action that updates the Dynamic Config and, consequently, the User Risk Premium for the entire user position. See User Position Conditions for more details.

To enable or disable a supplied asset as collateral, follow these steps.

1

Configure Wallet Integration

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);

2

Define the Collateral Management Flow

Use the useSetUserSuppliesAsCollateral hook to prepare the transaction request.

import { useSetUserSuppliesAsCollateral } from "@aave/react";
const [setAsCollateral, { loading, error }] = useSetUserSuppliesAsCollateral(  (transaction) => sendTransaction(transaction),);

3

Execute the Transaction

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    changes: [      {        reserve: supplyPosition.reserve.id,        enableCollateral: true,      },    ],  });};
// …

4

Handle the Result

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.exchange.symbol}        {fee.exchange.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: BigDecimal(2000000000.0), // 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.

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    },  });
  // …};