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 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: 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 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.

To supply assets to an Aave reserve with AaveKit React, 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 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 "ApprovalRequired":      return sendTransaction(plan.transaction);  }});

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

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: BigDecimal(2000000000.0), // 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.

The process outlined here assumes you have familiarity with the standard supply operation flow.

1

Configure Wallet Integration

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

2

Generate an ERC-20 Permit Signature

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

3

Execute the Supply Operation

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.