Repay Loans
Learn how to repay borrowed assets to Aave v4 reserves.
Repaying borrowed assets allows you to:
Reduce or eliminate your debt position
Improve your position's health factor and reduce liquidation risk
Free up collateral for withdrawal or additional borrowing
Close your borrow position entirely when repaying the full amount
Repaying borrowed assets improves the position's health factor and reduces liquidation risk. You can repay partial amounts or the full debt amount.
Repaying
Repaying a loan can be broken down into the following steps:
Identify the borrow position to repay
Preview the impact of the repay operation
Repay the borrowed assets
Identify the Borrow Position
Given a list of the user's borrow positions, identify the position (token) you want to reduce and choose the repayment amount (partial or full).
The borrowed position reserve should not be paused in order to repay.
For example, let’s say you have identified the following UserBorrowItem object.
Example UserBorrowItem
const borrowPosition: UserBorrowItem = { reserve: { id: "SGVsbG8h", onChainId: "42", chain: { chainId: 1, name: "Ethereum", }, spoke: { address: "0x123…", // … }, asset: { underlying: { address: "0xa0b86a33e6e2ad05ad6c9ac3b6e5e5f6e7b6c1b2", // USDC }, // … }, status: { paused: false, }, // … other reserve properties }, debt: { amount: { value: BigDecimal(512.023456), // ~512 USDC debt // … }, // … }, // …};Keep in mind that repaying:
Improves your health factor, reducing liquidation risk.
Frees up collateral, making it available for withdrawal or additional borrowing.
Can be partial (reducing debt but keeping the position open) or full (closing the borrow position entirely).
Preview Repay
Preview the impact of a repay operation before committing to it.
- React
- TypeScript
- GraphQL
- Solidity
Use the usePreview hook (or the imperative usePreviewAction variant) to preview the impact of the repay operation on the user's position.
Where the RepayRequest can be as follows:
The PreviewUserPosition shows the impact of the repay operation by comparing current and after states, with the table below outlining key fields and how to interpret them.
| Field | Impact | |
|---|---|---|
| healthFactor.[current → after]: BigDecimal|null | Higher is better (null if not applicable) | |
| riskPremium.[current → after]: PercentNumber | Lower is better | |
| netApy.[current → after]: PercentNumber | Higher is better | |
| netCollateral.[current → after]: ExchangeAmount | Higher is better | |
| netBalance.[current → after]: ExchangeAmount | Updated balance | |
| borrowingPower.[current → after]: ExchangeAmount | New borrowing power |
You can also specify a different currency to return fiat amounts in.
Step-by-Step
Now that we know how to identify a borrow position to repay, and we know how to preview the impact of a repay operation, let's see how to repay assets for this position.
Repaying ERC-20 tokens requires token approval. You can choose between two approaches:
Transaction-based approval — Sends a separate ERC-20 approve() transaction before the repay transaction (2 transactions total).
Permit-based approval — Signs an EIP-2612 permit to approve and repay in a single transaction. More gas-efficient, but requires the token to support permits.
- React
- TypeScript
- GraphQL
- Solidity
To repay a loan with AaveKit React, follow these steps.
First, instantiate the hooks for the wallet library of your choice:
useSendTransaction — used to send ERC-20 approval and repay 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);Then, use the useRepay hook to prepare the repay operation.
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.
Finally, handle the result.
Example
const execute = async () => { const result = await repay(/* … */);
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("Repay 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 = { repay: { sender: evmAddress("0x123…"), // User's address reserve: borrowPosition.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 repay the debt using the chain's native token with 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 Borrow Position
const borrowPosition: UserBorrowItem = { reserve: { id: "SGVsbG8h", onChainId: "42", 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