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

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 ?? "N/A"}</p>      <p>To: {data.healthFactor?.after ?? "N/A"}</p>
      <h3>Risk Premium:</h3>      <p>From: {data.riskPremium.current.value}</p>      <p>To: {data.riskPremium.after.value}</p>
      <h3>Net APY:</h3>      <p>From: {data.netApy.current.value}</p>      <p>To: {data.netApy.after.value}</p>
      <h3>Net Collateral:</h3>      <p>From: {data.netCollateral.current.value}</p>      <p>To: {data.netCollateral.after.value}</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    },  },};

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

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 → AfterImpact
healthFactor?.current → healthFactor?.after: BigDecimalHigher is better
riskPremium.current → riskPremium.after: PercentNumberLower is better
netApy.current → netApy.after: PercentNumberHigher is better
netCollateral.current → netCollateral.after: FiatAmountHigher is better
netBalance.current → netBalance.after: FiatAmountUpdated 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.

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: ${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:

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

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

import { type SetUserSupplyAsCollateralRequest, usePreview } from "@aave/react";
function CollateralPreview({  request,}: {  request: SetUserSupplyAsCollateralRequest;}) {  const { data, error, loading } = usePreview({    action: {      setUserSupplyAsCollateral: 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>Risk Premium:</h3>      <p>From: {data.riskPremium.current.value}</p>      <p>To: {data.riskPremium.after.value}</p>
      <h3>Net Collateral:</h3>      <p>From: {data.netCollateral.current.value}</p>      <p>To: {data.netCollateral.after.value}</p>    </div>  );}

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.

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

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 → AfterImpact
healthFactor?.current → healthFactor?.after: BigDecimalHigher is better
riskPremium.current → riskPremium.after: PercentNumberLower is better
netCollateral.current → netCollateral.after: FiatAmountHigher is better

Step-by-Step

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

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 useSetUserSupplyAsCollateral hook to prepare the transaction request.

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

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

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.

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.