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#
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).
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 |
maxBorrowingPower.[current → after]: ExchangeAmount | Maximum borrowing power |
remainingBorrowingPower.[current → after]: ExchangeAmount | Remaining 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.
1
Configure Wallet Integration#
First, instantiate the hooks for the wallet library of your choice:
useSendTransaction— used to send ERC-20 approval and repay transactionsuseSignTypedData— 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 Repay Flow#
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.
bySignature will
be null for these approvals.3
Execute the Repay Operation#
Then, execute the desired repay operation.
4
Handle the Result#
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.txHash);};Advanced Usage#
Network Fee#
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