Borrow Assets on Aave V3
Borrow assets from Aave V3 markets against your collateral, or delegate your borrowing power.
Borrow Assets
Users can borrow assets from Aave markets at the reserve’s borrow APY using supply positions as collateral.
When borrowing assets from an Aave market, you can use the following methods:
| Method | Description | Typical Scenario |
|---|---|---|
| Direct Borrow | Borrow assets from a reserve using supplied collateral | User borrows from a reserve against their own supplied collateral. |
| Credit Delegation | Borrow assets from a reserve using delegated borrow credits | User borrows from a reserve using credits from collateral supplied by a credit delegator. |
To borrow assets from an Aave market, follow these steps.
1. Identify the Borrow Reserve
First, determine which reserve you want to borrow assets from.
Given this Reserve object for WETH on an Ethereum market:
const reserve: Reserve = {
__typename: "Reserve",
market: {
__typename: "MarketInfo",
address: "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2",
chain: { chainId: 1 },
// …
},
underlyingToken: {
__typename: "Currency",
symbol: "WETH",
name: "Wrapped Ether",
address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
// …
},
acceptsNative: {
__typename: "NativeCurrency",
symbol: "ETH",
name: "Ethereum",
// …
},
isFrozen: false,
isPaused: false,
userState: {
__typename: "ReserveUserState",
borrowable: {
__typename: "TokenAmount",
amount: {
__typename: "DecimalValue",
value: "15.75",
raw: "15750000000000000000",
decimals: 18,
// …
},
// …
},
},
// …
};
To borrow from the reserve, you need to verify these conditions:
Reserve.isis Frozen false- the reserve is not frozenReserve.isis Paused false- the reserve is not pausedReserve.is greater than 0 - the amount the user can borrow from the reserve given their unique circumstancesuser State. borrowable. amount. value
Include a user address when fetching market and reserve data, or
Reserve. will be empty.
Additionally, if Reserve. is not null, users can choose to receive any borrowed assets as the chain’s native token and it will be automatically unwrapped before being sent to the user’s wallet. This is typical for reserves of the wrapped version of the chain’s native token (e.g., WETH on Ethereum).
2. Prepare the Borrow Execution Plan
Next, if all of these conditions are met, we can proceed with creating the execution plan for the borrow operation.
Use the use hook in React, the borrow action in TypeScript, or the borrow query in GraphQL to create the execution plan for a direct borrow operation.
import { useWalletClient } from "wagmi";
import { useBorrow, bigDecimal, evmAddress } from "@aave/react";
// …
const { data: walletClient } = useWalletClient();
const [borrow, borrowing] = useBorrow();
const execute = async () => {
const result = await borrow({
market: reserve.market.address,
amount: {
erc20: {
currency: reserve.underlyingToken.address,
value: bigDecimal(2), // 2 WETH
},
},
sender: evmAddress(walletClient!.account.address),
chainId: reserve.market.chain.chainId,
});
// …
};
import { useWalletClient } from "wagmi";
import { useBorrow, bigDecimal, evmAddress } from "@aave/react";
// …
const { data: walletClient } = useWalletClient();
const [borrow, borrowing] = useBorrow();
const execute = async () => {
const result = await borrow({
market: reserve.market.address,
amount: {
native: bigDecimal(2), // 2 ETH
},
sender: evmAddress(walletClient!.account.address),
chainId: reserve.market.chain.chainId,
});
// …
};
import { borrow } from "@aave/client/actions";
import { bigDecimal, evmAddress } from "@aave/client";
import { client } from "./client";
import { wallet } from "./wallet";
const result = await borrow(client, {
market: reserve.market.address,
amount: {
erc20: {
currency: reserve.underlyingToken.address,
value: bigDecimal(2), // 2 WETH
},
},
sender: evmAddress(wallet.account.address),
chainId: reserve.market.chain.chainId,
});
// …
import { borrow } from "@aave/client/actions";
import { bigDecimal, evmAddress } from "@aave/client";
import { client } from "./client";
import { wallet } from "./wallet";
const result = await borrow(client, {
market: reserve.market.address,
amount: {
native: bigDecimal(2), // 2 ETH
},
sender: evmAddress(wallet.account.address),
chainId: reserve.market.chain.chainId,
});
// …
import { AaveClient } from "@aave/client";
export const client = AaveClient.create();
import { createWalletClient, http, type Hex } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";
export const wallet = createWalletClient({
account: privateKeyToAccount(process.env.PRIVATE_KEY as Hex),
chain: mainnet,
transport: http(),
});
query {
borrow(
request: {
market: "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2"
amount: {
erc20: {
currency: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
value: "2"
}
# OR
# native: "2" # if the reserve accepts native currency
}
sender: "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234"
chainId: 1
}
) {
__typename
... on TransactionRequest {
to
from
data
value
chainId
operation
}
... on ApprovalRequired {
reason
requiredAmount {
value
decimals
}
currentAllowance {
value
decimals
}
approval {
to
from
data
value
chainId
operation
}
originalTransaction {
to
from
data
value
chainId
operation
}
}
... on InsufficientBalanceError {
required {
value
decimals
}
available {
value
decimals
}
}
}
}
If you intend to leverage credit delegation, you can first check the credit delegation allowance.
import { useCreditDelegateeAllowance, evmAddress } from "@aave/react";
// …
const { data: allowance, loading } = useCreditDelegateeAllowance({
market: reserve.market.address,
underlyingToken: reserve.underlyingToken.address,
user: evmAddress("0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234"), // Delegator
delegatee: evmAddress(walletClient!.account.address), // Your address
chainId: reserve.market.chain.chainId,
});
if (allowance && BigInt(allowance.amount.raw) > 0n) {
console.log("Available delegation allowance:", allowance.amount.value);
} else {
console.log("No credit delegation allowance available");
}
import { creditDelegateeAllowance } from "@aave/client/actions";
import { evmAddress } from "@aave/client";
import { client } from "./client";
import { wallet } from "./wallet";
const allowanceResult = await creditDelegateeAllowance(client, {
market: reserve.market.address,
underlyingToken: reserve.underlyingToken.address,
user: evmAddress("0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234"), // Delegator
delegatee: evmAddress(wallet.account.address), // Your address
chainId: reserve.market.chain.chainId,
});
if (allowanceResult.isOk() && BigInt(allowanceResult.value.amount.raw) > 0n) {
console.log(
"Available delegation allowance:",
allowanceResult.value.amount.value
);
} else {
console.log("No credit delegation allowance available");
}
query {
creditDelegateeAllowance(
request: {
market: "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2"
underlyingToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
user: "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234" # Delegator
delegatee: "0x8ba1f109551bd432803012645ac136ddd64dba72" # Your address
chainId: 1
}
) {
usdPerToken
amount {
value
raw
decimals
}
usd
}
}
And then, you can proceed with preparing the execution plan for the borrow operation.
import { useWalletClient } from "wagmi";
import { useBorrow, bigDecimal, evmAddress } from "@aave/react";
// …
const { data: walletClient } = useWalletClient();
const [borrow, borrowing] = useBorrow();
const execute = async () => {
const result = await borrow({
market: reserve.market.address,
amount: {
erc20: {
currency: reserve.underlyingToken.address,
value: bigDecimal(2), // 2 WETH
},
},
sender: evmAddress(walletClient!.account.address),
chainId: reserve.market.chain.chainId,
onBehalfOf: evmAddress("0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234"), // Delegator
});
// …
};
import { borrow } from "@aave/client/actions";
import { bigDecimal, evmAddress } from "@aave/client";
import { client } from "./client";
import { wallet } from "./wallet";
const result = await borrow(client, {
market: reserve.market.address,
amount: {
erc20: {
currency: reserve.underlyingToken.address,
value: bigDecimal(2), // 2 WETH
},
},
sender: evmAddress(wallet.account.address),
chainId: reserve.market.chain.chainId,
onBehalfOf: evmAddress("0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234"), // Delegator
});
// …
query {
borrow(
request: {
market: "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2"
amount: {
erc20: {
currency: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
value: "2"
}
# OR
# native: "2" # if the reserve accepts native currency
}
sender: "0x8ba1f109551bd432803012645ac136ddd64dba72" # Delegatee
chainId: 1
onBehalfOf: "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234" # Delegator
}
) {
# …
}
}
3. Process the Borrow Execution Plan
Finally, handle the execution plan.
- React: Use the
usehook for the wallet library of your choice to send the transactions in the execution plan.Send Transaction - TypeScript: Use
sendfor your chosen wallet library to send the transactions in the execution plan, and await indexing withWith client..wait For Transaction - GraphQL: If the response is a
Transaction, you can send it directly using the wallet associated with the from address.Request
import { useWalletClient } from "wagmi";
import { errAsync, useBorrow } from "@aave/react";
import { useSendTransaction } from "@aave/react/viem";
// …
const { data: walletClient } = useWalletClient();
const [borrow, borrowing] = useBorrow();
const [sendTransaction, sending] = useSendTransaction(walletClient);
// …
// Optional: combine loading states
const loading = borrowing.loading || sending.loading;
const error = borrowing.error || sending.error;
// …
const execute = async () => {
const result = await borrow({
// …
}).andThen((plan) => {
switch (plan.__typename) {
case "TransactionRequest":
// Single transaction execution
return sendTransaction(plan);
case "ApprovalRequired":
// Approval + transaction sequence
return sendTransaction(plan.approval).andThen(() =>
sendTransaction(plan.originalTransaction)
);
case "InsufficientBalanceError":
return errAsync(
new Error(`Insufficient balance: ${plan.required.value} required.`)
);
}
});
if (result.isErr()) {
console.error("Borrow failed:", result.error);
} else {
console.log("Borrow successful with hash:", result.value);
}
};
import { borrow } from "@aave/client/actions";
import { sendWith } from "@aave/client/viem";
import { client } from "./client";
import { wallet } from "./wallet";
const result = await borrow(client, {
// …
})
.andThen(sendWith(wallet))
.andThen(client.waitForTransaction);
if (result.isErr()) {
console.error("Borrow failed:", result.error);
} else {
console.log("Borrow successful:", result.value);
}
{
"data": {
"borrow": {
"__typename": "TransactionRequest",
"to": "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2",
"from": "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234",
"data": "0xa415bcad000000000000000000000000a0b…",
"value": "0",
"chainId": 1,
"operation": "BORROW"
}
}
}
In GraphQL, if the response is an Approval, you must first send the approval transaction. Once it succeeds, you can then send the original. This happens only for native borrows, which need credit delegation to the WrappedTokenGateway.
{
"data": {
"borrow": {
"__typename": "ApprovalRequired",
"reason": "Insufficient credit delegation to the WrappedTokenGateway",
"requiredAmount": {
"value": "2.0",
"decimals": 18
},
"currentAllowance": {
"value": "0.0",
"decimals": 18
},
"approval": {
"to": "0xeA51d7853EEFb32b6ee06b1C12E6dcCA88Be0fFE",
"from": "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234",
"data": "0xc04a8a10000000000000000000000000d01…",
"value": "0",
"chainId": 1,
"operation": null
},
"originalTransaction": {
"to": "0xd01607c3C5eCABa394D8be377a08590149325722",
"from": "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234",
"data": "0xe74f7b85000000000000000000000000878…",
"value": "0",
"chainId": 1,
"operation": "BORROW"
}
}
}
}
In GraphQL, if the response is an Insufficient, inform the user that they don’t have enough balance to borrow the requested amount.
{
"data": {
"borrow": {
"__typename": "InsufficientBalanceError",
"required": {
"value": "2.0",
"decimals": 18
},
"available": {
"value": "1.0",
"decimals": 18
}
}
}
}
Credit Delegation
Credit delegation allows a user to approve another user (delegatee) to borrow assets against their own supply position. The supply position must be enabled as collateral.
To delegate credit to another address, follow these steps.
1. Identify the Delegated Position
First, choose the asset the delegatee may borrow and make sure your collateral is enabled.
Given this Market object:
const position: MarketUserReserveSupplyPosition = {
__typename: "MarketUserReserveSupplyPosition",
market: {
__typename: "MarketInfo",
address: "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2",
chain: { chainId: 1 },
// …
},
currency: {
__typename: "Currency",
symbol: "WETH",
name: "Wrapped Ether",
address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
// …
},
balance: {
__typename: "TokenAmount",
amount: {
__typename: "DecimalValue",
value: "5.0",
raw: "5000000000000000000",
decimals: 18,
// …
},
// …
},
isCollateral: true,
canBeCollateral: true,
// …
};
Ensure Market is true, if not enable it as collateral first.
2. Prepare the Delegation Transaction Request
Next, we can proceed with creating the transaction request for the credit delegation operation.
Use the use hook in React, the approve action in TypeScript, or the approve query in GraphQL to create the transaction request for approving credit delegation. underlying is the asset the delegatee will borrow; the example uses WETH.
import { useWalletClient } from "wagmi";
import {
useApproveBorrowCreditDelegation,
evmAddress,
bigDecimal,
} from "@aave/react";
// …
const { data: walletClient } = useWalletClient();
const [approveDelegation, approving] = useApproveBorrowCreditDelegation();
const execute = async () => {
const result = await approveDelegation({
market: position.market.address,
underlyingToken: position.currency.address,
amount: bigDecimal(1000),
user: evmAddress(walletClient!.account.address),
delegatee: evmAddress("0x8ba1f109551bd432803012645ac136ddd64dba72"),
chainId: position.market.chain.chainId,
});
// …
};
import { approveBorrowCreditDelegation } from "@aave/client/actions";
import { evmAddress, bigDecimal } from "@aave/client";
import { client } from "./client";
import { wallet } from "./wallet";
const result = await approveBorrowCreditDelegation(client, {
market: position.market.address,
underlyingToken: position.currency.address,
amount: bigDecimal(1000),
user: evmAddress(wallet.account.address),
delegatee: evmAddress("0x8ba1f109551bd432803012645ac136ddd64dba72"),
chainId: position.market.chain.chainId,
});
// …
import { AaveClient } from "@aave/client";
export const client = AaveClient.create();
import { createWalletClient, http, type Hex } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";
export const wallet = createWalletClient({
account: privateKeyToAccount(process.env.PRIVATE_KEY as Hex),
chain: mainnet,
transport: http(),
});
query {
approveBorrowCreditDelegation(
request: {
market: "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2"
underlyingToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
amount: "1000"
user: "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234"
delegatee: "0x8ba1f109551bd432803012645ac136ddd64dba72"
chainId: 1
}
) {
to
from
data
value
chainId
}
}
3. Send the Delegation Transaction
Finally, send the transaction.
- React: Use the
usehook for the wallet library of your choice to send the transaction.Send Transaction - TypeScript: Use
sendfor your chosen wallet library to initiate the transaction, and await indexing withWith client..wait For Transaction - GraphQL: The response is a
Transactionthat can be sent directly using the wallet associated with theRequest fromaddress.
import { useWalletClient } from "wagmi";
import { useApproveBorrowCreditDelegation } from "@aave/react";
import { useSendTransaction } from "@aave/react/viem";
// …
const { data: walletClient } = useWalletClient();
const [approveDelegation, approving] = useApproveBorrowCreditDelegation();
const [sendTransaction, sending] = useSendTransaction(walletClient);
// …
// Optional: combine loading states
const loading = approving.loading || sending.loading;
const error = approving.error || sending.error;
// …
const execute = async () => {
const result = await approveDelegation({
// …
}).andThen(sendTransaction);
if (result.isErr()) {
console.error("Credit delegation failed:", result.error);
} else {
console.log("Credit delegation successful with hash:", result.value);
}
};
import { approveBorrowCreditDelegation } from "@aave/client/actions";
import { sendWith } from "@aave/client/viem";
import { client } from "./client";
import { wallet } from "./wallet";
const result = await approveBorrowCreditDelegation(client, {
// …
})
.andThen(sendWith(wallet))
.andThen(client.waitForTransaction);
if (result.isErr()) {
console.error("Credit delegation failed:", result.error);
} else {
console.log("Credit delegation successful:", result.value);
}
{
"data": {
"approveBorrowCreditDelegation": {
"__typename": "TransactionRequest",
"to": "0xeA51d7853EEFb32b6ee06b1C12E6dcCA88Be0fFE",
"from": "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234",
"data": "0xc04a8a10…",
"value": "0",
"chainId": 1,
"operation": null
}
}
}
The delegatee can now borrow against the user’s supply position.