Aave V4 User Balances
User balances show a user’s token holdings across chains, aggregated by token type with supply and borrow APY information.
User Balance Data Structure
User balance data provides information about a user’s token holdings, including:
- Globally unique identifier: for each balance entry
- Token identification: name, symbol, icon, decimals
- Balance amounts: individual balances per network, total amounts
- Yield information: highest and lowest supply/borrow APY for each token
- Collateral factors: highest and lowest collateral factors for each token
- Fiat valuations: converted amounts in selected currency
The following TypeScript interfaces illustrate the core UserBalance type:
interface UserBalance {
__typename: "UserBalance";
id: UserBalanceId;
info: TokenInfo;
balances: TokenAmount[];
totalAmount: DecimalNumber;
exchange: ExchangeAmount;
highestSupplyApy: PercentNumber;
highestBorrowApy: PercentNumber;
lowestSupplyApy: PercentNumber;
lowestBorrowApy: PercentNumber;
highestCollateralFactor: PercentNumber | null;
lowestCollateralFactor: PercentNumber | null;
}
interface TokenInfo {
__typename: "TokenInfo";
id: TokenInfoId;
name: string;
symbol: string;
icon: string;
decimals: number;
categories: TokenCategory[];
}
type TokenAmount = Erc20Amount | NativeAmount;
interface Erc20Amount {
__typename: "Erc20Amount";
token: Erc20Token;
amount: DecimalNumber;
exchange: ExchangeAmount;
exchangeRate: DecimalNumber;
}
interface NativeAmount {
__typename: "NativeAmount";
token: NativeToken;
amount: DecimalNumber;
exchange: ExchangeAmount;
exchangeRate: DecimalNumber;
}
interface ExchangeAmount {
__typename: "ExchangeAmount";
value: BigDecimal;
name: string;
symbol: string;
icon: string;
decimals: number;
}
The following GraphQL fragments illustrate the core UserBalance type:
fragment UserBalance on UserBalance {
__typename
id
info {
...TokenInfo
}
balances {
...TokenAmount
}
totalAmount {
...DecimalNumber
}
exchange(currency: $currency) {
...ExchangeAmount
}
highestSupplyApy: supplyApy(metric: HIGHEST) {
...PercentNumber
}
highestBorrowApy: borrowApy(metric: HIGHEST) {
...PercentNumber
}
lowestSupplyApy: supplyApy(metric: LOWEST) {
...PercentNumber
}
lowestBorrowApy: borrowApy(metric: LOWEST) {
...PercentNumber
}
highestCollateralFactor: collateralFactor(metric: HIGHEST) {
...PercentNumber
}
lowestCollateralFactor: collateralFactor(metric: LOWEST) {
...PercentNumber
}
}
fragment TokenInfo on TokenInfo {
__typename
id
name
symbol
icon
decimals
categories
}
fragment TokenAmount on TokenAmount {
__typename
... on Erc20Amount {
token {
...Erc20Token
}
amount {
...DecimalNumber
}
exchange(currency: $currency) {
...ExchangeAmount
}
exchangeRate(currency: $currency) {
...DecimalNumber
}
}
... on NativeAmount {
token {
...NativeToken
}
amount {
...DecimalNumber
}
exchange(currency: $currency) {
...ExchangeAmount
}
exchangeRate(currency: $currency) {
...DecimalNumber
}
}
}
fragment ExchangeAmount on ExchangeAmount {
__typename
value
name
symbol
icon
decimals
}
Fetching User Balances
Use the use hook in React, the user action in TypeScript, or the user query in GraphQL to fetch user token balances. In TypeScript, the examples filter balances by chains, hub, spoke, user position, or tokens.
import { type UserBalancesRequest, useUserBalances } from "@aave/react";
function UserBalancesList({ request }: { request: UserBalancesRequest }) {
const { data, loading, error } = useUserBalances(request);
if (loading) return <div>Loading…</div>;
if (error) return <div>Error: {error.message}</div>;
// data: UserBalance[]
return (
<div>
{data.map((balance) => (
<div key={balance.id}>
<h2>{balance.info.name}</h2>
<p>
<strong>Total:</strong>
{balance.totalAmount.value.toDisplayString(2)}
{balance.info.symbol}
<small>
{balance.exchange.symbol}
{balance.exchange.value.toDisplayString(2)}
</small>
</p>
<p>
<strong>Supply APY:</strong>
{balance.highestSupplyApy.normalized.toFixed(2)}%
</p>
</div>
))}
</div>
);
}
import { type UserBalancesRequest, useUserBalances } from "@aave/react";
function UserBalancesList({ request }: { request: UserBalancesRequest }) {
const { data } = useUserBalances({
...request,
suspense: true,
});
// data: UserBalance[]
return (
<div>
{data.map((balance) => (
<div key={balance.id}>
<h2>{balance.info.name}</h2>
<p>
<strong>Total:</strong>
{balance.totalAmount.value.toDisplayString(2)}
{balance.info.symbol}
<small>
{balance.exchange.symbol}
{balance.exchange.value.toDisplayString(2)}
</small>
</p>
<p>
<strong>Supply APY:</strong>
{balance.highestSupplyApy.normalized.toFixed(2)}%
</p>
</div>
))}
</div>
);
}
import { userBalances } from "@aave/client/actions";
import { evmAddress, chainId } from "@aave/client";
import { client } from "./client";
const result = await userBalances(client, {
user: evmAddress("0x456…"),
filter: {
chains: {
chainIds: [chainId(1)],
},
},
});
if (result.isOk()) {
console.log("User balances:", result.value);
// result.value: UserBalance[]
} else {
console.error("Error:", result.error);
}
import { userBalances } from "@aave/client/actions";
import { evmAddress, chainId } from "@aave/client";
import { client } from "./client";
// By hub ID
const result = await userBalances(client, {
user: evmAddress("0x456…"),
filter: {
hubId: {
hubId: hub.id, // hub: Hub
},
},
});
if (result.isOk()) {
console.log("User balances:", result.value);
// result.value: UserBalance[]
} else {
console.error("Error:", result.error);
}
// By hub address and chain ID
const resultByAddress = await userBalances(client, {
user: evmAddress("0x456…"),
filter: {
hub: {
address: evmAddress("0x123…"),
chainId: chainId(1),
},
},
});
import { userBalances } from "@aave/client/actions";
import { evmAddress, chainId } from "@aave/client";
import { client } from "./client";
const result = await userBalances(client, {
user: evmAddress("0x456…"),
filter: {
spoke: {
address: evmAddress("0x123…"),
chainId: chainId(1),
},
},
});
if (result.isOk()) {
console.log("User balances:", result.value);
// result.value: UserBalance[]
} else {
console.error("Error:", result.error);
}
import { userBalances } from "@aave/client/actions";
import { evmAddress, type UserPosition } from "@aave/client";
import { client } from "./client";
// const position: UserPosition = …
const result = await userBalances(client, {
user: evmAddress("0x456…"),
filter: {
userPosition: {
userPositionId: position.id,
},
},
});
if (result.isOk()) {
console.log("User balances:", result.value);
// result.value: UserBalance[]
} else {
console.error("Error:", result.error);
}
import { userBalances } from "@aave/client/actions";
import { evmAddress, chainId } from "@aave/client";
import { client } from "./client";
const result = await userBalances(client, {
user: evmAddress("0x456…"),
filter: {
tokens: {
chainTokens: [
{ chainId: chainId(1), token: { erc20: evmAddress("0xA0b8…") } },
{ chainId: chainId(1), token: { native: true } },
],
},
},
});
if (result.isOk()) {
console.log("User balances:", result.value);
// result.value: UserBalance[]
} else {
console.error("Error:", result.error);
}
import { AaveClient } from "@aave/client";
export const client = AaveClient.create();
query ($request: UserBalancesRequest!, $currency: Currency! = USD) {
userBalances(request: $request) {
info {
...TokenInfo
}
balances {
...TokenAmount
}
totalAmount {
...DecimalNumber
}
exchange(currency: $currency) {
...ExchangeAmount
}
highestSupplyApy: supplyApy(metric: HIGHEST) {
...PercentNumber
}
highestBorrowApy: borrowApy(metric: HIGHEST) {
...PercentNumber
}
lowestSupplyApy: supplyApy(metric: LOWEST) {
...PercentNumber
}
lowestBorrowApy: borrowApy(metric: LOWEST) {
...PercentNumber
}
highestCollateralFactor: collateralFactor(metric: HIGHEST) {
...PercentNumber
}
lowestCollateralFactor: collateralFactor(metric: LOWEST) {
...PercentNumber
}
}
}
fragment TokenAmount on TokenAmount {
__typename
... on Erc20Amount {
amount {
...DecimalNumber
}
exchange(currency: $currency) {
...ExchangeAmount
}
exchangeRate(currency: $currency) {
...DecimalNumber
}
token {
...Erc20Token
}
}
... on NativeAmount {
amount {
...DecimalNumber
}
exchange(currency: $currency) {
...ExchangeAmount
}
exchangeRate(currency: $currency) {
...DecimalNumber
}
token {
...NativeToken
}
}
}
Filter balances by chain, hub, spoke, user position, or token. In GraphQL, specify the user address and pass the filter in filter.
import { evmAddress, chainId } from "@aave/react";
const request: UserBalancesRequest = {
user: evmAddress("0x456…"),
filter: {
chains: {
chainIds: [chainId(1)],
},
},
};
import { evmAddress, chainId } from "@aave/react";
// By hub ID
const request: UserBalancesRequest = {
user: evmAddress("0x456…"),
filter: {
hubId: {
hubId: hub.id, // hub: Hub
},
},
};
// By hub address and chain ID
const requestByAddress: UserBalancesRequest = {
user: evmAddress("0x456…"),
filter: {
hub: {
address: evmAddress("0x123…"),
chainId: chainId(1),
},
},
};
import { evmAddress, chainId } from "@aave/react";
const request: UserBalancesRequest = {
user: evmAddress("0x456…"),
filter: {
spoke: {
address: evmAddress("0x123…"),
chainId: chainId(1),
},
},
};
import { evmAddress } from "@aave/react";
const request: UserBalancesRequest = {
user: evmAddress("0x456…"),
filter: {
userPosition: {
userPositionId: position.id,
},
},
};
import { evmAddress, chainId } from "@aave/react";
const request: UserBalancesRequest = {
user: evmAddress("0x456…"),
filter: {
tokens: {
chainTokens: [
{ chainId: chainId(1), token: { erc20: evmAddress("0xA0b8…") } },
{ chainId: chainId(1), token: { native: true } },
],
},
},
};
{
"request": {
"user": "0x456…",
"filter": {
"chains": {
"chainIds": [1]
}
}
}
}
{
"request": {
"user": "0x456…",
"filter": {
"hubId": {
"hubId": "SGVsbG8h"
}
}
}
}
{
"request": {
"user": "0x456…",
"filter": {
"hub": {
"address": "0x123…",
"chainId": 1
}
}
}
}
{
"request": {
"user": "0x456…",
"filter": {
"spoke": {
"address": "0x123…",
"chainId": 1
}
}
}
}
{
"request": {
"user": "0x456…",
"filter": {
"userPosition": {
"userPositionId": "SGVsbG8h"
}
}
}
}
{
"request": {
"user": "0x456…",
"filter": {
"tokens": {
"chainTokens": [
{
"chainId": 1,
"token": {
"erc20": "0xA0b8…"
}
},
{
"chainId": 1,
"token": {
"native": true
}
}
]
}
}
}
}
Sort balances by name or balance value. In GraphQL, use the order field.
import { evmAddress, OrderDirection } from "@aave/react";
const request: UserBalancesRequest = {
user: evmAddress("0x456…"),
filter: {
// your filter criteria
},
orderBy: { balance: OrderDirection.Desc },
};
import { evmAddress, OrderDirection } from "@aave/react";
const request: UserBalancesRequest = {
user: evmAddress("0x456…"),
filter: {
// your filter criteria
},
orderBy: { name: OrderDirection.Asc },
};
import { userBalances } from "@aave/client/actions";
import { evmAddress, OrderDirection } from "@aave/client";
import { client } from "./client";
const result = await userBalances(client, {
user: evmAddress("0x456…"),
filter: {
// your filter criteria
},
orderBy: { balance: OrderDirection.Desc },
});
import { userBalances } from "@aave/client/actions";
import { evmAddress, OrderDirection } from "@aave/client";
import { client } from "./client";
const result = await userBalances(client, {
user: evmAddress("0x456…"),
filter: {
// your filter criteria
},
orderBy: { name: OrderDirection.Asc },
});
{
"request": {
"user": "0x456…",
"filter": {
// your filter criteria
},
"orderBy": { "balance": "DESC" }
}
}
{
"request": {
"user": "0x456…",
"filter": {
// your filter criteria
},
"orderBy": { "name": "ASC" }
}
}
Include tokens with zero balances in the results.
const request: UserBalancesRequest = {
user: evmAddress("0x456…"),
filter: {
// your filter criteria
},
includeZeroBalances: true,
};
const result = await userBalances(client, {
user: evmAddress("0x456…"),
filter: {
chains: { chainIds: [chainId(1)] },
},
includeZeroBalances: true,
});
{
"request": {
"user": "0x456…",
"filter": {
// your filter criteria
},
"includeZeroBalances": true
}
}
Specify a different currency for displaying fiat amounts. In GraphQL, use the currency argument.
import { evmAddress, Currency } from "@aave/react";
// …
const { data, loading, error } = useUserBalances({
user: evmAddress("0x456…"),
filter: {
// your filter criteria
},
currency: Currency.Eur,
});
import { userBalances } from "@aave/client/actions";
import { evmAddress, chainId, Currency } from "@aave/client";
import { client } from "./client";
const result = await userBalances(
client,
{
user: evmAddress("0x456…"),
filter: {
chains: { chainIds: [chainId(1)] },
},
},
{
currency: Currency.Eur,
}
);
{
"request": {
"user": "0x456…",
"filter": {
// your filter criteria
}
},
"currency": "EUR"
}