Fetch Open Positions on Aave V4
Each user position captures a user’s activity within a Spoke, simplifying health and performance tracking.
User Positions
User Position Data Structure
User position data describes a user’s lending and borrowing activity on a specific spoke:
- Identification: id, user, spoke, creation timestamp
- Position balances: supplied amounts, collateral amounts, debt amounts, net balances
- Yield information: supply APY, borrow APY, net APY, net interests
- Risk metrics: health factor, user risk premium breakdown, liquidation price, maximum and remaining borrowing power
- Performance data: net balance percentage changes over time
The following TypeScript interfaces illustrate the core UserPosition type:
interface UserPosition {
__typename: "UserPosition";
id: UserPositionId;
user: EvmAddress;
createdAt: Date;
spoke: Spoke;
totalSupplied: ExchangeAmountWithChange;
totalCollateral: ExchangeAmountWithChange;
totalDebt: ExchangeAmountWithChange;
netBalance: ExchangeAmountWithChange;
netCollateral: ExchangeAmountWithChange;
netApy: PercentNumber;
netSupplyApy: PercentNumberWithChange;
netBorrowApy: PercentNumberWithChange;
netAccruedInterest: ExchangeAmount;
healthFactor: HealthFactorWithChange;
riskPremium: UserPositionRiskPremium | null;
liquidationPrice: ExchangeAmount | null;
maxBorrowingPower: ExchangeAmount;
remainingBorrowingPower: ExchangeAmount;
canUpdateDynamicConfig: boolean;
netBalancePercentChange: PercentNumber;
averageCollateralFactor: PercentNumber;
}
interface UserPositionRiskPremium {
__typename: "UserPositionRiskPremium";
current: PercentNumber;
latest: PercentNumber;
breakdown: UserRiskPremiumBreakdownItem[];
}
interface UserRiskPremiumBreakdownItem {
__typename: "UserRiskPremiumBreakdownItem";
token: Erc20Token;
currentRiskPremiumWeight: PercentNumber;
latestRiskPremiumWeight: PercentNumber;
collateral: PercentNumber;
}
interface HealthFactorWithChange {
__typename: "HealthFactorWithChange";
current: BigDecimal | null;
change: PercentNumber;
}
interface ExchangeAmountWithChange {
__typename: "ExchangeAmountWithChange";
current: ExchangeAmount;
change: PercentNumber;
}
interface ExchangeAmount {
__typename: "ExchangeAmount";
value: BigDecimal;
name: string;
symbol: string;
icon: string;
decimals: number;
}
interface PercentNumberWithChange {
__typename: "PercentNumberWithChange";
current: PercentNumber;
change: PercentNumber;
}
The following GraphQL fragments illustrate the core UserPosition type:
fragment UserPosition on UserPosition {
__typename
id
user
createdAt
spoke {
...Spoke
}
totalSupplied {
...ExchangeAmountWithChange
}
totalCollateral {
...ExchangeAmountWithChange
}
totalDebt {
...ExchangeAmountWithChange
}
netBalance {
...ExchangeAmountWithChange
}
netCollateral {
...ExchangeAmountWithChange
}
netAccruedInterest {
...ExchangeAmount
}
netApy {
...PercentNumber
}
netSupplyApy {
...PercentNumberWithChange
}
netBorrowApy {
...PercentNumberWithChange
}
healthFactor {
...HealthFactorWithChange
}
riskPremium {
...UserPositionRiskPremium
}
liquidationPrice {
...ExchangeAmount
}
maxBorrowingPower {
...ExchangeAmount
}
remainingBorrowingPower {
...ExchangeAmount
}
canUpdateDynamicConfig
netBalancePercentChange {
...PercentNumber
}
averageCollateralFactor {
...PercentNumber
}
}
fragment UserPositionRiskPremium on UserPositionRiskPremium {
__typename
current {
...PercentNumber
}
latest {
...PercentNumber
}
breakdown {
...UserRiskPremiumBreakdownItem
}
}
fragment UserRiskPremiumBreakdownItem on UserRiskPremiumBreakdownItem {
__typename
token {
...Erc20Token
}
currentRiskPremiumWeight {
...PercentNumber
}
latestRiskPremiumWeight {
...PercentNumber
}
collateral {
...PercentNumber
}
}
fragment HealthFactorWithChange on HealthFactorWithChange {
__typename
current
change {
...PercentNumber
}
}
fragment ExchangeAmountWithChange on ExchangeAmountWithChange {
__typename
current {
...ExchangeAmount
}
change {
...PercentNumber
}
}
fragment ExchangeAmount on ExchangeAmount {
__typename
value
name
symbol
icon
decimals
}
fragment PercentNumberWithChange on PercentNumberWithChange {
__typename
current {
...PercentNumber
}
change {
...PercentNumber
}
}
A user’s position lives inside the spoke contract. The ISpoke interface exposes the user’s share balances and risk metrics.
struct UserPosition {
uint120 drawnShares;
uint120 premiumShares;
int200 premiumOffsetRay;
uint120 suppliedShares;
uint24 dynamicConfigKey;
}
struct UserAccountData {
uint256 riskPremium;
uint256 avgCollateralFactor;
uint256 healthFactor;
uint256 totalCollateralValue;
uint256 totalDebtValue;
uint256 activeCollateralCount;
uint256 borrowedCount;
}
See the Spoke Data Structure for more details.
Listing User Positions
Fetch all user positions with filtering and ordering options.
Use the use hook in React, the user action in TypeScript, or the user query in GraphQL to fetch positions for a given user address. In TypeScript, the examples also show how to filter by chains or tokens.
import { type UserPositionsRequest, useUserPositions } from "@aave/react";
function UserPositionsList({ request }: { request: UserPositionsRequest }) {
const { data, loading, error } = useUserPositions(request);
if (loading) return <div>Loading…</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
{data.map((position) => (
<div key={position.id}>
<h3>Position on {position.spoke.name}</h3>
<p>Chain: {position.spoke.chain.name}</p>
</div>
))}
</div>
);
}
import { type UserPositionsRequest, useUserPositions } from "@aave/react";
function UserPositionsList({ request }: { request: UserPositionsRequest }) {
const { data } = useUserPositions({
...request,
suspense: true,
});
return (
<div>
{data.map((position) => (
<div key={position.id}>
<h3>Position on {position.spoke.name}</h3>
<p>Chain: {position.spoke.chain.name}</p>
</div>
))}
</div>
);
}
import { userPositions } from "@aave/client/actions";
import { evmAddress, chainId } from "@aave/client";
import { client } from "./client";
const result = await userPositions(client, {
user: evmAddress("0x456…"),
filter: { chainIds: [chainId(1)] },
});
if (result.isOk()) {
console.log(result.value); // UserPosition[]
} else {
console.error(result.error);
}
import { userPositions } from "@aave/client/actions";
import { evmAddress, chainId } from "@aave/client";
import { client } from "./client";
const result = await userPositions(client, {
user: evmAddress("0x456…"),
filter: {
tokens: [
{
address: evmAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"), // USDC
chainId: chainId(1),
},
{
address: evmAddress("0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f"), // GHO
chainId: chainId(1),
},
],
},
});
query (
$request: UserPositionsRequest!
$currency: Currency! = USD
$window: TimeWindow! = LAST_DAY
) {
userPositions(request: $request) {
id
user
createdAt
spoke {
...Spoke
}
totalSupplied(currency: $currency) {
...ExchangeAmountWithChange
}
totalCollateral(currency: $currency) {
...ExchangeAmountWithChange
}
totalDebt(currency: $currency) {
...ExchangeAmountWithChange
}
netBalance(currency: $currency) {
...ExchangeAmountWithChange
}
netCollateral(currency: $currency) {
...ExchangeAmountWithChange
}
netApy {
...PercentNumber
}
netSupplyApy {
...PercentNumberWithChange
}
netBorrowApy {
...PercentNumberWithChange
}
healthFactor {
...HealthFactorWithChange
}
riskPremium {
...UserPositionRiskPremium
}
liquidationPrice(currency: $currency) {
...ExchangeAmount
}
maxBorrowingPower(currency: $currency) {
...ExchangeAmount
}
remainingBorrowingPower(currency: $currency) {
...ExchangeAmount
}
canUpdateDynamicConfig
netBalancePercentChange(window: $window) {
...PercentNumber
}
averageCollateralFactor {
...PercentNumber
}
}
}
fragment HealthFactorWithChange on HealthFactorWithChange {
__typename
current
change(window: $window) {
...PercentNumber
}
}
fragment ExchangeAmountWithChange on ExchangeAmountWithChange {
__typename
current {
...ExchangeAmount
}
change(window: $window) {
...PercentNumber
}
}
fragment ExchangeAmount on ExchangeAmount {
__typename
value
name
symbol
icon
decimals
}
fragment PercentNumberWithChange on PercentNumberWithChange {
__typename
current {
...PercentNumber
}
change(window: $window) {
...PercentNumber
}
}
In React and GraphQL, you can filter positions by the following criteria:
import { evmAddress, chainId } from "@aave/react";
const request: UserPositionsRequest = {
user: evmAddress("0x456…"),
filter: {
chainIds: [chainId(1)],
},
};
import { evmAddress, chainId } from "@aave/react";
const request: UserPositionsRequest = {
user: evmAddress("0x456…"),
filter: {
tokens: [
{
address: evmAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"), // USDC
chainId: chainId(1),
},
{
address: evmAddress("0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f"), // GHO
chainId: chainId(1),
},
],
},
};
{
"request": {
"user": "0x456…",
"filter": {
"chainIds": [1]
}
}
}
{
"request": {
"user": "0x456…",
"filter": {
"tokens": [
{
"address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
"chainId": 1
},
{
"address": "0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f", // GHO
"chainId": 1
}
]
}
}
}
And you can sort positions as follows:
import { evmAddress, OrderDirection, type UserPositionsRequest } from "@aave/react";
const request: UserPositionsRequest = {
user: evmAddress("0x456…"),
filter: {
// your filter criteria
},
orderBy: { balance: OrderDirection.Asc },
};
import { evmAddress, OrderDirection, type UserPositionsRequest } from "@aave/react";
const request: UserPositionsRequest = {
user: evmAddress("0x456…"),
filter: {
// your filter criteria
},
orderBy: { netApy: OrderDirection.Asc },
};
import { evmAddress, OrderDirection, type UserPositionsRequest } from "@aave/react";
const request: UserPositionsRequest = {
user: evmAddress("0x456…"),
filter: {
// your filter criteria
},
orderBy: { healthFactor: OrderDirection.Asc },
};
import { evmAddress, OrderDirection, type UserPositionsRequest } from "@aave/react";
const request: UserPositionsRequest = {
user: evmAddress("0x456…"),
filter: {
// your filter criteria
},
orderBy: { netCollateral: OrderDirection.Asc },
};
import { evmAddress, OrderDirection, type UserPositionsRequest } from "@aave/react";
const request: UserPositionsRequest = {
user: evmAddress("0x456…"),
filter: {
// your filter criteria
},
orderBy: { created: OrderDirection.Asc },
};
import { userPositions } from "@aave/client/actions";
import { evmAddress, chainId, OrderDirection } from "@aave/client";
import { client } from "./client";
const result = await userPositions(client, {
user: evmAddress("0x456…"),
filter: { chainIds: [chainId(1)] },
orderBy: { balance: OrderDirection.Asc },
});
import { userPositions } from "@aave/client/actions";
import { evmAddress, chainId, OrderDirection } from "@aave/client";
import { client } from "./client";
const result = await userPositions(client, {
user: evmAddress("0x456…"),
filter: { chainIds: [chainId(1)] },
orderBy: { netApy: OrderDirection.Asc },
});
import { userPositions } from "@aave/client/actions";
import { evmAddress, chainId, OrderDirection } from "@aave/client";
import { client } from "./client";
const result = await userPositions(client, {
user: evmAddress("0x456…"),
filter: { chainIds: [chainId(1)] },
orderBy: { healthFactor: OrderDirection.Asc },
});
import { userPositions } from "@aave/client/actions";
import { evmAddress, chainId, OrderDirection } from "@aave/client";
import { client } from "./client";
const result = await userPositions(client, {
user: evmAddress("0x456…"),
filter: { chainIds: [chainId(1)] },
orderBy: { netCollateral: OrderDirection.Asc },
});
import { userPositions } from "@aave/client/actions";
import { evmAddress, chainId, OrderDirection } from "@aave/client";
import { client } from "./client";
const result = await userPositions(client, {
user: evmAddress("0x456…"),
filter: { chainIds: [chainId(1)] },
orderBy: { created: OrderDirection.Asc },
});
{
"request": {
"user": "0x456…",
"filter": { "chainIds": [1] },
"orderBy": { "balance": "ASC" }
}
}
{
"request": {
"user": "0x456…",
"filter": { "chainIds": [1] },
"orderBy": { "netApy": "ASC" }
}
}
{
"request": {
"user": "0x456…",
"filter": { "chainIds": [1] },
"orderBy": { "healthFactor": "ASC" }
}
}
{
"request": {
"user": "0x456…",
"filter": { "chainIds": [1] },
"orderBy": { "netCollateral": "ASC" }
}
}
{
"request": {
"user": "0x456…",
"filter": { "chainIds": [1] },
"orderBy": { "created": "ASC" }
}
}
Finally, you can specify a different time window or currency for the data.
import { evmAddress, TimeWindow } from "@aave/react";
const { data, error, loading } = useUserPositions({
user: evmAddress("0x456…"),
filter: {
// your filter criteria
},
timeWindow: TimeWindow.LastWeek,
});
import { evmAddress, Currency } from "@aave/react";
const { data, error, loading } = useUserPositions({
user: evmAddress("0x456…"),
filter: {
// your filter criteria
},
currency: Currency.Eur,
});
import { userPositions } from "@aave/client/actions";
import { evmAddress, chainId, TimeWindow } from "@aave/client";
import { client } from "./client";
const result = await userPositions(
client,
{
user: evmAddress("0x456…"),
filter: { chainIds: [chainId(1)] },
},
{
timeWindow: TimeWindow.LastWeek,
}
);
import { userPositions } from "@aave/client/actions";
import { evmAddress, chainId, Currency } from "@aave/client";
import { client } from "./client";
const result = await userPositions(
client,
{
user: evmAddress("0x456…"),
filter: { chainIds: [chainId(1)] },
},
{
currency: Currency.Eur,
}
);
{
"request": {
"user": "0x456…",
"filter": { "chainIds": [1] }
},
"currency": "EUR"
}
{
"request": {
"user": "0x456…",
"filter": { "chainIds": [1] }
},
"window": "LAST_WEEK"
}
In Solidity, to fetch user positions onchain, iterate through each reserve listed on the spoke and fetch the record for the target user.
import { ISpoke } from "aave-v4/src/spoke/interfaces/ISpoke.sol";
function getUserPositions(
address spokeAddress,
address user
) external view returns (ISpoke.UserPosition[] memory) {
ISpoke spoke = ISpoke(spokeAddress);
uint256 reserveCount = spoke.getReserveCount();
ISpoke.UserPosition[] memory positions = new ISpoke.UserPosition[](reserveCount);
for (uint256 reserveId = 0; reserveId < reserveCount; reserveId++) {
positions[reserveId] = spoke.getUserPosition(reserveId, user);
}
return positions;
}
In Solidity, you can also pull aggregate risk metrics directly:
import { ISpoke } from "aave-v4/src/spoke/interfaces/ISpoke.sol";
function getUserAccountSummary(
address spokeAddress,
address user
) external view returns (ISpoke.UserAccountData memory) {
ISpoke spoke = ISpoke(spokeAddress);
return spoke.getUserAccountData(user);
}
Fetching a Single Position
Fetch a specific user position.
Use the use hook in React, the user action in TypeScript, or the user query in GraphQL to fetch a specific user position. In TypeScript, the examples also show how to query by user and spoke or by position ID, and how to specify a different time window or currency.
import { type UserPositionRequest, useUserPosition } from "@aave/react";
function UserPositionDetail({ request }: { request: UserPositionRequest }) {
const { data, loading, error } = useUserPosition(request);
if (loading) return <div>Loading…</div>;
if (error) return <div>Error: {error.message}</div>;
if (!data) return <div>Position not found</div>;
return (
<div>
<h2>Position {data.id}</h2>
<p>Net Balance: {data.netBalance.current.value.toDisplayString(2)}</p>
<p>Health Factor: {data.healthFactor.current?.toFixed(2) ?? "N/A"}</p>
</div>
);
}
import { type UserPositionRequest, useUserPosition } from "@aave/react";
function UserPositionDetail({ request }: { request: UserPositionRequest }) {
const { data } = useUserPosition({
...request,
suspense: true,
});
if (!data) return <div>Position not found</div>;
return (
<div>
<h2>Position {data.id}</h2>
<p>Net Balance: {data.netBalance.current.value.toDisplayString(2)}</p>
<p>Health Factor: {data.healthFactor.current?.toFixed(2) ?? "N/A"}</p>
</div>
);
}
import { userPosition } from "@aave/client/actions";
import { evmAddress } from "@aave/client";
import { client } from "./client";
const result = await userPosition(client, {
userSpoke: {
user: evmAddress("0x456…"),
spoke: spoke.id,
},
});
if (result.isOk()) {
console.log(result.value); // UserPosition | null
} else {
console.error(result.error);
}
import { userPosition } from "@aave/client/actions";
import { userPositionId } from "@aave/client";
import { client } from "./client";
const id = userPositionId("SGVsbG8h");
const result = await userPosition(client, { id });
import { userPosition } from "@aave/client/actions";
import { TimeWindow } from "@aave/client";
import { client } from "./client";
const result = await userPosition(
client,
{
// your query criteria
},
{
timeWindow: TimeWindow.LastWeek,
}
);
import { userPosition } from "@aave/client/actions";
import { Currency } from "@aave/client";
import { client } from "./client";
const result = await userPosition(
client,
{
// your query criteria
},
{
currency: Currency.Eur,
}
);
query (
$request: UserPositionRequest!
$currency: Currency! = USD
$window: TimeWindow! = LAST_DAY
) {
userPosition(request: $request) {
id
user
createdAt
spoke {
...Spoke
}
totalSupplied(currency: $currency) {
...ExchangeAmountWithChange
}
totalCollateral(currency: $currency) {
...ExchangeAmountWithChange
}
totalDebt(currency: $currency) {
...ExchangeAmountWithChange
}
netBalance(currency: $currency) {
...ExchangeAmountWithChange
}
netCollateral(currency: $currency) {
...ExchangeAmountWithChange
}
netApy {
...PercentNumber
}
netSupplyApy {
...PercentNumberWithChange
}
netBorrowApy {
...PercentNumberWithChange
}
healthFactor {
...HealthFactorWithChange
}
riskPremium {
...UserPositionRiskPremium
}
liquidationPrice(currency: $currency) {
...ExchangeAmount
}
maxBorrowingPower(currency: $currency) {
...ExchangeAmount
}
remainingBorrowingPower(currency: $currency) {
...ExchangeAmount
}
canUpdateDynamicConfig
netBalancePercentChange(window: $window) {
...PercentNumber
}
averageCollateralFactor {
...PercentNumber
}
}
}
fragment HealthFactorWithChange on HealthFactorWithChange {
__typename
current
change(window: $window) {
...PercentNumber
}
}
fragment ExchangeAmountWithChange on ExchangeAmountWithChange {
__typename
current {
...ExchangeAmount
}
change(window: $window) {
...PercentNumber
}
}
fragment ExchangeAmount on ExchangeAmount {
__typename
value
name
symbol
icon
decimals
}
fragment PercentNumberWithChange on PercentNumberWithChange {
__typename
current {
...PercentNumber
}
change(window: $window) {
...PercentNumber
}
}
Query by user and spoke or by position ID.
- React: See below for examples of
Userobjects.Position Request - GraphQL: Use the
requestfield; the examples also show how to specify a different currency or time window.
import { userPositionId } from "@aave/react";
const request: UserPositionRequest = {
id: userPositionId(params.id), // UserPositionId - typically from URL params
};
import { evmAddress } from "@aave/react";
const request: UserPositionRequest = {
userSpoke: {
user: evmAddress("0x456…"),
spoke: spoke.id,
},
};
{
"request": {
"userSpoke": {
"user": "0x456…",
"spoke": "SGVsbG8h"
}
}
}
{
"request": {
"id": "SGVsbG8h"
}
}
{
"request": {
// your query criteria
},
"currency": "EUR"
}
{
"request": {
// your query criteria
},
"window": "LAST_WEEK"
}
In React, you can also specify a different time window or currency for the data.
import { TimeWindow } from "@aave/react";
const { data, error, loading } = useUserPosition({
// your query criteria
timeWindow: TimeWindow.LastWeek,
});
import { Currency } from "@aave/react";
const { data, error, loading } = useUserPosition({
// your query criteria
currency: Currency.Eur,
});
In Solidity, positions are identified by the spoke’s reserve and the user address onchain. To inspect a single position, call get on the spoke with the reserve ID and user address.
import { ISpoke } from "aave-v4/src/spoke/interfaces/ISpoke.sol";
function getUserPosition(
address spokeAddress,
uint256 reserveId,
address user
) external view returns (
ISpoke.UserPosition memory position,
bool isBorrowing,
bool usingAsCollateral
) {
ISpoke spoke = ISpoke(spokeAddress);
position = spoke.getUserPosition(reserveId, user);
(usingAsCollateral, isBorrowing) = spoke.getUserReserveStatus(reserveId, user);
return (position, isBorrowing, usingAsCollateral);
}
User Supplies
User Supply Data Structure
User supply data provides detailed information about individual user supplies within a spoke, including:
- Reserve details: token information, supply APY, lending limits
- Position amounts: current balance, principal, earned interest, and withdrawable amounts
- Collateral status: whether the supply is used as collateral
- Performance metrics: earned interest over time
The following TypeScript interfaces illustrate the UserSupplyItem type:
interface UserSupplyItem {
__typename: "UserSupplyItem";
id: UserSupplyItemId;
reserve: Reserve;
principal: Erc20Amount;
balance: Erc20Amount;
withdrawable: Erc20Amount;
interest: Erc20Amount;
isCollateral: boolean;
}
The following GraphQL fragments illustrate the UserSupplyItem type:
fragment UserSupplyItem on UserSupplyItem {
__typename
id
reserve {
...Reserve
}
principal {
...Erc20Amount
}
balance {
...Erc20Amount
}
withdrawable {
...Erc20Amount
}
interest {
...Erc20Amount
}
isCollateral
}
See the Reserve Data Structure for more details.
Fetching User Supplies
Fetch user supplies within a specific spoke.
Use the use hook in React, the user action in TypeScript, or the user query in GraphQL to fetch user supplies for a user. In TypeScript, the examples also show the supported fetch criteria.
import { type UserSuppliesRequest, useUserSupplies } from "@aave/react";
function UserSuppliesList({ request }: { request: UserSuppliesRequest }) {
const { data, loading, error } = useUserSupplies(request);
if (loading) return <div>Loading…</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
{data.map((supply) => (
<div key={supply.id}>
<h3>{supply.reserve.asset.underlying.info.name}</h3>
<p>Amount: {supply.withdrawable.amount.value.toDisplayString(2)}</p>
</div>
))}
</div>
);
}
import { type UserSuppliesRequest, useUserSupplies } from "@aave/react";
function UserSuppliesList({ request }: { request: UserSuppliesRequest }) {
const { data } = useUserSupplies({
...request,
suspense: true,
});
return (
<div>
{data.map((supply) => (
<div key={supply.id}>
<h3>{supply.reserve.asset.underlying.info.name}</h3>
<p>Amount: {supply.withdrawable.amount.value.toDisplayString(2)}</p>
</div>
))}
</div>
);
}
import { userSupplies } from "@aave/client/actions";
import { client } from "./client";
const result = await userSupplies(client, {
query: {
userPositionId: position.id, // position: UserPosition
},
});
if (result.isOk()) {
console.log(result.value); // UserSupplyItem[]
} else {
console.error(result.error);
}
import { userSupplies } from "@aave/client/actions";
import { evmAddress } from "@aave/client";
import { client } from "./client";
const result = await userSupplies(client, {
query: {
userSpoke: {
user: evmAddress("0x456…"),
spoke: spoke.id, // spoke: Spoke
},
},
});
import { userSupplies } from "@aave/client/actions";
import { chainId, evmAddress } from "@aave/client";
import { client } from "./client";
const result = await userSupplies(client, {
query: {
userChains: {
user: evmAddress("0x456…"),
chainIds: [chainId(1)],
},
},
});
import { userSupplies } from "@aave/client/actions";
import { evmAddress } from "@aave/client";
import { client } from "./client";
const result = await userSupplies(client, {
query: {
userHub: {
user: evmAddress("0x456…"),
hub: {
id: hub.id, // hub: Hub
},
},
},
});
import { userSupplies } from "@aave/client/actions";
import { chainId, evmAddress } from "@aave/client";
import { client } from "./client";
const result = await userSupplies(client, {
query: {
userSymbol: {
user: evmAddress("0x456…"),
symbol: "USDC",
chainIds: [chainId(1), chainId(43114)], // optional; omit to match all chains
},
},
});
query ($request: UserSuppliesRequest!, $currency: Currency! = USD) {
userSupplies(request: $request) {
id
reserve {
...Reserve
}
principal {
...Erc20Amount
}
balance {
...Erc20Amount
}
withdrawable {
...Erc20Amount
}
interest {
...Erc20Amount
}
isCollateral
}
}
fragment Erc20Amount on Erc20Amount {
__typename
amount {
...DecimalNumber
}
exchange(currency: $currency) {
...ExchangeAmount
}
exchangeRate(currency: $currency) {
...DecimalNumber
}
token {
...Erc20Token
}
}
See below for examples of fetch criteria.
- React: These are
Userobjects.Supplies Request - GraphQL: Use the
queryfield to specify the fetch criteria.
const request: UserSuppliesRequest = {
query: {
userPositionId: position.id, // position: UserPosition
},
};
import { evmAddress } from "@aave/react";
const request: UserSuppliesRequest = {
query: {
userSpoke: {
user: evmAddress("0x456…"),
spoke: spoke.id, // spoke: Spoke
},
},
};
import { evmAddress, chainId } from "@aave/react";
const request: UserSuppliesRequest = {
query: {
userChains: {
user: evmAddress("0x456…"),
chainIds: [chainId(1)],
},
},
};
import { evmAddress } from "@aave/react";
const request: UserSuppliesRequest = {
query: {
userHub: {
user: evmAddress("0x456…"),
hub: {
id: hub.id, // hub: Hub
},
},
},
};
import { chainId, evmAddress } from "@aave/react";
const request: UserSuppliesRequest = {
query: {
userSymbol: {
user: evmAddress("0x456…"),
symbol: "USDC",
chainIds: [chainId(1), chainId(43114)], // optional; omit to match all chains
},
},
};
{
"request": {
"query": {
"userPositionId": "SGVsbG8h"
}
}
}
{
"request": {
"query": {
"userSpoke": {
"user": "0x456…",
"spoke": "SGVsbG8h"
}
}
}
}
{
"request": {
"query": {
"userChains": {
"user": "0x456…",
"chainIds": [1]
}
}
}
}
{
"request": {
"query": {
"userHub": {
"user": "0x456…",
"hub": {
"id": "SGVsbG8h"
}
}
}
}
}
{
"request": {
"query": {
"userSymbol": {
"user": "0x456…",
"symbol": "USDC",
"chainIds": [1, 43114]
}
}
}
}
Sort user supplies by asset name, amount, or supply APY.
import { OrderDirection } from "@aave/react";
const request: UserSuppliesRequest = {
query: {
// your query criteria
},
orderBy: { assetName: OrderDirection.Asc },
};
import { OrderDirection } from "@aave/react";
const request: UserSuppliesRequest = {
query: {
// your query criteria
},
orderBy: { amount: OrderDirection.Desc },
};
import { OrderDirection } from "@aave/react";
const request: UserSuppliesRequest = {
query: {
// your query criteria
},
orderBy: { apy: OrderDirection.Desc },
};
import { userSupplies } from "@aave/client/actions";
import { OrderDirection } from "@aave/client";
import { client } from "./client";
const result = await userSupplies(client, {
query: {
// your query criteria
},
orderBy: { assetName: OrderDirection.Asc },
});
import { userSupplies } from "@aave/client/actions";
import { OrderDirection } from "@aave/client";
import { client } from "./client";
const result = await userSupplies(client, {
query: {
// your query criteria
},
orderBy: { amount: OrderDirection.Desc },
});
import { userSupplies } from "@aave/client/actions";
import { OrderDirection } from "@aave/client";
import { client } from "./client";
const result = await userSupplies(client, {
query: {
// your query criteria
},
orderBy: { apy: OrderDirection.Desc },
});
{
"request": {
"query": {
// your query criteria
},
"orderBy": { "assetName": "ASC" }
}
}
{
"request": {
"query": {
// your query criteria
},
"orderBy": { "amount": "DESC" }
}
}
{
"request": {
"query": {
// your query criteria
},
"orderBy": { "apy": "DESC" }
}
}
Include zero-value entries for all other supply reserves available on the same spoke, typically ordered by descending amount.
import { OrderDirection } from "@aave/react";
const request: UserSuppliesRequest = {
query: {
// your query criteria
},
includeZeroBalances: true,
orderBy: { amount: OrderDirection.Desc },
};
import { userSupplies } from "@aave/client/actions";
import { OrderDirection } from "@aave/client";
import { client } from "./client";
const result = await userSupplies(client, {
query: {
// your query criteria
},
includeZeroBalances: true,
orderBy: { amount: OrderDirection.Desc },
});
{
"request": {
"query": {
// your query criteria
},
"includeZeroBalances": true,
"orderBy": { "amount": "DESC" }
}
}
Finally, you can specify a different currency for displaying fiat amounts. In React, you can also specify a different time window.
import { TimeWindow } from "@aave/react";
const { data, error, loading } = useUserSupplies({
query: {
// your query criteria
},
timeWindow: TimeWindow.LastWeek,
});
import { Currency } from "@aave/react";
const { data, error, loading } = useUserSupplies({
query: {
// your query criteria
},
currency: Currency.Eur,
});
import { userSupplies } from "@aave/client/actions";
import { Currency } from "@aave/client";
import { client } from "./client";
const result = await userSupplies(
client,
{
query: {
// your query criteria
},
},
{
currency: Currency.Eur,
}
);
{
"request": {
"query": {
// your query criteria
}
},
"currency": "EUR"
}
In Solidity, supply balances are exposed per reserve on the spoke. Loop through all reserves and inspect supplied for the target user, converting them to asset amounts using the hub’s exchange rate. Here’s a complete example:
import { ISpoke } from "aave-v4/src/spoke/interfaces/ISpoke.sol";
struct UserSupply {
uint256 reserveId;
address underlying;
uint256 suppliedAmount;
bool isCollateral;
}
function getUserSupplies(
address spokeAddress,
address user
) external view returns (UserSupply[] memory) {
ISpoke spoke = ISpoke(spokeAddress);
uint256 reserveCount = spoke.getReserveCount();
UserSupply[] memory supplies = new UserSupply[](reserveCount);
uint256 supplyCount = 0;
for (uint256 reserveId = 0; reserveId < reserveCount; reserveId++) {
ISpoke.UserPosition memory position = spoke.getUserPosition(reserveId, user);
if (position.suppliedShares > 0) {
ISpoke.Reserve memory reserve = spoke.getReserve(reserveId);
(bool isCollateral, ) = spoke.getUserReserveStatus(reserveId, user);
supplies[supplyCount] = UserSupply({
reserveId: reserveId,
underlying: reserve.underlying,
suppliedAmount: reserve.hub.previewRemoveByShares(reserve.assetId, position.suppliedShares),
isCollateral: isCollateral
});
supplyCount++;
}
}
UserSupply[] memory result = new UserSupply[](supplyCount);
for (uint256 i = 0; i < supplyCount; i++) {
result[i] = supplies[i];
}
return result;
}
User positions store balances as shares. Use the hub’s preview to convert shares to the underlying asset amount, accounting for accrued interest.
User Borrows
User Borrow Data Structure
User borrow data provides detailed information about individual borrow positions within a spoke, including:
- Reserve details: token information, borrow APY, borrowing limits
- Position amounts: principal and current debt, with token details
- Performance metrics: accrued interest
The following TypeScript interfaces illustrate the UserBorrowItem type:
interface UserBorrowItem {
__typename: "UserBorrowItem";
id: UserBorrowItemId;
reserve: Reserve;
principal: Erc20Amount;
debt: Erc20Amount;
interest: Erc20Amount;
}
The following GraphQL fragments illustrate the UserBorrowItem type:
fragment UserBorrowItem on UserBorrowItem {
__typename
id
reserve {
...Reserve
}
principal {
...Erc20Amount
}
debt {
...Erc20Amount
}
interest {
...Erc20Amount
}
}
See the Reserve Data Structure for more details.
Fetching User Borrows
Fetch user borrows within a specific spoke.
Use the use hook in React, the user action in TypeScript, or the user query in GraphQL to fetch borrow positions for a user. In TypeScript, the examples also show the supported fetch criteria.
import { type UserBorrowsRequest, useUserBorrows } from "@aave/react";
function UserBorrowsList({ request }: { request: UserBorrowsRequest }) {
const { data, loading, error } = useUserBorrows(request);
if (loading) return <div>Loading…</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
{data.map((borrow) => (
<div key={borrow.id}>
<h3>{borrow.reserve.asset.underlying.info.name}</h3>
<p>Amount: {borrow.debt.amount.value.toDisplayString(2)}</p>
</div>
))}
</div>
);
}
import { type UserBorrowsRequest, useUserBorrows } from "@aave/react";
function UserBorrowsList({ request }: { request: UserBorrowsRequest }) {
const { data } = useUserBorrows({
...request,
suspense: true,
});
return (
<div>
{data.map((borrow) => (
<div key={borrow.id}>
<h3>{borrow.reserve.asset.underlying.info.name}</h3>
<p>Amount: {borrow.debt.amount.value.toDisplayString(2)}</p>
</div>
))}
</div>
);
}
import { userBorrows } from "@aave/client/actions";
import { client } from "./client";
const result = await userBorrows(client, {
query: {
userPositionId: position.id, // position: UserPosition
},
});
if (result.isOk()) {
console.log(result.value); // UserBorrowItem[]
} else {
console.error(result.error);
}
import { userBorrows } from "@aave/client/actions";
import { evmAddress } from "@aave/client";
import { client } from "./client";
const result = await userBorrows(client, {
query: {
userSpoke: {
user: evmAddress("0x456…"),
spoke: spoke.id, // spoke: Spoke
},
},
});
import { userBorrows } from "@aave/client/actions";
import { chainId, evmAddress } from "@aave/client";
import { client } from "./client";
const result = await userBorrows(client, {
query: {
userChains: {
user: evmAddress("0x456…"),
chainIds: [chainId(1)],
},
},
});
import { userBorrows } from "@aave/client/actions";
import { evmAddress } from "@aave/client";
import { client } from "./client";
const result = await userBorrows(client, {
query: {
userHub: {
user: evmAddress("0x456…"),
hub: {
id: hub.id, // hub: Hub
},
},
},
});
import { userBorrows } from "@aave/client/actions";
import { chainId, evmAddress } from "@aave/client";
import { client } from "./client";
const result = await userBorrows(client, {
query: {
userSymbol: {
user: evmAddress("0x456…"),
symbol: "USDC",
chainIds: [chainId(1), chainId(43114)], // optional; omit to match all chains
},
},
});
query ($request: UserBorrowsRequest!, $currency: Currency! = USD) {
userBorrows(request: $request) {
id
reserve {
...Reserve
}
principal {
...Erc20Amount
}
debt {
...Erc20Amount
}
interest {
...Erc20Amount
}
}
}
fragment Erc20Amount on Erc20Amount {
__typename
amount {
...DecimalNumber
}
exchange(currency: $currency) {
...ExchangeAmount
}
exchangeRate(currency: $currency) {
...DecimalNumber
}
token {
...Erc20Token
}
}
See below for examples of fetch criteria.
- React: These are
Userobjects.Borrows Request - GraphQL: Use the
queryfield to specify the fetch criteria.
const request: UserBorrowsRequest = {
query: {
userPositionId: position.id, // position: UserPosition
},
};
import { evmAddress } from "@aave/react";
const request: UserBorrowsRequest = {
query: {
userSpoke: {
user: evmAddress("0x456…"),
spoke: spoke.id, // spoke: Spoke
},
},
};
import { evmAddress, chainId } from "@aave/react";
const request: UserBorrowsRequest = {
query: {
userChains: {
user: evmAddress("0x456…"),
chainIds: [chainId(1)],
},
},
};
import { evmAddress } from "@aave/react";
const request: UserBorrowsRequest = {
query: {
userHub: {
user: evmAddress("0x456…"),
hub: {
id: hub.id, // hub: Hub
},
},
},
};
import { chainId, evmAddress } from "@aave/react";
const request: UserBorrowsRequest = {
query: {
userSymbol: {
user: evmAddress("0x456…"),
symbol: "USDC",
chainIds: [chainId(1), chainId(43114)], // optional; omit to match all chains
},
},
};
{
"request": {
"query": {
"userPositionId": "SGVsbG8h"
}
}
}
{
"request": {
"query": {
"userSpoke": {
"user": "0x456…",
"spoke": "SGVsbG8h"
}
}
}
}
{
"request": {
"query": {
"userChains": {
"user": "0x456…",
"chainIds": [1]
}
}
}
}
{
"request": {
"query": {
"userHub": {
"user": "0x456…",
"hub": {
"id": "SGVsbG8h"
}
}
}
}
}
{
"request": {
"query": {
"userSymbol": {
"user": "0x456…",
"symbol": "USDC",
"chainIds": [1, 43114]
}
}
}
}
Sort user borrows by asset name, amount, or borrow APY.
import { OrderDirection } from "@aave/react";
const request: UserBorrowsRequest = {
query: {
// your query criteria
},
orderBy: { assetName: OrderDirection.Asc },
};
import { OrderDirection } from "@aave/react";
const request: UserBorrowsRequest = {
query: {
// your query criteria
},
orderBy: { amount: OrderDirection.Desc },
};
import { OrderDirection } from "@aave/react";
const request: UserBorrowsRequest = {
query: {
// your query criteria
},
orderBy: { apy: OrderDirection.Desc },
};
import { userBorrows } from "@aave/client/actions";
import { OrderDirection } from "@aave/client";
import { client } from "./client";
const result = await userBorrows(client, {
query: {
// your query criteria
},
orderBy: { assetName: OrderDirection.Asc },
});
import { userBorrows } from "@aave/client/actions";
import { OrderDirection } from "@aave/client";
import { client } from "./client";
const result = await userBorrows(client, {
query: {
// your query criteria
},
orderBy: { amount: OrderDirection.Desc },
});
import { userBorrows } from "@aave/client/actions";
import { OrderDirection } from "@aave/client";
import { client } from "./client";
const result = await userBorrows(client, {
query: {
// your query criteria
},
orderBy: { apy: OrderDirection.Desc },
});
{
"request": {
"query": {
// your query criteria
},
"orderBy": { "assetName": "ASC" }
}
}
{
"request": {
"query": {
// your query criteria
},
"orderBy": { "amount": "DESC" }
}
}
{
"request": {
"query": {
// your query criteria
},
"orderBy": { "apy": "DESC" }
}
}
Include zero-value entries for all other borrow reserves available on the same spoke, typically ordered by descending amount.
import { OrderDirection } from "@aave/react";
const request: UserBorrowsRequest = {
query: {
// your query criteria
},
includeZeroBalances: true,
orderBy: { amount: OrderDirection.Desc },
};
import { userBorrows } from "@aave/client/actions";
import { OrderDirection } from "@aave/client";
import { client } from "./client";
const result = await userBorrows(client, {
query: {
// your query criteria
},
includeZeroBalances: true,
orderBy: { amount: OrderDirection.Desc },
});
{
"request": {
"query": {
// your query criteria
},
"includeZeroBalances": true,
"orderBy": { "amount": "DESC" }
}
}
Finally, you can specify a different currency for displaying fiat amounts. In React, you can also specify a different time window.
import { TimeWindow } from "@aave/react";
const { data, error, loading } = useUserBorrows({
query: {
// your query criteria
},
timeWindow: TimeWindow.LastWeek,
});
import { Currency } from "@aave/react";
const { data, error, loading } = useUserBorrows({
query: {
// your query criteria
},
currency: Currency.Eur,
});
import { userBorrows } from "@aave/client/actions";
import { Currency } from "@aave/client";
import { client } from "./client";
const result = await userBorrows(
client,
{
query: {
// your query criteria
},
},
{
currency: Currency.Eur,
}
);
{
"request": {
"query": {
// your query criteria
}
},
"currency": "EUR"
}
In Solidity, borrow balances are exposed per reserve on the spoke. Loop through all reserves and read each borrowed reserve’s total debt for the target user with get. Here’s a complete example:
import { ISpoke } from "aave-v4/src/spoke/interfaces/ISpoke.sol";
struct UserBorrow {
uint256 reserveId;
address underlying;
uint256 borrowedAmount;
}
function getUserBorrows(
address spokeAddress,
address user
) external view returns (UserBorrow[] memory) {
ISpoke spoke = ISpoke(spokeAddress);
uint256 reserveCount = spoke.getReserveCount();
UserBorrow[] memory borrows = new UserBorrow[](reserveCount);
uint256 borrowCount = 0;
for (uint256 reserveId = 0; reserveId < reserveCount; reserveId++) {
ISpoke.UserPosition memory position = spoke.getUserPosition(reserveId, user);
if (position.drawnShares > 0) {
borrows[borrowCount] = UserBorrow({
reserveId: reserveId,
underlying: spoke.getReserve(reserveId).underlying,
borrowedAmount: spoke.getUserTotalDebt(reserveId, user)
});
borrowCount++;
}
}
UserBorrow[] memory result = new UserBorrow[](borrowCount);
for (uint256 i = 0; i < borrowCount; i++) {
result[i] = borrows[i];
}
return result;
}
Use the Spoke’s get to get the drawn plus premium debt in asset units, including accrued interest.
User Summary
User Summary Data Structure
User summary data describes a user’s aggregated activity across all positions:
- Position count: total number of positions held by the user
- Portfolio totals: supplied amounts, collateral amounts, debt amounts, net balances
- Yield information: calculated net APY across all positions
- Risk metrics: lowest health factor across all positions
- Performance data: net accrued interest across all positions
The following TypeScript interfaces illustrate the UserSummary type:
interface UserSummary {
__typename: "UserSummary";
totalPositions: number;
netBalance: ExchangeAmountWithChange;
totalCollateral: ExchangeAmount;
totalSupplied: ExchangeAmount;
totalDebt: ExchangeAmount;
netApy: PercentNumber;
netAccruedInterest: ExchangeAmount;
lowestHealthFactor: BigDecimal | null;
}
interface ExchangeAmountWithChange {
__typename: "ExchangeAmountWithChange";
current: ExchangeAmount;
change: PercentNumber;
}
The following GraphQL fragments illustrate the UserSummary type:
fragment UserSummary on UserSummary {
__typename
totalPositions
netBalance {
...ExchangeAmountWithChange
}
totalCollateral {
...ExchangeAmount
}
totalSupplied {
...ExchangeAmount
}
totalDebt {
...ExchangeAmount
}
netApy {
...PercentNumber
}
netAccruedInterest {
...ExchangeAmount
}
lowestHealthFactor
}
fragment ExchangeAmountWithChange on ExchangeAmountWithChange {
__typename
current {
...ExchangeAmount
}
change {
...PercentNumber
}
}
Fetching User Summary
Fetch a financial summary for a user across all their positions.
Use the use hook in React, the user action in TypeScript, or the user query in GraphQL to fetch the financial summary for a user. In TypeScript, the examples also show how to filter the summary.
import { type UserSummaryRequest, useUserSummary } from "@aave/react";
function UserSummaryDisplay({ request }: { request: UserSummaryRequest }) {
const { data, loading, error } = useUserSummary(request);
if (loading) return <div>Loading…</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h2>Financial Summary</h2>
<p>Total Positions: {data.totalPositions}</p>
<p>
Net Balance: {data.netBalance.current.symbol}
{data.netBalance.current.value.toDisplayString(2)}
</p>
<p>
Total Supplied: {data.totalSupplied.symbol}
{data.totalSupplied.value.toDisplayString(2)}
</p>
<p>
Total Debt: {data.totalDebt.symbol}
{data.totalDebt.value.toDisplayString(2)}
</p>
<p>Net APY: {data.netApy.normalized.toFixed(2)}%</p>
</div>
);
}
import { type UserSummaryRequest, useUserSummary } from "@aave/react";
function UserSummaryDisplay({ request }: { request: UserSummaryRequest }) {
const { data } = useUserSummary({
...request,
suspense: true,
});
return (
<div>
<h2>Financial Summary</h2>
<p>Total Positions: {data.totalPositions}</p>
<p>
Net Balance: {data.netBalance.current.symbol}
{data.netBalance.current.value.toDisplayString(2)}
</p>
<p>
Total Supplied: {data.totalSupplied.symbol}
{data.totalSupplied.value.toDisplayString(2)}
</p>
<p>
Total Debt: {data.totalDebt.symbol}
{data.totalDebt.value.toDisplayString(2)}
</p>
<p>Net APY: {data.netApy.normalized.toFixed(2)}%</p>
</div>
);
}
import { userSummary } from "@aave/client/actions";
import { evmAddress } from "@aave/client";
import { client } from "./client";
const result = await userSummary(client, {
user: evmAddress("0x456…"),
});
if (result.isOk()) {
console.log(result.value); // UserSummary
} else {
console.error(result.error);
}
import { userSummary } from "@aave/client/actions";
import { evmAddress } from "@aave/client";
import { client } from "./client";
const result = await userSummary(client, {
user: evmAddress("0x456…"),
filter: {
spokeId: spoke.id,
},
});
import { userSummary } from "@aave/client/actions";
import { evmAddress, chainId } from "@aave/client";
import { client } from "./client";
const result = await userSummary(client, {
user: evmAddress("0x456…"),
filter: {
chainIds: [chainId(1)],
},
});
import { userSummary } from "@aave/client/actions";
import { evmAddress } from "@aave/client";
import { client } from "./client";
const result = await userSummary(client, {
user: evmAddress("0x456…"),
filter: {
userPositionId: position.id,
},
});
import { userSummary } from "@aave/client/actions";
import { evmAddress, chainId } from "@aave/client";
import { client } from "./client";
const result = await userSummary(client, {
user: evmAddress("0x456…"),
filter: {
spoke: {
address: evmAddress("0x123…"),
chainId: chainId(1),
},
},
});
query (
$request: UserSummaryRequest!
$currency: Currency! = USD
$window: TimeWindow! = LAST_DAY
) {
userSummary(request: $request) {
totalPositions
netBalance(currency: $currency) {
...ExchangeAmountWithChange
}
totalCollateral(currency: $currency) {
...ExchangeAmount
}
totalSupplied(currency: $currency) {
...ExchangeAmount
}
totalDebt(currency: $currency) {
...ExchangeAmount
}
netApy {
...PercentNumber
}
netAccruedInterest(currency: $currency) {
...ExchangeAmount
}
lowestHealthFactor
}
}
fragment ExchangeAmountWithChange on ExchangeAmountWithChange {
__typename
current {
...ExchangeAmount
}
change(window: $window) {
...PercentNumber
}
}
- React: See below for examples of
Userobjects.Summary Request - GraphQL: Use the
userfield to specify the user.
import { evmAddress } from "@aave/react";
const request: UserSummaryRequest = {
user: evmAddress("0x456…"),
};
import { evmAddress } from "@aave/react";
const request: UserSummaryRequest = {
user: evmAddress("0x456…"),
filter: {
spokeId: spoke.id,
},
};
import { evmAddress, chainId } from "@aave/react";
const request: UserSummaryRequest = {
user: evmAddress("0x456…"),
filter: {
chainIds: [chainId(1)],
},
};
import { evmAddress } from "@aave/react";
const request: UserSummaryRequest = {
user: evmAddress("0x456…"),
filter: {
userPositionId: position.id,
},
};
import { evmAddress, chainId } from "@aave/react";
const request: UserSummaryRequest = {
user: evmAddress("0x456…"),
filter: {
spoke: {
address: evmAddress("0x123…"),
chainId: chainId(1),
},
},
};
{
"request": {
"user": "0x456…"
}
}
In GraphQL, filter the summary by specific spokes or chains using the filter field.
{
"request": {
"user": "0x456…",
"filter": {
"spokeId": "SGVsbG8h"
}
}
}
{
"request": {
"user": "0x456…",
"filter": {
"chainIds": [1]
}
}
}
{
"request": {
"user": "0x456…",
"filter": {
"userPositionId": "SGVsbG8h"
}
}
}
{
"request": {
"user": "0x456…",
"filter": {
"spoke": { "address": "0x123…", "chainId": 1 }
}
}
}
Finally, you can specify a different time window or currency for the data.
import { evmAddress, TimeWindow } from "@aave/react";
const { data, error, loading } = useUserSummary({
user: evmAddress("0x456…"),
timeWindow: TimeWindow.LastWeek,
});
import { evmAddress, Currency, useUserSummary } from "@aave/react";
const { data, error, loading } = useUserSummary({
user: evmAddress("0x456…"),
currency: Currency.Eur,
});
import { userSummary } from "@aave/client/actions";
import { evmAddress, TimeWindow } from "@aave/client";
import { client } from "./client";
const result = await userSummary(
client,
{
user: evmAddress("0x456…"),
},
{
timeWindow: TimeWindow.LastWeek,
}
);
import { userSummary } from "@aave/client/actions";
import { evmAddress, Currency } from "@aave/client";
import { client } from "./client";
const result = await userSummary(
client,
{
user: evmAddress("0x456…"),
},
{
currency: Currency.Eur,
}
);
{
"request": {
// your query criteria
},
"currency": "EUR"
}
{
"request": {
// your query criteria
},
"window": "LAST_WEEK"
}
User Summary History
User Summary History Data Structure
User summary history data provides detailed information about a user’s activity changes over time, including:
- Historical snapshots: portfolio state at different points in time
- Portfolio metrics: net balance, borrows, supplies at each snapshot
- Risk metrics: health factor changes over time
The following TypeScript interfaces illustrate the UserSummaryHistoryItem type:
interface UserSummaryHistoryItem {
__typename: "UserSummaryHistoryItem";
netBalance: ExchangeAmount;
borrows: ExchangeAmount;
supplies: ExchangeAmount;
healthFactor: BigDecimal | null;
date: Date;
}
The following GraphQL fragments illustrate the UserSummaryHistoryItem type:
fragment UserSummaryHistoryItem on UserSummaryHistoryItem {
__typename
netBalance {
...ExchangeAmount
}
borrows {
...ExchangeAmount
}
supplies {
...ExchangeAmount
}
healthFactor
date
}
Fetching User Summary History
Fetch historical financial data for a user over a specified time window.
Use the use hook in React, the user action in TypeScript, or the user query in GraphQL to fetch historical financial data for a user. In TypeScript, the examples also show how to set the time window.
import {
type UserSummaryHistoryRequest,
useUserSummaryHistory,
} from "@aave/react";
function UserSummaryHistory({
request,
}: {
request: UserSummaryHistoryRequest;
}) {
const { data, loading, error } = useUserSummaryHistory(request);
if (loading) return <div>Loading…</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h2>Financial History</h2>
{data.map((item) => (
<div key={item.date.toString()}>
<p>Date: {item.date.toLocaleDateString()}</p>
<p>
Net Balance: {item.netBalance.symbol}
{item.netBalance.value.toDisplayString(2)}
</p>
<p>
Supplies: {item.supplies.symbol}
{item.supplies.value.toDisplayString(2)}
</p>
<p>
Borrows: {item.borrows.symbol}
{item.borrows.value.toDisplayString(2)}
</p>
</div>
))}
</div>
);
}
import {
type UserSummaryHistoryRequest,
useUserSummaryHistory,
} from "@aave/react";
function UserSummaryHistory({
request,
}: {
request: UserSummaryHistoryRequest;
}) {
const { data } = useUserSummaryHistory({
...request,
suspense: true,
});
return (
<div>
<h2>Financial History</h2>
{data.map((item) => (
<div key={item.date.toString()}>
<p>Date: {item.date.toLocaleDateString()}</p>
<p>
Net Balance: {item.netBalance.symbol}
{item.netBalance.value.toDisplayString(2)}
</p>
<p>
Supplies: {item.supplies.symbol}
{item.supplies.value.toDisplayString(2)}
</p>
<p>
Borrows: {item.borrows.symbol}
{item.borrows.value.toDisplayString(2)}
</p>
</div>
))}
</div>
);
}
import { userSummaryHistory } from "@aave/client/actions";
import { evmAddress, TimeWindow } from "@aave/client";
import { client } from "./client";
const result = await userSummaryHistory(client, {
user: evmAddress("0x456…"),
window: TimeWindow.LastDay,
});
if (result.isOk()) {
console.log(result.value); // UserSummaryHistoryItem[]
} else {
console.error(result.error);
}
import { userSummaryHistory } from "@aave/client/actions";
import { evmAddress, TimeWindow } from "@aave/client";
import { client } from "./client";
const result = await userSummaryHistory(client, {
user: evmAddress("0x456…"),
window: TimeWindow.LastWeek,
});
import { userSummaryHistory } from "@aave/client/actions";
import { evmAddress, TimeWindow } from "@aave/client";
import { client } from "./client";
const result = await userSummaryHistory(client, {
user: evmAddress("0x456…"),
window: TimeWindow.LastMonth,
});
query ($request: UserSummaryHistoryRequest!, $currency: Currency! = USD) {
userSummaryHistory(request: $request) {
netBalance(currency: $currency) {
...ExchangeAmount
}
borrows(currency: $currency) {
...ExchangeAmount
}
supplies(currency: $currency) {
...ExchangeAmount
}
healthFactor
date
}
}
- React: See below for examples of
Userobjects.Summary History Request - GraphQL: Use the
userandwindowfields to specify the user and time period.
import { evmAddress, TimeWindow } from "@aave/react";
const request: UserSummaryHistoryRequest = {
user: evmAddress("0x456…"),
window: TimeWindow.LastDay,
};
import { evmAddress, TimeWindow } from "@aave/react";
const request: UserSummaryHistoryRequest = {
user: evmAddress("0x456…"),
window: TimeWindow.LastWeek,
};
import { evmAddress, TimeWindow } from "@aave/react";
const request: UserSummaryHistoryRequest = {
user: evmAddress("0x456…"),
window: TimeWindow.LastMonth,
};
{
"request": {
"user": "0x456…",
"window": "LAST_DAY"
}
}
{
"request": {
"user": "0x456…",
"window": "LAST_WEEK"
}
}
{
"request": {
"user": "0x456…",
"window": "LAST_MONTH"
}
}
Filter the history by specific spokes or chains. In GraphQL, use the filter field.
import { evmAddress, TimeWindow } from "@aave/react";
const request: UserSummaryHistoryRequest = {
user: evmAddress("0x456…"),
window: TimeWindow.LastWeek,
filter: {
spokeId: spoke.id,
},
};
import { evmAddress, chainId, TimeWindow } from "@aave/react";
const request: UserSummaryHistoryRequest = {
user: evmAddress("0x456…"),
window: TimeWindow.LastWeek,
filter: {
chainIds: [chainId(1)],
},
};
import { evmAddress, TimeWindow } from "@aave/react";
const request: UserSummaryHistoryRequest = {
user: evmAddress("0x456…"),
window: TimeWindow.LastWeek,
filter: {
userPositionId: position.id,
},
};
import { evmAddress, chainId, TimeWindow } from "@aave/react";
const request: UserSummaryHistoryRequest = {
user: evmAddress("0x456…"),
window: TimeWindow.LastWeek,
filter: {
spoke: {
address: evmAddress("0x123…"),
chainId: chainId(1),
},
},
};
import { userSummaryHistory } from "@aave/client/actions";
import { evmAddress, TimeWindow } from "@aave/client";
import { client } from "./client";
const result = await userSummaryHistory(client, {
user: evmAddress("0x456…"),
window: TimeWindow.LastWeek,
filter: {
spokeId: spoke.id,
},
});
import { userSummaryHistory } from "@aave/client/actions";
import { evmAddress, chainId, TimeWindow } from "@aave/client";
import { client } from "./client";
const result = await userSummaryHistory(client, {
user: evmAddress("0x456…"),
window: TimeWindow.LastWeek,
filter: {
chainIds: [chainId(1)],
},
});
import { userSummaryHistory } from "@aave/client/actions";
import { evmAddress, TimeWindow } from "@aave/client";
import { client } from "./client";
const result = await userSummaryHistory(client, {
user: evmAddress("0x456…"),
window: TimeWindow.LastWeek,
filter: {
userPositionId: position.id,
},
});
import { userSummaryHistory } from "@aave/client/actions";
import { evmAddress, chainId, TimeWindow } from "@aave/client";
import { client } from "./client";
const result = await userSummaryHistory(client, {
user: evmAddress("0x456…"),
window: TimeWindow.LastWeek,
filter: {
spoke: {
address: evmAddress("0x123…"),
chainId: chainId(1),
},
},
});
{
"request": {
"user": "0x456…",
"window": "LAST_WEEK",
"filter": {
"spokeId": "SGVsbG8h"
}
}
}
{
"request": {
"user": "0x456…",
"window": "LAST_WEEK",
"filter": {
"chainIds": [1]
}
}
}
{
"request": {
"user": "0x456…",
"window": "LAST_WEEK",
"filter": {
"userPositionId": "SGVsbG8h"
}
}
}
{
"request": {
"user": "0x456…",
"window": "LAST_WEEK",
"filter": {
"spoke": { "address": "0x123…", "chainId": 1 }
}
}
}
Finally, you can specify a different currency for displaying fiat amounts.
import { Currency, evmAddress, TimeWindow } from "@aave/react";
const { data, error, loading } = useUserSummaryHistory({
user: evmAddress("0x456…"),
window: TimeWindow.LastWeek,
currency: Currency.Eur,
});
import { userSummaryHistory } from "@aave/client/actions";
import { evmAddress, TimeWindow, Currency } from "@aave/client";
import { client } from "./client";
const result = await userSummaryHistory(
client,
{
user: evmAddress("0x456…"),
window: TimeWindow.LastWeek,
},
{
currency: Currency.Eur,
}
);
{
"request": {
// your query criteria
},
"currency": "EUR"
}