@defisaver/positions-sdk 2.1.43-aave-v4-dev-1 → 2.1.43-aave-v4-dev-2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  import { Client } from 'viem';
2
- import { AaveV4SpokeData, AaveV4SpokeInfo } from '../types';
2
+ import { AaveV4AccountData, AaveV4SpokeData, AaveV4SpokeInfo } from '../types';
3
3
  import { EthAddress, EthereumProvider, NetworkNumber } from '../types/common';
4
4
  export declare function _getAaveV4SpokeData(provider: Client, network: NetworkNumber, market: AaveV4SpokeInfo, blockNumber?: 'latest' | number): Promise<AaveV4SpokeData>;
5
5
  export declare function getAaveV4SpokeData(provider: EthereumProvider, network: NetworkNumber, spoke: AaveV4SpokeInfo, blockNumber?: 'latest' | number): Promise<AaveV4SpokeData>;
6
- export declare function _getAaveV4AccountData(provider: Client, network: NetworkNumber, marketData: AaveV4SpokeData, address: EthAddress, blockNumber?: 'latest' | number): Promise<any>;
6
+ export declare function _getAaveV4AccountData(provider: Client, network: NetworkNumber, spokeData: AaveV4SpokeData, address: EthAddress, blockNumber?: 'latest' | number): Promise<AaveV4AccountData>;
7
7
  export declare function getAaveV4AccountData(provider: EthereumProvider, network: NetworkNumber, marketData: AaveV4SpokeData, address: EthAddress, blockNumber?: 'latest' | number): Promise<any>;
@@ -19,10 +19,13 @@ exports.getAaveV4AccountData = getAaveV4AccountData;
19
19
  const decimal_js_1 = __importDefault(require("decimal.js"));
20
20
  const tokens_1 = require("@defisaver/tokens");
21
21
  const viem_1 = require("../services/viem");
22
+ const common_1 = require("../types/common");
22
23
  const contracts_1 = require("../contracts");
24
+ const staking_1 = require("../staking");
25
+ const utils_1 = require("../services/utils");
26
+ const aaveV4Helpers_1 = require("../helpers/aaveV4Helpers");
23
27
  const fetchHubData = (viewContract, hubAddress) => __awaiter(void 0, void 0, void 0, function* () {
24
28
  const hubData = yield viewContract.read.getHubAllAssetsData([hubAddress]);
25
- console.log('hubData', hubData);
26
29
  return {
27
30
  assets: hubData.reduce((acc, assetOnChainData) => {
28
31
  acc[assetOnChainData.assetId] = {
@@ -33,10 +36,32 @@ const fetchHubData = (viewContract, hubAddress) => __awaiter(void 0, void 0, voi
33
36
  }, {}),
34
37
  };
35
38
  });
36
- const formatReserveAsset = (reserveAsset, hubAsset, oracleDecimals) => {
37
- const assetInfo = (0, tokens_1.getAssetInfoByAddress)(reserveAsset.underlying);
39
+ const formatReserveAsset = (reserveAsset, hubAsset, oracleDecimals, network) => __awaiter(void 0, void 0, void 0, function* () {
40
+ const assetInfo = (0, tokens_1.getAssetInfoByAddress)(reserveAsset.underlying, network);
41
+ const symbol = (0, utils_1.wethToEth)(assetInfo.symbol);
42
+ const isStakingAsset = staking_1.STAKING_ASSETS.includes(symbol);
43
+ const supplyIncentives = [];
44
+ const borrowIncentives = [];
45
+ if (isStakingAsset) {
46
+ const yieldApy = yield (0, staking_1.getStakingApy)(symbol, network);
47
+ supplyIncentives.push({
48
+ apy: yieldApy,
49
+ token: symbol,
50
+ incentiveKind: common_1.IncentiveKind.Staking,
51
+ description: `Native ${symbol} yield.`,
52
+ });
53
+ if (reserveAsset.borrowable) {
54
+ // when borrowing assets whose value increases over time
55
+ borrowIncentives.push({
56
+ apy: new decimal_js_1.default(yieldApy).mul(-1).toString(),
57
+ token: symbol,
58
+ incentiveKind: common_1.IncentiveKind.Reward,
59
+ description: `Due to the native yield of ${symbol}, the value of the debt would increase over time.`,
60
+ });
61
+ }
62
+ }
38
63
  return ({
39
- symbol: assetInfo.symbol,
64
+ symbol,
40
65
  underlying: reserveAsset.underlying,
41
66
  hub: reserveAsset.hub,
42
67
  assetId: reserveAsset.assetId,
@@ -47,17 +72,20 @@ const formatReserveAsset = (reserveAsset, hubAsset, oracleDecimals) => {
47
72
  collateralFactor: new decimal_js_1.default(reserveAsset.collateralFactor).div(10000).toNumber(),
48
73
  liquidationFee: new decimal_js_1.default(reserveAsset.liquidationFee).div(10000).toNumber(),
49
74
  price: new decimal_js_1.default(reserveAsset.price).div(new decimal_js_1.default(10).pow(oracleDecimals)).toString(),
50
- totalSupplied: (0, tokens_1.assetAmountInEth)(reserveAsset.totalSupplied.toString(), assetInfo.symbol),
51
- totalDrawn: (0, tokens_1.assetAmountInEth)(reserveAsset.totalDrawn.toString(), assetInfo.symbol),
52
- totalPremium: (0, tokens_1.assetAmountInEth)(reserveAsset.totalPremium.toString(), assetInfo.symbol),
53
- totalDebt: (0, tokens_1.assetAmountInEth)(reserveAsset.totalDebt.toString(), assetInfo.symbol),
54
- supplyCap: (0, tokens_1.assetAmountInEth)(reserveAsset.supplyCap.toString(), assetInfo.symbol),
55
- borrowCap: (0, tokens_1.assetAmountInEth)(reserveAsset.borrowCap.toString(), assetInfo.symbol),
75
+ totalSupplied: (0, tokens_1.assetAmountInEth)(reserveAsset.totalSupplied.toString(), symbol),
76
+ totalDrawn: (0, tokens_1.assetAmountInEth)(reserveAsset.totalDrawn.toString(), symbol),
77
+ totalPremium: (0, tokens_1.assetAmountInEth)(reserveAsset.totalPremium.toString(), symbol),
78
+ totalDebt: (0, tokens_1.assetAmountInEth)(reserveAsset.totalDebt.toString(), symbol),
79
+ supplyCap: (0, tokens_1.assetAmountInEth)(reserveAsset.supplyCap.toString(), symbol),
80
+ borrowCap: (0, tokens_1.assetAmountInEth)(reserveAsset.borrowCap.toString(), symbol),
56
81
  spokeActive: reserveAsset.spokeActive,
57
82
  spokePaused: reserveAsset.spokePaused,
58
83
  drawnRate: new decimal_js_1.default(hubAsset.drawnRate).div(new decimal_js_1.default(10).pow(27)).toString(),
84
+ supplyRate: '0', // To be implemented
85
+ supplyIncentives,
86
+ borrowIncentives,
59
87
  });
60
- };
88
+ });
61
89
  function _getAaveV4SpokeData(provider_1, network_1, market_1) {
62
90
  return __awaiter(this, arguments, void 0, function* (provider, network, market, blockNumber = 'latest') {
63
91
  const viewContract = (0, contracts_1.AaveV4ViewContractViem)(provider, network, blockNumber);
@@ -68,7 +96,7 @@ function _getAaveV4SpokeData(provider_1, network_1, market_1) {
68
96
  hubsData[hubAddress] = yield fetchHubData(viewContract, hubAddress);
69
97
  })),
70
98
  ]);
71
- const reserveAssetsArray = spokeData[1].map((reserveAssetOnChain) => formatReserveAsset(reserveAssetOnChain, hubsData[reserveAssetOnChain.hub].assets[reserveAssetOnChain.assetId], +spokeData[0].oracleDecimals.toString()));
99
+ const reserveAssetsArray = yield Promise.all(spokeData[1].map((reserveAssetOnChain) => __awaiter(this, void 0, void 0, function* () { return formatReserveAsset(reserveAssetOnChain, hubsData[reserveAssetOnChain.hub].assets[reserveAssetOnChain.assetId], +spokeData[0].oracleDecimals.toString(), network); })));
72
100
  return {
73
101
  assetsData: reserveAssetsArray.reduce((acc, reserveAsset) => {
74
102
  acc[reserveAsset.symbol] = reserveAsset;
@@ -85,11 +113,40 @@ function getAaveV4SpokeData(provider_1, network_1, spoke_1) {
85
113
  return _getAaveV4SpokeData((0, viem_1.getViemProvider)(provider, network), network, spoke, blockNumber);
86
114
  });
87
115
  }
88
- function _getAaveV4AccountData(provider_1, network_1, marketData_1, address_1) {
89
- return __awaiter(this, arguments, void 0, function* (provider, network, marketData, address, blockNumber = 'latest') {
116
+ function _getAaveV4AccountData(provider_1, network_1, spokeData_1, address_1) {
117
+ return __awaiter(this, arguments, void 0, function* (provider, network, spokeData, address, blockNumber = 'latest') {
90
118
  const viewContract = (0, contracts_1.AaveV4ViewContractViem)(provider, network, blockNumber);
91
- const loanData = yield viewContract.read.getLoanData([marketData.address, address]);
92
- console.log('loanData', loanData);
119
+ const loanData = yield viewContract.read.getLoanData([spokeData.address, address]);
120
+ const healthFactor = new decimal_js_1.default(loanData.healthFactor).div(1e18).toString();
121
+ const usedAssets = loanData.reserves.reduce((acc, usedReserveAsset) => {
122
+ const reserveData = spokeData.assetsData[(0, utils_1.wethToEth)((0, tokens_1.getAssetInfoByAddress)(usedReserveAsset.underlying, network).symbol)];
123
+ const price = reserveData.price;
124
+ const supplied = (0, tokens_1.assetAmountInEth)(usedReserveAsset.supplied.toString(), reserveData.symbol);
125
+ const drawn = (0, tokens_1.assetAmountInEth)(usedReserveAsset.drawn.toString(), reserveData.symbol);
126
+ const premium = (0, tokens_1.assetAmountInEth)(usedReserveAsset.premium.toString(), reserveData.symbol);
127
+ const borrowed = (0, tokens_1.assetAmountInEth)(usedReserveAsset.totalDebt.toString(), reserveData.symbol);
128
+ acc[reserveData.symbol] = {
129
+ symbol: reserveData.symbol,
130
+ supplied,
131
+ suppliedUsd: new decimal_js_1.default(supplied).mul(price).toString(),
132
+ drawn,
133
+ drawnUsd: new decimal_js_1.default(drawn).mul(price).toString(),
134
+ premium,
135
+ premiumUsd: new decimal_js_1.default(premium).mul(price).toString(),
136
+ borrowed,
137
+ borrowedUsd: new decimal_js_1.default(borrowed).mul(price).toString(),
138
+ isSupplied: !new decimal_js_1.default(supplied).eq(0),
139
+ isBorrowed: usedReserveAsset.isBorrowing,
140
+ collateral: usedReserveAsset.isUsingAsCollateral,
141
+ };
142
+ return acc;
143
+ }, {});
144
+ return Object.assign({ usedAssets,
145
+ healthFactor }, (0, aaveV4Helpers_1.aaveV4GetAggregatedPositionData)({
146
+ usedAssets,
147
+ assetsData: spokeData.assetsData,
148
+ network,
149
+ }));
93
150
  });
94
151
  }
95
152
  function getAaveV4AccountData(provider_1, network_1, marketData_1, address_1) {
@@ -0,0 +1,7 @@
1
+ import { AaveV4AggregatedPositionData, AaveV4AssetsData, AaveV4UsedReserveAssets } from '../../types';
2
+ import { NetworkNumber } from '../../types/common';
3
+ export declare const aaveV4GetAggregatedPositionData: ({ usedAssets, assetsData, network, }: {
4
+ usedAssets: AaveV4UsedReserveAssets;
5
+ assetsData: AaveV4AssetsData;
6
+ network: NetworkNumber;
7
+ }) => AaveV4AggregatedPositionData;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.aaveV4GetAggregatedPositionData = void 0;
7
+ const decimal_js_1 = __importDefault(require("decimal.js"));
8
+ const moneymarket_1 = require("../../moneymarket");
9
+ const aaveV4GetAggregatedPositionData = ({ usedAssets, assetsData, network, }) => {
10
+ const payload = {};
11
+ payload.suppliedUsd = (0, moneymarket_1.getAssetsTotal)(usedAssets, ({ isSupplied }) => isSupplied, ({ suppliedUsd }) => suppliedUsd);
12
+ payload.suppliedCollateralUsd = (0, moneymarket_1.getAssetsTotal)(usedAssets, ({ isSupplied, collateral }) => isSupplied && collateral, ({ suppliedUsd }) => suppliedUsd);
13
+ payload.borrowLimitUsd = (0, moneymarket_1.getAssetsTotal)(usedAssets, ({ isSupplied, collateral }) => isSupplied && collateral, ({ symbol, suppliedUsd }) => new decimal_js_1.default(suppliedUsd).mul(assetsData[symbol].collateralFactor));
14
+ payload.liquidationLimitUsd = (0, moneymarket_1.getAssetsTotal)(usedAssets, ({ isSupplied, collateral }) => isSupplied && collateral,
15
+ // TODO: Verify if liquidation factor is available in Aave V4, currently using collateralFactor as placeholder
16
+ ({ symbol, suppliedUsd }) => new decimal_js_1.default(suppliedUsd).mul(assetsData[symbol].collateralFactor));
17
+ payload.borrowedUsd = (0, moneymarket_1.getAssetsTotal)(usedAssets, ({ isBorrowed }) => isBorrowed, ({ borrowedUsd }) => borrowedUsd);
18
+ payload.drawnUsd = (0, moneymarket_1.getAssetsTotal)(usedAssets, ({ isBorrowed }) => isBorrowed, ({ drawnUsd }) => drawnUsd);
19
+ payload.premiumUsd = (0, moneymarket_1.getAssetsTotal)(usedAssets, ({ isBorrowed }) => isBorrowed, ({ premiumUsd }) => premiumUsd);
20
+ const leftToBorrowUsd = new decimal_js_1.default(payload.borrowLimitUsd).sub(payload.borrowedUsd);
21
+ payload.leftToBorrowUsd = leftToBorrowUsd.lte('0') ? '0' : leftToBorrowUsd.toString();
22
+ payload.ratio = +payload.suppliedUsd ? new decimal_js_1.default(payload.borrowLimitUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
23
+ payload.collRatio = +payload.suppliedUsd ? new decimal_js_1.default(payload.suppliedCollateralUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
24
+ payload.liqRatio = new decimal_js_1.default(payload.borrowLimitUsd).div(payload.liquidationLimitUsd).toString();
25
+ payload.liqPercent = new decimal_js_1.default(payload.borrowLimitUsd).div(payload.liquidationLimitUsd).mul(100).toString();
26
+ const { leveragedType, leveragedAsset } = (0, moneymarket_1.isLeveragedPos)(usedAssets);
27
+ payload.leveragedType = leveragedType;
28
+ payload.leveragedAsset = leveragedAsset;
29
+ payload.liquidationPrice = '';
30
+ if (leveragedType !== '') {
31
+ let assetPrice = assetsData[leveragedAsset].price;
32
+ if (leveragedType === 'lsd-leverage') {
33
+ // Treat ETH like a stablecoin in a long stETH position
34
+ payload.leveragedLsdAssetRatio = new decimal_js_1.default(assetsData[leveragedAsset].price).div(assetsData.ETH.price).toDP(18).toString();
35
+ assetPrice = new decimal_js_1.default(assetPrice).div(assetsData.ETH.price).toString();
36
+ }
37
+ payload.liquidationPrice = (0, moneymarket_1.calcLeverageLiqPrice)(leveragedType, assetPrice, payload.borrowedUsd, payload.liquidationLimitUsd);
38
+ }
39
+ payload.minCollRatio = new decimal_js_1.default(payload.suppliedCollateralUsd).div(payload.borrowLimitUsd).mul(100).toString();
40
+ payload.collLiquidationRatio = new decimal_js_1.default(payload.suppliedCollateralUsd).div(payload.liquidationLimitUsd).mul(100).toString();
41
+ // payload.healthRatio = new Dec(payload.liquidationLimitUsd).div(payload.borrowedUsd).toDP(4).toString();
42
+ payload.minHealthRatio = new decimal_js_1.default(payload.liquidationLimitUsd).div(payload.borrowLimitUsd).toDP(4).toString();
43
+ // TODO: Re-implement netApy calculation
44
+ // const { netApy, incentiveUsd, totalInterestUsd } = calculateNetApy({
45
+ // usedAssets,
46
+ // assetsData,
47
+ // optionalData: { healthRatio: payload.healthRatio },
48
+ // });
49
+ payload.netApy = '0';
50
+ payload.incentiveUsd = '0';
51
+ payload.totalInterestUsd = '0';
52
+ return payload;
53
+ };
54
+ exports.aaveV4GetAggregatedPositionData = aaveV4GetAggregatedPositionData;
@@ -8,3 +8,4 @@ export * as llamaLendHelpers from './llamaLendHelpers';
8
8
  export * as liquityV2Helpers from './liquityV2Helpers';
9
9
  export * as eulerV2Helpers from './eulerHelpers';
10
10
  export * as fluidHelpers from './fluidHelpers';
11
+ export * as aaveV4Helpers from './aaveV4Helpers';
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.fluidHelpers = exports.eulerV2Helpers = exports.liquityV2Helpers = exports.llamaLendHelpers = exports.morphoBlueHelpers = exports.makerHelpers = exports.curveUsdHelpers = exports.sparkHelpers = exports.compoundHelpers = exports.aaveHelpers = void 0;
36
+ exports.aaveV4Helpers = exports.fluidHelpers = exports.eulerV2Helpers = exports.liquityV2Helpers = exports.llamaLendHelpers = exports.morphoBlueHelpers = exports.makerHelpers = exports.curveUsdHelpers = exports.sparkHelpers = exports.compoundHelpers = exports.aaveHelpers = void 0;
37
37
  exports.aaveHelpers = __importStar(require("./aaveHelpers"));
38
38
  exports.compoundHelpers = __importStar(require("./compoundHelpers"));
39
39
  exports.sparkHelpers = __importStar(require("./sparkHelpers"));
@@ -44,3 +44,4 @@ exports.llamaLendHelpers = __importStar(require("./llamaLendHelpers"));
44
44
  exports.liquityV2Helpers = __importStar(require("./liquityV2Helpers"));
45
45
  exports.eulerV2Helpers = __importStar(require("./eulerHelpers"));
46
46
  exports.fluidHelpers = __importStar(require("./fluidHelpers"));
47
+ exports.aaveV4Helpers = __importStar(require("./aaveV4Helpers"));
@@ -40,6 +40,7 @@ const spark_2 = require("../claiming/spark");
40
40
  const morphoBlue_2 = require("../claiming/morphoBlue");
41
41
  const king_1 = require("../claiming/king");
42
42
  const ethena_1 = require("../claiming/ethena");
43
+ const aaveV4_1 = require("../aaveV4");
43
44
  function getPortfolioData(provider, network, defaultProvider, addresses, summerFiAddresses) {
44
45
  return __awaiter(this, void 0, void 0, function* () {
45
46
  const isMainnet = network === common_1.NetworkNumber.Eth;
@@ -56,6 +57,7 @@ function getPortfolioData(provider, network, defaultProvider, addresses, summerF
56
57
  const llamaLendMarkets = [common_1.NetworkNumber.Eth, common_1.NetworkNumber.Arb].includes(network) ? Object.values((0, markets_1.LlamaLendMarkets)(network)).filter((market) => market.chainIds.includes(network)) : [];
57
58
  const liquityV2Markets = [common_1.NetworkNumber.Eth].includes(network) ? Object.values((0, markets_1.LiquityV2Markets)(network)) : [];
58
59
  const liquityV2MarketsStaking = [common_1.NetworkNumber.Eth].includes(network) ? Object.values((0, markets_1.LiquityV2Markets)(network)).filter(market => !market.isLegacy) : [];
60
+ const aaveV4Spokes = Object.values((0, markets_1.AaveV4Spokes)(network)).filter((market) => market.chainIds.includes(network));
59
61
  const client = (0, viem_1.getViemProvider)(provider, network, {
60
62
  batch: {
61
63
  multicall: {
@@ -81,6 +83,7 @@ function getPortfolioData(provider, network, defaultProvider, addresses, summerF
81
83
  const crvUsdMarketsData = {};
82
84
  const llamaLendMarketsData = {};
83
85
  const liquityV2MarketsData = {};
86
+ const aaveV4SpokesData = {};
84
87
  const markets = {
85
88
  morphoMarketsData,
86
89
  compoundV3MarketsData,
@@ -92,6 +95,7 @@ function getPortfolioData(provider, network, defaultProvider, addresses, summerF
92
95
  crvUsdMarketsData,
93
96
  llamaLendMarketsData,
94
97
  liquityV2MarketsData,
98
+ aaveV4SpokesData,
95
99
  };
96
100
  const positions = {};
97
101
  const stakingPositions = {};
@@ -100,6 +104,7 @@ function getPortfolioData(provider, network, defaultProvider, addresses, summerF
100
104
  for (const address of allAddresses) {
101
105
  positions[address.toLowerCase()] = {
102
106
  aaveV3: {},
107
+ aaveV4: {},
103
108
  morphoBlue: {},
104
109
  compoundV3: {},
105
110
  spark: {},
@@ -165,6 +170,10 @@ function getPortfolioData(provider, network, defaultProvider, addresses, summerF
165
170
  const marketData = yield (0, aaveV3_1._getAaveV3MarketData)(client, network, market);
166
171
  aaveV3MarketsData[market.value] = marketData;
167
172
  })),
173
+ ...aaveV4Spokes.map((spoke) => __awaiter(this, void 0, void 0, function* () {
174
+ const spokeData = yield (0, aaveV4_1._getAaveV4SpokeData)(client, network, spoke);
175
+ aaveV4SpokesData[spoke.value] = spokeData;
176
+ })),
168
177
  ...aaveV2Markets.map((market) => __awaiter(this, void 0, void 0, function* () {
169
178
  const marketData = yield (0, aaveV2_1._getAaveV2MarketsData)(client, network, market);
170
179
  aaveV2MarketsData[market.value] = marketData;
@@ -434,6 +443,17 @@ function getPortfolioData(provider, network, defaultProvider, addresses, summerF
434
443
  positions[address.toLowerCase()].aaveV3[market.value] = { error: `Error fetching AaveV3 account data for address ${address} on market ${market.value}`, data: null };
435
444
  }
436
445
  }))).flat(),
446
+ ...aaveV4Spokes.map((spoke) => allAddresses.map((address) => __awaiter(this, void 0, void 0, function* () {
447
+ try {
448
+ const accData = yield (0, aaveV4_1._getAaveV4AccountData)(client, network, aaveV4SpokesData[spoke.value], address);
449
+ if (new decimal_js_1.default(accData.suppliedUsd).gt(0))
450
+ positions[address.toLowerCase()].aaveV4[spoke.value] = { error: '', data: accData };
451
+ }
452
+ catch (error) {
453
+ console.error(`Error fetching AaveV4 account data for address ${address} on spoke ${spoke.value}:`, error);
454
+ positions[address.toLowerCase()].aaveV4[spoke.value] = { error: `Error fetching AaveV4 account data for address ${address} on spoke ${spoke.value}`, data: null };
455
+ }
456
+ }))).flat(),
437
457
  ...morphoMarkets.map((market) => addresses.map((address) => __awaiter(this, void 0, void 0, function* () {
438
458
  try {
439
459
  const [accDataPromise, earnDataPromise] = yield Promise.allSettled([
@@ -1,4 +1,4 @@
1
- import { EthAddress, NetworkNumber } from './common';
1
+ import { EthAddress, IncentiveData, NetworkNumber } from './common';
2
2
  export declare enum AaveV4SpokesType {
3
3
  AaveV4CoreSpoke = "aave_v4_core_spoke"
4
4
  }
@@ -64,10 +64,57 @@ export interface AaveV4ReserveAssetData {
64
64
  spokeActive: boolean;
65
65
  spokePaused: boolean;
66
66
  drawnRate: string;
67
+ supplyRate: string;
68
+ supplyIncentives: IncentiveData[];
69
+ borrowIncentives: IncentiveData[];
67
70
  }
71
+ export type AaveV4AssetsData = Record<string, AaveV4ReserveAssetData>;
68
72
  export interface AaveV4SpokeData {
69
- assetsData: Record<string, AaveV4ReserveAssetData>;
73
+ assetsData: AaveV4AssetsData;
70
74
  oracle: EthAddress;
71
75
  oracleDecimals: number;
72
76
  address: EthAddress;
73
77
  }
78
+ export interface AaveV4UsedReserveAsset {
79
+ symbol: string;
80
+ supplied: string;
81
+ suppliedUsd: string;
82
+ drawn: string;
83
+ drawnUsd: string;
84
+ premium: string;
85
+ premiumUsd: string;
86
+ borrowed: string;
87
+ borrowedUsd: string;
88
+ isSupplied: boolean;
89
+ isBorrowed: boolean;
90
+ collateral: boolean;
91
+ }
92
+ export interface AaveV4AggregatedPositionData {
93
+ suppliedUsd: string;
94
+ suppliedCollateralUsd: string;
95
+ borrowLimitUsd: string;
96
+ liquidationLimitUsd: string;
97
+ borrowedUsd: string;
98
+ drawnUsd: string;
99
+ premiumUsd: string;
100
+ leftToBorrowUsd: string;
101
+ ratio: string;
102
+ collRatio: string;
103
+ liqRatio: string;
104
+ liqPercent: string;
105
+ leveragedType: string;
106
+ leveragedAsset: string;
107
+ liquidationPrice: string;
108
+ leveragedLsdAssetRatio?: string;
109
+ minCollRatio: string;
110
+ collLiquidationRatio: string;
111
+ minHealthRatio: string;
112
+ netApy: string;
113
+ incentiveUsd: string;
114
+ totalInterestUsd: string;
115
+ }
116
+ export type AaveV4UsedReserveAssets = Record<string, AaveV4UsedReserveAsset>;
117
+ export interface AaveV4AccountData extends AaveV4AggregatedPositionData {
118
+ usedAssets: AaveV4UsedReserveAssets;
119
+ healthFactor: string;
120
+ }
@@ -1,4 +1,5 @@
1
1
  import { AaveV2PositionData, AaveV3PositionData, AaveVersions } from './aave';
2
+ import { AaveV4AccountData, AaveV4SpokesType } from './aaveV4';
2
3
  import { EthAddress } from './common';
3
4
  import { CompoundV2PositionData, CompoundV3PositionData, CompoundVersions } from './compound';
4
5
  import { CrvUSDUserData, CrvUSDVersions } from './curveUsd';
@@ -51,6 +52,9 @@ export interface PortfolioPositionsDataForAddress {
51
52
  [key: string]: FluidVaultData;
52
53
  };
53
54
  };
55
+ aaveV4: {
56
+ [key in AaveV4SpokesType]?: PortfolioProtocolData<AaveV4AccountData>;
57
+ };
54
58
  }
55
59
  export interface PortfolioPositionsData {
56
60
  [key: EthAddress]: PortfolioPositionsDataForAddress;
@@ -1,7 +1,7 @@
1
1
  import { Client } from 'viem';
2
- import { AaveV4SpokeData, AaveV4SpokeInfo } from '../types';
2
+ import { AaveV4AccountData, AaveV4SpokeData, AaveV4SpokeInfo } from '../types';
3
3
  import { EthAddress, EthereumProvider, NetworkNumber } from '../types/common';
4
4
  export declare function _getAaveV4SpokeData(provider: Client, network: NetworkNumber, market: AaveV4SpokeInfo, blockNumber?: 'latest' | number): Promise<AaveV4SpokeData>;
5
5
  export declare function getAaveV4SpokeData(provider: EthereumProvider, network: NetworkNumber, spoke: AaveV4SpokeInfo, blockNumber?: 'latest' | number): Promise<AaveV4SpokeData>;
6
- export declare function _getAaveV4AccountData(provider: Client, network: NetworkNumber, marketData: AaveV4SpokeData, address: EthAddress, blockNumber?: 'latest' | number): Promise<any>;
6
+ export declare function _getAaveV4AccountData(provider: Client, network: NetworkNumber, spokeData: AaveV4SpokeData, address: EthAddress, blockNumber?: 'latest' | number): Promise<AaveV4AccountData>;
7
7
  export declare function getAaveV4AccountData(provider: EthereumProvider, network: NetworkNumber, marketData: AaveV4SpokeData, address: EthAddress, blockNumber?: 'latest' | number): Promise<any>;
@@ -10,10 +10,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import Dec from 'decimal.js';
11
11
  import { assetAmountInEth, getAssetInfoByAddress } from '@defisaver/tokens';
12
12
  import { getViemProvider } from '../services/viem';
13
+ import { IncentiveKind, } from '../types/common';
13
14
  import { AaveV4ViewContractViem } from '../contracts';
15
+ import { getStakingApy, STAKING_ASSETS } from '../staking';
16
+ import { wethToEth } from '../services/utils';
17
+ import { aaveV4GetAggregatedPositionData } from '../helpers/aaveV4Helpers';
14
18
  const fetchHubData = (viewContract, hubAddress) => __awaiter(void 0, void 0, void 0, function* () {
15
19
  const hubData = yield viewContract.read.getHubAllAssetsData([hubAddress]);
16
- console.log('hubData', hubData);
17
20
  return {
18
21
  assets: hubData.reduce((acc, assetOnChainData) => {
19
22
  acc[assetOnChainData.assetId] = {
@@ -24,10 +27,32 @@ const fetchHubData = (viewContract, hubAddress) => __awaiter(void 0, void 0, voi
24
27
  }, {}),
25
28
  };
26
29
  });
27
- const formatReserveAsset = (reserveAsset, hubAsset, oracleDecimals) => {
28
- const assetInfo = getAssetInfoByAddress(reserveAsset.underlying);
30
+ const formatReserveAsset = (reserveAsset, hubAsset, oracleDecimals, network) => __awaiter(void 0, void 0, void 0, function* () {
31
+ const assetInfo = getAssetInfoByAddress(reserveAsset.underlying, network);
32
+ const symbol = wethToEth(assetInfo.symbol);
33
+ const isStakingAsset = STAKING_ASSETS.includes(symbol);
34
+ const supplyIncentives = [];
35
+ const borrowIncentives = [];
36
+ if (isStakingAsset) {
37
+ const yieldApy = yield getStakingApy(symbol, network);
38
+ supplyIncentives.push({
39
+ apy: yieldApy,
40
+ token: symbol,
41
+ incentiveKind: IncentiveKind.Staking,
42
+ description: `Native ${symbol} yield.`,
43
+ });
44
+ if (reserveAsset.borrowable) {
45
+ // when borrowing assets whose value increases over time
46
+ borrowIncentives.push({
47
+ apy: new Dec(yieldApy).mul(-1).toString(),
48
+ token: symbol,
49
+ incentiveKind: IncentiveKind.Reward,
50
+ description: `Due to the native yield of ${symbol}, the value of the debt would increase over time.`,
51
+ });
52
+ }
53
+ }
29
54
  return ({
30
- symbol: assetInfo.symbol,
55
+ symbol,
31
56
  underlying: reserveAsset.underlying,
32
57
  hub: reserveAsset.hub,
33
58
  assetId: reserveAsset.assetId,
@@ -38,17 +63,20 @@ const formatReserveAsset = (reserveAsset, hubAsset, oracleDecimals) => {
38
63
  collateralFactor: new Dec(reserveAsset.collateralFactor).div(10000).toNumber(),
39
64
  liquidationFee: new Dec(reserveAsset.liquidationFee).div(10000).toNumber(),
40
65
  price: new Dec(reserveAsset.price).div(new Dec(10).pow(oracleDecimals)).toString(),
41
- totalSupplied: assetAmountInEth(reserveAsset.totalSupplied.toString(), assetInfo.symbol),
42
- totalDrawn: assetAmountInEth(reserveAsset.totalDrawn.toString(), assetInfo.symbol),
43
- totalPremium: assetAmountInEth(reserveAsset.totalPremium.toString(), assetInfo.symbol),
44
- totalDebt: assetAmountInEth(reserveAsset.totalDebt.toString(), assetInfo.symbol),
45
- supplyCap: assetAmountInEth(reserveAsset.supplyCap.toString(), assetInfo.symbol),
46
- borrowCap: assetAmountInEth(reserveAsset.borrowCap.toString(), assetInfo.symbol),
66
+ totalSupplied: assetAmountInEth(reserveAsset.totalSupplied.toString(), symbol),
67
+ totalDrawn: assetAmountInEth(reserveAsset.totalDrawn.toString(), symbol),
68
+ totalPremium: assetAmountInEth(reserveAsset.totalPremium.toString(), symbol),
69
+ totalDebt: assetAmountInEth(reserveAsset.totalDebt.toString(), symbol),
70
+ supplyCap: assetAmountInEth(reserveAsset.supplyCap.toString(), symbol),
71
+ borrowCap: assetAmountInEth(reserveAsset.borrowCap.toString(), symbol),
47
72
  spokeActive: reserveAsset.spokeActive,
48
73
  spokePaused: reserveAsset.spokePaused,
49
74
  drawnRate: new Dec(hubAsset.drawnRate).div(new Dec(10).pow(27)).toString(),
75
+ supplyRate: '0', // To be implemented
76
+ supplyIncentives,
77
+ borrowIncentives,
50
78
  });
51
- };
79
+ });
52
80
  export function _getAaveV4SpokeData(provider_1, network_1, market_1) {
53
81
  return __awaiter(this, arguments, void 0, function* (provider, network, market, blockNumber = 'latest') {
54
82
  const viewContract = AaveV4ViewContractViem(provider, network, blockNumber);
@@ -59,7 +87,7 @@ export function _getAaveV4SpokeData(provider_1, network_1, market_1) {
59
87
  hubsData[hubAddress] = yield fetchHubData(viewContract, hubAddress);
60
88
  })),
61
89
  ]);
62
- const reserveAssetsArray = spokeData[1].map((reserveAssetOnChain) => formatReserveAsset(reserveAssetOnChain, hubsData[reserveAssetOnChain.hub].assets[reserveAssetOnChain.assetId], +spokeData[0].oracleDecimals.toString()));
90
+ const reserveAssetsArray = yield Promise.all(spokeData[1].map((reserveAssetOnChain) => __awaiter(this, void 0, void 0, function* () { return formatReserveAsset(reserveAssetOnChain, hubsData[reserveAssetOnChain.hub].assets[reserveAssetOnChain.assetId], +spokeData[0].oracleDecimals.toString(), network); })));
63
91
  return {
64
92
  assetsData: reserveAssetsArray.reduce((acc, reserveAsset) => {
65
93
  acc[reserveAsset.symbol] = reserveAsset;
@@ -76,11 +104,40 @@ export function getAaveV4SpokeData(provider_1, network_1, spoke_1) {
76
104
  return _getAaveV4SpokeData(getViemProvider(provider, network), network, spoke, blockNumber);
77
105
  });
78
106
  }
79
- export function _getAaveV4AccountData(provider_1, network_1, marketData_1, address_1) {
80
- return __awaiter(this, arguments, void 0, function* (provider, network, marketData, address, blockNumber = 'latest') {
107
+ export function _getAaveV4AccountData(provider_1, network_1, spokeData_1, address_1) {
108
+ return __awaiter(this, arguments, void 0, function* (provider, network, spokeData, address, blockNumber = 'latest') {
81
109
  const viewContract = AaveV4ViewContractViem(provider, network, blockNumber);
82
- const loanData = yield viewContract.read.getLoanData([marketData.address, address]);
83
- console.log('loanData', loanData);
110
+ const loanData = yield viewContract.read.getLoanData([spokeData.address, address]);
111
+ const healthFactor = new Dec(loanData.healthFactor).div(1e18).toString();
112
+ const usedAssets = loanData.reserves.reduce((acc, usedReserveAsset) => {
113
+ const reserveData = spokeData.assetsData[wethToEth(getAssetInfoByAddress(usedReserveAsset.underlying, network).symbol)];
114
+ const price = reserveData.price;
115
+ const supplied = assetAmountInEth(usedReserveAsset.supplied.toString(), reserveData.symbol);
116
+ const drawn = assetAmountInEth(usedReserveAsset.drawn.toString(), reserveData.symbol);
117
+ const premium = assetAmountInEth(usedReserveAsset.premium.toString(), reserveData.symbol);
118
+ const borrowed = assetAmountInEth(usedReserveAsset.totalDebt.toString(), reserveData.symbol);
119
+ acc[reserveData.symbol] = {
120
+ symbol: reserveData.symbol,
121
+ supplied,
122
+ suppliedUsd: new Dec(supplied).mul(price).toString(),
123
+ drawn,
124
+ drawnUsd: new Dec(drawn).mul(price).toString(),
125
+ premium,
126
+ premiumUsd: new Dec(premium).mul(price).toString(),
127
+ borrowed,
128
+ borrowedUsd: new Dec(borrowed).mul(price).toString(),
129
+ isSupplied: !new Dec(supplied).eq(0),
130
+ isBorrowed: usedReserveAsset.isBorrowing,
131
+ collateral: usedReserveAsset.isUsingAsCollateral,
132
+ };
133
+ return acc;
134
+ }, {});
135
+ return Object.assign({ usedAssets,
136
+ healthFactor }, aaveV4GetAggregatedPositionData({
137
+ usedAssets,
138
+ assetsData: spokeData.assetsData,
139
+ network,
140
+ }));
84
141
  });
85
142
  }
86
143
  export function getAaveV4AccountData(provider_1, network_1, marketData_1, address_1) {
@@ -0,0 +1,7 @@
1
+ import { AaveV4AggregatedPositionData, AaveV4AssetsData, AaveV4UsedReserveAssets } from '../../types';
2
+ import { NetworkNumber } from '../../types/common';
3
+ export declare const aaveV4GetAggregatedPositionData: ({ usedAssets, assetsData, network, }: {
4
+ usedAssets: AaveV4UsedReserveAssets;
5
+ assetsData: AaveV4AssetsData;
6
+ network: NetworkNumber;
7
+ }) => AaveV4AggregatedPositionData;
@@ -0,0 +1,47 @@
1
+ import Dec from 'decimal.js';
2
+ import { calcLeverageLiqPrice, getAssetsTotal, isLeveragedPos } from '../../moneymarket';
3
+ export const aaveV4GetAggregatedPositionData = ({ usedAssets, assetsData, network, }) => {
4
+ const payload = {};
5
+ payload.suppliedUsd = getAssetsTotal(usedAssets, ({ isSupplied }) => isSupplied, ({ suppliedUsd }) => suppliedUsd);
6
+ payload.suppliedCollateralUsd = getAssetsTotal(usedAssets, ({ isSupplied, collateral }) => isSupplied && collateral, ({ suppliedUsd }) => suppliedUsd);
7
+ payload.borrowLimitUsd = getAssetsTotal(usedAssets, ({ isSupplied, collateral }) => isSupplied && collateral, ({ symbol, suppliedUsd }) => new Dec(suppliedUsd).mul(assetsData[symbol].collateralFactor));
8
+ payload.liquidationLimitUsd = getAssetsTotal(usedAssets, ({ isSupplied, collateral }) => isSupplied && collateral,
9
+ // TODO: Verify if liquidation factor is available in Aave V4, currently using collateralFactor as placeholder
10
+ ({ symbol, suppliedUsd }) => new Dec(suppliedUsd).mul(assetsData[symbol].collateralFactor));
11
+ payload.borrowedUsd = getAssetsTotal(usedAssets, ({ isBorrowed }) => isBorrowed, ({ borrowedUsd }) => borrowedUsd);
12
+ payload.drawnUsd = getAssetsTotal(usedAssets, ({ isBorrowed }) => isBorrowed, ({ drawnUsd }) => drawnUsd);
13
+ payload.premiumUsd = getAssetsTotal(usedAssets, ({ isBorrowed }) => isBorrowed, ({ premiumUsd }) => premiumUsd);
14
+ const leftToBorrowUsd = new Dec(payload.borrowLimitUsd).sub(payload.borrowedUsd);
15
+ payload.leftToBorrowUsd = leftToBorrowUsd.lte('0') ? '0' : leftToBorrowUsd.toString();
16
+ payload.ratio = +payload.suppliedUsd ? new Dec(payload.borrowLimitUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
17
+ payload.collRatio = +payload.suppliedUsd ? new Dec(payload.suppliedCollateralUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
18
+ payload.liqRatio = new Dec(payload.borrowLimitUsd).div(payload.liquidationLimitUsd).toString();
19
+ payload.liqPercent = new Dec(payload.borrowLimitUsd).div(payload.liquidationLimitUsd).mul(100).toString();
20
+ const { leveragedType, leveragedAsset } = isLeveragedPos(usedAssets);
21
+ payload.leveragedType = leveragedType;
22
+ payload.leveragedAsset = leveragedAsset;
23
+ payload.liquidationPrice = '';
24
+ if (leveragedType !== '') {
25
+ let assetPrice = assetsData[leveragedAsset].price;
26
+ if (leveragedType === 'lsd-leverage') {
27
+ // Treat ETH like a stablecoin in a long stETH position
28
+ payload.leveragedLsdAssetRatio = new Dec(assetsData[leveragedAsset].price).div(assetsData.ETH.price).toDP(18).toString();
29
+ assetPrice = new Dec(assetPrice).div(assetsData.ETH.price).toString();
30
+ }
31
+ payload.liquidationPrice = calcLeverageLiqPrice(leveragedType, assetPrice, payload.borrowedUsd, payload.liquidationLimitUsd);
32
+ }
33
+ payload.minCollRatio = new Dec(payload.suppliedCollateralUsd).div(payload.borrowLimitUsd).mul(100).toString();
34
+ payload.collLiquidationRatio = new Dec(payload.suppliedCollateralUsd).div(payload.liquidationLimitUsd).mul(100).toString();
35
+ // payload.healthRatio = new Dec(payload.liquidationLimitUsd).div(payload.borrowedUsd).toDP(4).toString();
36
+ payload.minHealthRatio = new Dec(payload.liquidationLimitUsd).div(payload.borrowLimitUsd).toDP(4).toString();
37
+ // TODO: Re-implement netApy calculation
38
+ // const { netApy, incentiveUsd, totalInterestUsd } = calculateNetApy({
39
+ // usedAssets,
40
+ // assetsData,
41
+ // optionalData: { healthRatio: payload.healthRatio },
42
+ // });
43
+ payload.netApy = '0';
44
+ payload.incentiveUsd = '0';
45
+ payload.totalInterestUsd = '0';
46
+ return payload;
47
+ };
@@ -8,3 +8,4 @@ export * as llamaLendHelpers from './llamaLendHelpers';
8
8
  export * as liquityV2Helpers from './liquityV2Helpers';
9
9
  export * as eulerV2Helpers from './eulerHelpers';
10
10
  export * as fluidHelpers from './fluidHelpers';
11
+ export * as aaveV4Helpers from './aaveV4Helpers';
@@ -8,3 +8,4 @@ export * as llamaLendHelpers from './llamaLendHelpers';
8
8
  export * as liquityV2Helpers from './liquityV2Helpers';
9
9
  export * as eulerV2Helpers from './eulerHelpers';
10
10
  export * as fluidHelpers from './fluidHelpers';
11
+ export * as aaveV4Helpers from './aaveV4Helpers';
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import Dec from 'decimal.js';
11
11
  import { NetworkNumber } from '../types/common';
12
- import { AaveMarkets, CompoundMarkets, CrvUsdMarkets, EulerV2Markets, LiquityV2Markets, LlamaLendMarkets, MorphoBlueMarkets, SparkMarkets, } from '../markets';
12
+ import { AaveMarkets, AaveV4Spokes, CompoundMarkets, CrvUsdMarkets, EulerV2Markets, LiquityV2Markets, LlamaLendMarkets, MorphoBlueMarkets, SparkMarkets, } from '../markets';
13
13
  import { _getMorphoBlueAccountData, _getMorphoBlueMarketData, getMorphoEarn } from '../morphoBlue';
14
14
  import { AaveVersions, CompoundVersions, } from '../types';
15
15
  import { _getCompoundV3AccountData, _getCompoundV3MarketsData } from '../compoundV3';
@@ -34,6 +34,7 @@ import { fetchSparkAirdropRewards, fetchSparkRewards } from '../claiming/spark';
34
34
  import { fetchMorphoBlueRewards } from '../claiming/morphoBlue';
35
35
  import { getKingRewards } from '../claiming/king';
36
36
  import { fetchEthenaAirdropRewards } from '../claiming/ethena';
37
+ import { _getAaveV4AccountData, _getAaveV4SpokeData } from '../aaveV4';
37
38
  export function getPortfolioData(provider, network, defaultProvider, addresses, summerFiAddresses) {
38
39
  return __awaiter(this, void 0, void 0, function* () {
39
40
  const isMainnet = network === NetworkNumber.Eth;
@@ -50,6 +51,7 @@ export function getPortfolioData(provider, network, defaultProvider, addresses,
50
51
  const llamaLendMarkets = [NetworkNumber.Eth, NetworkNumber.Arb].includes(network) ? Object.values(LlamaLendMarkets(network)).filter((market) => market.chainIds.includes(network)) : [];
51
52
  const liquityV2Markets = [NetworkNumber.Eth].includes(network) ? Object.values(LiquityV2Markets(network)) : [];
52
53
  const liquityV2MarketsStaking = [NetworkNumber.Eth].includes(network) ? Object.values(LiquityV2Markets(network)).filter(market => !market.isLegacy) : [];
54
+ const aaveV4Spokes = Object.values(AaveV4Spokes(network)).filter((market) => market.chainIds.includes(network));
53
55
  const client = getViemProvider(provider, network, {
54
56
  batch: {
55
57
  multicall: {
@@ -75,6 +77,7 @@ export function getPortfolioData(provider, network, defaultProvider, addresses,
75
77
  const crvUsdMarketsData = {};
76
78
  const llamaLendMarketsData = {};
77
79
  const liquityV2MarketsData = {};
80
+ const aaveV4SpokesData = {};
78
81
  const markets = {
79
82
  morphoMarketsData,
80
83
  compoundV3MarketsData,
@@ -86,6 +89,7 @@ export function getPortfolioData(provider, network, defaultProvider, addresses,
86
89
  crvUsdMarketsData,
87
90
  llamaLendMarketsData,
88
91
  liquityV2MarketsData,
92
+ aaveV4SpokesData,
89
93
  };
90
94
  const positions = {};
91
95
  const stakingPositions = {};
@@ -94,6 +98,7 @@ export function getPortfolioData(provider, network, defaultProvider, addresses,
94
98
  for (const address of allAddresses) {
95
99
  positions[address.toLowerCase()] = {
96
100
  aaveV3: {},
101
+ aaveV4: {},
97
102
  morphoBlue: {},
98
103
  compoundV3: {},
99
104
  spark: {},
@@ -159,6 +164,10 @@ export function getPortfolioData(provider, network, defaultProvider, addresses,
159
164
  const marketData = yield _getAaveV3MarketData(client, network, market);
160
165
  aaveV3MarketsData[market.value] = marketData;
161
166
  })),
167
+ ...aaveV4Spokes.map((spoke) => __awaiter(this, void 0, void 0, function* () {
168
+ const spokeData = yield _getAaveV4SpokeData(client, network, spoke);
169
+ aaveV4SpokesData[spoke.value] = spokeData;
170
+ })),
162
171
  ...aaveV2Markets.map((market) => __awaiter(this, void 0, void 0, function* () {
163
172
  const marketData = yield _getAaveV2MarketsData(client, network, market);
164
173
  aaveV2MarketsData[market.value] = marketData;
@@ -428,6 +437,17 @@ export function getPortfolioData(provider, network, defaultProvider, addresses,
428
437
  positions[address.toLowerCase()].aaveV3[market.value] = { error: `Error fetching AaveV3 account data for address ${address} on market ${market.value}`, data: null };
429
438
  }
430
439
  }))).flat(),
440
+ ...aaveV4Spokes.map((spoke) => allAddresses.map((address) => __awaiter(this, void 0, void 0, function* () {
441
+ try {
442
+ const accData = yield _getAaveV4AccountData(client, network, aaveV4SpokesData[spoke.value], address);
443
+ if (new Dec(accData.suppliedUsd).gt(0))
444
+ positions[address.toLowerCase()].aaveV4[spoke.value] = { error: '', data: accData };
445
+ }
446
+ catch (error) {
447
+ console.error(`Error fetching AaveV4 account data for address ${address} on spoke ${spoke.value}:`, error);
448
+ positions[address.toLowerCase()].aaveV4[spoke.value] = { error: `Error fetching AaveV4 account data for address ${address} on spoke ${spoke.value}`, data: null };
449
+ }
450
+ }))).flat(),
431
451
  ...morphoMarkets.map((market) => addresses.map((address) => __awaiter(this, void 0, void 0, function* () {
432
452
  try {
433
453
  const [accDataPromise, earnDataPromise] = yield Promise.allSettled([
@@ -1,4 +1,4 @@
1
- import { EthAddress, NetworkNumber } from './common';
1
+ import { EthAddress, IncentiveData, NetworkNumber } from './common';
2
2
  export declare enum AaveV4SpokesType {
3
3
  AaveV4CoreSpoke = "aave_v4_core_spoke"
4
4
  }
@@ -64,10 +64,57 @@ export interface AaveV4ReserveAssetData {
64
64
  spokeActive: boolean;
65
65
  spokePaused: boolean;
66
66
  drawnRate: string;
67
+ supplyRate: string;
68
+ supplyIncentives: IncentiveData[];
69
+ borrowIncentives: IncentiveData[];
67
70
  }
71
+ export type AaveV4AssetsData = Record<string, AaveV4ReserveAssetData>;
68
72
  export interface AaveV4SpokeData {
69
- assetsData: Record<string, AaveV4ReserveAssetData>;
73
+ assetsData: AaveV4AssetsData;
70
74
  oracle: EthAddress;
71
75
  oracleDecimals: number;
72
76
  address: EthAddress;
73
77
  }
78
+ export interface AaveV4UsedReserveAsset {
79
+ symbol: string;
80
+ supplied: string;
81
+ suppliedUsd: string;
82
+ drawn: string;
83
+ drawnUsd: string;
84
+ premium: string;
85
+ premiumUsd: string;
86
+ borrowed: string;
87
+ borrowedUsd: string;
88
+ isSupplied: boolean;
89
+ isBorrowed: boolean;
90
+ collateral: boolean;
91
+ }
92
+ export interface AaveV4AggregatedPositionData {
93
+ suppliedUsd: string;
94
+ suppliedCollateralUsd: string;
95
+ borrowLimitUsd: string;
96
+ liquidationLimitUsd: string;
97
+ borrowedUsd: string;
98
+ drawnUsd: string;
99
+ premiumUsd: string;
100
+ leftToBorrowUsd: string;
101
+ ratio: string;
102
+ collRatio: string;
103
+ liqRatio: string;
104
+ liqPercent: string;
105
+ leveragedType: string;
106
+ leveragedAsset: string;
107
+ liquidationPrice: string;
108
+ leveragedLsdAssetRatio?: string;
109
+ minCollRatio: string;
110
+ collLiquidationRatio: string;
111
+ minHealthRatio: string;
112
+ netApy: string;
113
+ incentiveUsd: string;
114
+ totalInterestUsd: string;
115
+ }
116
+ export type AaveV4UsedReserveAssets = Record<string, AaveV4UsedReserveAsset>;
117
+ export interface AaveV4AccountData extends AaveV4AggregatedPositionData {
118
+ usedAssets: AaveV4UsedReserveAssets;
119
+ healthFactor: string;
120
+ }
@@ -1,4 +1,5 @@
1
1
  import { AaveV2PositionData, AaveV3PositionData, AaveVersions } from './aave';
2
+ import { AaveV4AccountData, AaveV4SpokesType } from './aaveV4';
2
3
  import { EthAddress } from './common';
3
4
  import { CompoundV2PositionData, CompoundV3PositionData, CompoundVersions } from './compound';
4
5
  import { CrvUSDUserData, CrvUSDVersions } from './curveUsd';
@@ -51,6 +52,9 @@ export interface PortfolioPositionsDataForAddress {
51
52
  [key: string]: FluidVaultData;
52
53
  };
53
54
  };
55
+ aaveV4: {
56
+ [key in AaveV4SpokesType]?: PortfolioProtocolData<AaveV4AccountData>;
57
+ };
54
58
  }
55
59
  export interface PortfolioPositionsData {
56
60
  [key: EthAddress]: PortfolioPositionsDataForAddress;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defisaver/positions-sdk",
3
- "version": "2.1.43-aave-v4-dev-1",
3
+ "version": "2.1.43-aave-v4-dev-2",
4
4
  "description": "",
5
5
  "main": "./cjs/index.js",
6
6
  "module": "./esm/index.js",
@@ -12,7 +12,7 @@
12
12
  "dev": "tsc -p tsconfig.json --watch",
13
13
  "lint": "eslint src/ --fix",
14
14
  "lint-check": "eslint src/",
15
- "test": "mocha tests/aaveV4.ts",
15
+ "test": "mocha tests/portfolio.ts",
16
16
  "test-single": "mocha ./tests/$npm_config_name.ts",
17
17
  "test:debugger": "mocha --inspect-brk tests/*",
18
18
  "version-bump": "git commit -am \"Version bump to $(npm version patch | cut -c 2-)\""
@@ -3,16 +3,22 @@ import Dec from 'decimal.js';
3
3
  import { assetAmountInEth, getAssetInfoByAddress } from '@defisaver/tokens';
4
4
  import { getViemProvider } from '../services/viem';
5
5
  import {
6
+ AaveV4AccountData,
6
7
  AaveV4HubAssetOnChainData,
7
8
  AaveV4HubOnChainData,
8
9
  AaveV4ReserveAssetData, AaveV4ReserveAssetOnChain, AaveV4SpokeData, AaveV4SpokeInfo,
10
+ AaveV4UsedReserveAssets,
9
11
  } from '../types';
10
- import { EthAddress, EthereumProvider, NetworkNumber } from '../types/common';
12
+ import {
13
+ EthAddress, EthereumProvider, IncentiveData, IncentiveKind, NetworkNumber,
14
+ } from '../types/common';
11
15
  import { AaveV4ViewContractViem } from '../contracts';
16
+ import { getStakingApy, STAKING_ASSETS } from '../staking';
17
+ import { wethToEth } from '../services/utils';
18
+ import { aaveV4GetAggregatedPositionData } from '../helpers/aaveV4Helpers';
12
19
 
13
20
  const fetchHubData = async (viewContract: ReturnType<typeof AaveV4ViewContractViem>, hubAddress: EthAddress): Promise<AaveV4HubOnChainData> => {
14
21
  const hubData = await viewContract.read.getHubAllAssetsData([hubAddress]);
15
- console.log('hubData', hubData);
16
22
  return {
17
23
  assets: hubData.reduce((acc: Record<number, AaveV4HubAssetOnChainData>, assetOnChainData) => {
18
24
  acc[assetOnChainData.assetId] = {
@@ -24,10 +30,35 @@ const fetchHubData = async (viewContract: ReturnType<typeof AaveV4ViewContractVi
24
30
  };
25
31
  };
26
32
 
27
- const formatReserveAsset = (reserveAsset: AaveV4ReserveAssetOnChain, hubAsset: AaveV4HubAssetOnChainData, oracleDecimals: number): AaveV4ReserveAssetData => {
28
- const assetInfo = getAssetInfoByAddress(reserveAsset.underlying);
33
+ const formatReserveAsset = async (reserveAsset: AaveV4ReserveAssetOnChain, hubAsset: AaveV4HubAssetOnChainData, oracleDecimals: number, network: NetworkNumber): Promise<AaveV4ReserveAssetData> => {
34
+ const assetInfo = getAssetInfoByAddress(reserveAsset.underlying, network);
35
+ const symbol = wethToEth(assetInfo.symbol);
36
+
37
+ const isStakingAsset = STAKING_ASSETS.includes(symbol);
38
+ const supplyIncentives: IncentiveData[] = [];
39
+ const borrowIncentives: IncentiveData[] = [];
40
+
41
+ if (isStakingAsset) {
42
+ const yieldApy = await getStakingApy(symbol, network as NetworkNumber);
43
+ supplyIncentives.push({
44
+ apy: yieldApy,
45
+ token: symbol,
46
+ incentiveKind: IncentiveKind.Staking,
47
+ description: `Native ${symbol} yield.`,
48
+ });
49
+ if (reserveAsset.borrowable) {
50
+ // when borrowing assets whose value increases over time
51
+ borrowIncentives.push({
52
+ apy: new Dec(yieldApy).mul(-1).toString(),
53
+ token: symbol,
54
+ incentiveKind: IncentiveKind.Reward,
55
+ description: `Due to the native yield of ${symbol}, the value of the debt would increase over time.`,
56
+ });
57
+ }
58
+ }
59
+
29
60
  return ({
30
- symbol: assetInfo.symbol,
61
+ symbol,
31
62
  underlying: reserveAsset.underlying,
32
63
  hub: reserveAsset.hub,
33
64
  assetId: reserveAsset.assetId,
@@ -38,15 +69,18 @@ const formatReserveAsset = (reserveAsset: AaveV4ReserveAssetOnChain, hubAsset: A
38
69
  collateralFactor: new Dec(reserveAsset.collateralFactor).div(10000).toNumber(),
39
70
  liquidationFee: new Dec(reserveAsset.liquidationFee).div(10000).toNumber(),
40
71
  price: new Dec(reserveAsset.price).div(new Dec(10).pow(oracleDecimals)).toString(),
41
- totalSupplied: assetAmountInEth(reserveAsset.totalSupplied.toString(), assetInfo.symbol),
42
- totalDrawn: assetAmountInEth(reserveAsset.totalDrawn.toString(), assetInfo.symbol),
43
- totalPremium: assetAmountInEth(reserveAsset.totalPremium.toString(), assetInfo.symbol),
44
- totalDebt: assetAmountInEth(reserveAsset.totalDebt.toString(), assetInfo.symbol),
45
- supplyCap: assetAmountInEth(reserveAsset.supplyCap.toString(), assetInfo.symbol),
46
- borrowCap: assetAmountInEth(reserveAsset.borrowCap.toString(), assetInfo.symbol),
72
+ totalSupplied: assetAmountInEth(reserveAsset.totalSupplied.toString(), symbol),
73
+ totalDrawn: assetAmountInEth(reserveAsset.totalDrawn.toString(), symbol),
74
+ totalPremium: assetAmountInEth(reserveAsset.totalPremium.toString(), symbol),
75
+ totalDebt: assetAmountInEth(reserveAsset.totalDebt.toString(), symbol),
76
+ supplyCap: assetAmountInEth(reserveAsset.supplyCap.toString(), symbol),
77
+ borrowCap: assetAmountInEth(reserveAsset.borrowCap.toString(), symbol),
47
78
  spokeActive: reserveAsset.spokeActive,
48
79
  spokePaused: reserveAsset.spokePaused,
49
80
  drawnRate: new Dec(hubAsset.drawnRate).div(new Dec(10).pow(27)).toString(),
81
+ supplyRate: '0', // To be implemented
82
+ supplyIncentives,
83
+ borrowIncentives,
50
84
  });
51
85
  };
52
86
 
@@ -61,7 +95,7 @@ export async function _getAaveV4SpokeData(provider: Client, network: NetworkNumb
61
95
  }),
62
96
  ]);
63
97
 
64
- const reserveAssetsArray = spokeData[1].map((reserveAssetOnChain: AaveV4ReserveAssetOnChain) => formatReserveAsset(reserveAssetOnChain, hubsData[reserveAssetOnChain.hub].assets[reserveAssetOnChain.assetId], +spokeData[0].oracleDecimals.toString()));
98
+ const reserveAssetsArray = await Promise.all(spokeData[1].map(async (reserveAssetOnChain: AaveV4ReserveAssetOnChain) => formatReserveAsset(reserveAssetOnChain, hubsData[reserveAssetOnChain.hub].assets[reserveAssetOnChain.assetId], +spokeData[0].oracleDecimals.toString(), network)));
65
99
 
66
100
  return {
67
101
  assetsData: reserveAssetsArray.reduce((acc: Record<string, AaveV4ReserveAssetData>, reserveAsset: AaveV4ReserveAssetData) => {
@@ -78,11 +112,46 @@ export async function getAaveV4SpokeData(provider: EthereumProvider, network: Ne
78
112
  return _getAaveV4SpokeData(getViemProvider(provider, network), network, spoke, blockNumber);
79
113
  }
80
114
 
81
- export async function _getAaveV4AccountData(provider: Client, network: NetworkNumber, marketData: AaveV4SpokeData, address: EthAddress, blockNumber: 'latest' | number = 'latest'): Promise<any> {
115
+ export async function _getAaveV4AccountData(provider: Client, network: NetworkNumber, spokeData: AaveV4SpokeData, address: EthAddress, blockNumber: 'latest' | number = 'latest'): Promise<AaveV4AccountData> {
82
116
  const viewContract = AaveV4ViewContractViem(provider, network, blockNumber);
83
117
 
84
- const loanData = await viewContract.read.getLoanData([marketData.address, address]);
85
- console.log('loanData', loanData);
118
+ const loanData = await viewContract.read.getLoanData([spokeData.address, address]);
119
+
120
+ const healthFactor = new Dec(loanData.healthFactor).div(1e18).toString();
121
+
122
+ const usedAssets = loanData.reserves.reduce((acc: AaveV4UsedReserveAssets, usedReserveAsset) => {
123
+ const reserveData = spokeData.assetsData[wethToEth(getAssetInfoByAddress(usedReserveAsset.underlying, network).symbol)];
124
+ const price = reserveData.price;
125
+ const supplied = assetAmountInEth(usedReserveAsset.supplied.toString(), reserveData.symbol);
126
+ const drawn = assetAmountInEth(usedReserveAsset.drawn.toString(), reserveData.symbol);
127
+ const premium = assetAmountInEth(usedReserveAsset.premium.toString(), reserveData.symbol);
128
+ const borrowed = assetAmountInEth(usedReserveAsset.totalDebt.toString(), reserveData.symbol);
129
+ acc[reserveData.symbol] = {
130
+ symbol: reserveData.symbol,
131
+ supplied,
132
+ suppliedUsd: new Dec(supplied).mul(price).toString(),
133
+ drawn,
134
+ drawnUsd: new Dec(drawn).mul(price).toString(),
135
+ premium,
136
+ premiumUsd: new Dec(premium).mul(price).toString(),
137
+ borrowed,
138
+ borrowedUsd: new Dec(borrowed).mul(price).toString(),
139
+ isSupplied: !new Dec(supplied).eq(0),
140
+ isBorrowed: usedReserveAsset.isBorrowing,
141
+ collateral: usedReserveAsset.isUsingAsCollateral,
142
+ };
143
+ return acc;
144
+ }, {});
145
+
146
+ return {
147
+ usedAssets,
148
+ healthFactor,
149
+ ...aaveV4GetAggregatedPositionData({
150
+ usedAssets,
151
+ assetsData: spokeData.assetsData,
152
+ network,
153
+ }),
154
+ };
86
155
  }
87
156
 
88
157
  export async function getAaveV4AccountData(provider: EthereumProvider, network: NetworkNumber, marketData: AaveV4SpokeData, address: EthAddress, blockNumber: 'latest' | number = 'latest'): Promise<any> {
@@ -0,0 +1,66 @@
1
+ import Dec from 'decimal.js';
2
+ import { calcLeverageLiqPrice, getAssetsTotal, isLeveragedPos } from '../../moneymarket';
3
+ import { AaveV4AggregatedPositionData, AaveV4AssetsData, AaveV4UsedReserveAssets } from '../../types';
4
+ import { NetworkNumber } from '../../types/common';
5
+
6
+ export const aaveV4GetAggregatedPositionData = ({
7
+ usedAssets,
8
+ assetsData,
9
+ network,
10
+ }: {
11
+ usedAssets: AaveV4UsedReserveAssets,
12
+ assetsData: AaveV4AssetsData,
13
+ network: NetworkNumber,
14
+ }): AaveV4AggregatedPositionData => {
15
+ const payload = {} as AaveV4AggregatedPositionData;
16
+ payload.suppliedUsd = getAssetsTotal(usedAssets, ({ isSupplied }: { isSupplied: boolean }) => isSupplied, ({ suppliedUsd }: { suppliedUsd: string }) => suppliedUsd);
17
+ payload.suppliedCollateralUsd = getAssetsTotal(usedAssets, ({ isSupplied, collateral }: { isSupplied: boolean, collateral: string }) => isSupplied && collateral, ({ suppliedUsd }: { suppliedUsd: string }) => suppliedUsd);
18
+ payload.borrowLimitUsd = getAssetsTotal(
19
+ usedAssets,
20
+ ({ isSupplied, collateral }: { isSupplied: boolean, collateral: string }) => isSupplied && collateral,
21
+ ({ symbol, suppliedUsd }: { symbol: string, suppliedUsd: string }) => new Dec(suppliedUsd).mul(assetsData[symbol].collateralFactor),
22
+ );
23
+ payload.liquidationLimitUsd = getAssetsTotal(
24
+ usedAssets,
25
+ ({ isSupplied, collateral }: { isSupplied: boolean, collateral: string }) => isSupplied && collateral,
26
+ // TODO: Verify if liquidation factor is available in Aave V4, currently using collateralFactor as placeholder
27
+ ({ symbol, suppliedUsd }: { symbol: string, suppliedUsd: string }) => new Dec(suppliedUsd).mul(assetsData[symbol].collateralFactor),
28
+ );
29
+ payload.borrowedUsd = getAssetsTotal(usedAssets, ({ isBorrowed }: { isBorrowed: boolean }) => isBorrowed, ({ borrowedUsd }: { borrowedUsd: string }) => borrowedUsd);
30
+ payload.drawnUsd = getAssetsTotal(usedAssets, ({ isBorrowed }: { isBorrowed: boolean }) => isBorrowed, ({ drawnUsd }: { drawnUsd: string }) => drawnUsd);
31
+ payload.premiumUsd = getAssetsTotal(usedAssets, ({ isBorrowed }: { isBorrowed: boolean }) => isBorrowed, ({ premiumUsd }: { premiumUsd: string }) => premiumUsd);
32
+ const leftToBorrowUsd = new Dec(payload.borrowLimitUsd).sub(payload.borrowedUsd);
33
+ payload.leftToBorrowUsd = leftToBorrowUsd.lte('0') ? '0' : leftToBorrowUsd.toString();
34
+ payload.ratio = +payload.suppliedUsd ? new Dec(payload.borrowLimitUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
35
+ payload.collRatio = +payload.suppliedUsd ? new Dec(payload.suppliedCollateralUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
36
+ payload.liqRatio = new Dec(payload.borrowLimitUsd).div(payload.liquidationLimitUsd).toString();
37
+ payload.liqPercent = new Dec(payload.borrowLimitUsd).div(payload.liquidationLimitUsd).mul(100).toString();
38
+ const { leveragedType, leveragedAsset } = isLeveragedPos(usedAssets);
39
+ payload.leveragedType = leveragedType;
40
+ payload.leveragedAsset = leveragedAsset;
41
+ payload.liquidationPrice = '';
42
+ if (leveragedType !== '') {
43
+ let assetPrice = assetsData[leveragedAsset].price;
44
+ if (leveragedType === 'lsd-leverage') {
45
+ // Treat ETH like a stablecoin in a long stETH position
46
+ payload.leveragedLsdAssetRatio = new Dec(assetsData[leveragedAsset].price).div(assetsData.ETH.price).toDP(18).toString();
47
+ assetPrice = new Dec(assetPrice).div(assetsData.ETH.price).toString();
48
+ }
49
+ payload.liquidationPrice = calcLeverageLiqPrice(leveragedType, assetPrice, payload.borrowedUsd, payload.liquidationLimitUsd);
50
+ }
51
+ payload.minCollRatio = new Dec(payload.suppliedCollateralUsd).div(payload.borrowLimitUsd).mul(100).toString();
52
+ payload.collLiquidationRatio = new Dec(payload.suppliedCollateralUsd).div(payload.liquidationLimitUsd).mul(100).toString();
53
+ // payload.healthRatio = new Dec(payload.liquidationLimitUsd).div(payload.borrowedUsd).toDP(4).toString();
54
+ payload.minHealthRatio = new Dec(payload.liquidationLimitUsd).div(payload.borrowLimitUsd).toDP(4).toString();
55
+
56
+ // TODO: Re-implement netApy calculation
57
+ // const { netApy, incentiveUsd, totalInterestUsd } = calculateNetApy({
58
+ // usedAssets,
59
+ // assetsData,
60
+ // optionalData: { healthRatio: payload.healthRatio },
61
+ // });
62
+ payload.netApy = '0';
63
+ payload.incentiveUsd = '0';
64
+ payload.totalInterestUsd = '0';
65
+ return payload;
66
+ };
@@ -8,3 +8,4 @@ export * as llamaLendHelpers from './llamaLendHelpers';
8
8
  export * as liquityV2Helpers from './liquityV2Helpers';
9
9
  export * as eulerV2Helpers from './eulerHelpers';
10
10
  export * as fluidHelpers from './fluidHelpers';
11
+ export * as aaveV4Helpers from './aaveV4Helpers';
@@ -2,6 +2,7 @@ import Dec from 'decimal.js';
2
2
  import { EthAddress, EthereumProvider, NetworkNumber } from '../types/common';
3
3
  import {
4
4
  AaveMarkets,
5
+ AaveV4Spokes,
5
6
  CompoundMarkets,
6
7
  CrvUsdMarkets,
7
8
  EulerV2Markets,
@@ -14,6 +15,7 @@ import { _getMorphoBlueAccountData, _getMorphoBlueMarketData, getMorphoEarn } fr
14
15
  import {
15
16
  AaveV2MarketData,
16
17
  AaveV3MarketData,
18
+ AaveV4SpokeData,
17
19
  AaveVersions,
18
20
  CdpInfo,
19
21
  CompoundV2MarketsData,
@@ -49,6 +51,7 @@ import { fetchSparkAirdropRewards, fetchSparkRewards } from '../claiming/spark';
49
51
  import { fetchMorphoBlueRewards } from '../claiming/morphoBlue';
50
52
  import { getKingRewards } from '../claiming/king';
51
53
  import { fetchEthenaAirdropRewards } from '../claiming/ethena';
54
+ import { _getAaveV4AccountData, _getAaveV4SpokeData } from '../aaveV4';
52
55
 
53
56
  export async function getPortfolioData(provider: EthereumProvider, network: NetworkNumber, defaultProvider: EthereumProvider, addresses: EthAddress[], summerFiAddresses: EthAddress[]): Promise<{
54
57
  positions: PortfolioPositionsData;
@@ -71,6 +74,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
71
74
  const llamaLendMarkets = [NetworkNumber.Eth, NetworkNumber.Arb].includes(network) ? Object.values(LlamaLendMarkets(network)).filter((market) => market.chainIds.includes(network)) : [];
72
75
  const liquityV2Markets = [NetworkNumber.Eth].includes(network) ? Object.values(LiquityV2Markets(network)) : [];
73
76
  const liquityV2MarketsStaking = [NetworkNumber.Eth].includes(network) ? Object.values(LiquityV2Markets(network)).filter(market => !market.isLegacy) : [];
77
+ const aaveV4Spokes = Object.values(AaveV4Spokes(network)).filter((market) => market.chainIds.includes(network));
74
78
 
75
79
  const client = getViemProvider(provider, network, {
76
80
  batch: {
@@ -98,6 +102,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
98
102
  const crvUsdMarketsData: Record<string, CrvUSDGlobalMarketData> = {};
99
103
  const llamaLendMarketsData: Record<string, LlamaLendGlobalMarketData> = {};
100
104
  const liquityV2MarketsData: Record<string, LiquityV2MarketData> = {};
105
+ const aaveV4SpokesData: Record<string, AaveV4SpokeData> = {};
101
106
 
102
107
  const markets = {
103
108
  morphoMarketsData,
@@ -110,6 +115,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
110
115
  crvUsdMarketsData,
111
116
  llamaLendMarketsData,
112
117
  liquityV2MarketsData,
118
+ aaveV4SpokesData,
113
119
  };
114
120
 
115
121
  const positions: PortfolioPositionsData = {};
@@ -120,6 +126,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
120
126
  for (const address of allAddresses) {
121
127
  positions[address.toLowerCase() as EthAddress] = {
122
128
  aaveV3: {},
129
+ aaveV4: {},
123
130
  morphoBlue: {},
124
131
  compoundV3: {},
125
132
  spark: {},
@@ -188,6 +195,10 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
188
195
  const marketData = await _getAaveV3MarketData(client, network, market);
189
196
  aaveV3MarketsData[market.value] = marketData;
190
197
  }),
198
+ ...aaveV4Spokes.map(async (spoke) => {
199
+ const spokeData = await _getAaveV4SpokeData(client, network, spoke);
200
+ aaveV4SpokesData[spoke.value] = spokeData;
201
+ }),
191
202
  ...aaveV2Markets.map(async (market) => {
192
203
  const marketData = await _getAaveV2MarketsData(client, network, market);
193
204
  aaveV2MarketsData[market.value] = marketData;
@@ -441,6 +452,15 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
441
452
  positions[address.toLowerCase() as EthAddress].aaveV3[market.value] = { error: `Error fetching AaveV3 account data for address ${address} on market ${market.value}`, data: null };
442
453
  }
443
454
  })).flat(),
455
+ ...aaveV4Spokes.map((spoke) => allAddresses.map(async (address) => {
456
+ try {
457
+ const accData = await _getAaveV4AccountData(client, network, aaveV4SpokesData[spoke.value], address);
458
+ if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].aaveV4[spoke.value] = { error: '', data: accData };
459
+ } catch (error) {
460
+ console.error(`Error fetching AaveV4 account data for address ${address} on spoke ${spoke.value}:`, error);
461
+ positions[address.toLowerCase() as EthAddress].aaveV4[spoke.value] = { error: `Error fetching AaveV4 account data for address ${address} on spoke ${spoke.value}`, data: null };
462
+ }
463
+ })).flat(),
444
464
  ...morphoMarkets.map((market) => addresses.map(async (address) => {
445
465
  try {
446
466
  const [accDataPromise, earnDataPromise] = await Promise.allSettled([
@@ -1,4 +1,4 @@
1
- import { EthAddress, NetworkNumber } from './common';
1
+ import { EthAddress, IncentiveData, NetworkNumber } from './common';
2
2
 
3
3
  export enum AaveV4SpokesType {
4
4
  AaveV4CoreSpoke = 'aave_v4_core_spoke',
@@ -71,11 +71,63 @@ export interface AaveV4ReserveAssetData {
71
71
  spokeActive: boolean,
72
72
  spokePaused: boolean,
73
73
  drawnRate: string,
74
+ supplyRate: string,
75
+ supplyIncentives: IncentiveData[];
76
+ borrowIncentives: IncentiveData[];
74
77
  }
75
78
 
79
+ export type AaveV4AssetsData = Record<string, AaveV4ReserveAssetData>;
80
+
76
81
  export interface AaveV4SpokeData {
77
- assetsData: Record<string, AaveV4ReserveAssetData>,
82
+ assetsData: AaveV4AssetsData,
78
83
  oracle: EthAddress,
79
84
  oracleDecimals: number,
80
85
  address: EthAddress,
81
86
  }
87
+
88
+ export interface AaveV4UsedReserveAsset {
89
+ symbol: string,
90
+ supplied: string,
91
+ suppliedUsd: string,
92
+ drawn: string,
93
+ drawnUsd: string,
94
+ premium: string,
95
+ premiumUsd: string,
96
+ borrowed: string,
97
+ borrowedUsd: string,
98
+ isSupplied: boolean,
99
+ isBorrowed: boolean,
100
+ collateral: boolean,
101
+ }
102
+
103
+ export interface AaveV4AggregatedPositionData {
104
+ suppliedUsd: string,
105
+ suppliedCollateralUsd: string,
106
+ borrowLimitUsd: string,
107
+ liquidationLimitUsd: string,
108
+ borrowedUsd: string,
109
+ drawnUsd: string,
110
+ premiumUsd: string,
111
+ leftToBorrowUsd: string,
112
+ ratio: string,
113
+ collRatio: string,
114
+ liqRatio: string,
115
+ liqPercent: string,
116
+ leveragedType: string,
117
+ leveragedAsset: string,
118
+ liquidationPrice: string,
119
+ leveragedLsdAssetRatio?: string,
120
+ minCollRatio: string,
121
+ collLiquidationRatio: string,
122
+ minHealthRatio: string,
123
+ netApy: string,
124
+ incentiveUsd: string,
125
+ totalInterestUsd: string,
126
+ }
127
+
128
+ export type AaveV4UsedReserveAssets = Record<string, AaveV4UsedReserveAsset>;
129
+
130
+ export interface AaveV4AccountData extends AaveV4AggregatedPositionData {
131
+ usedAssets: AaveV4UsedReserveAssets,
132
+ healthFactor: string,
133
+ }
@@ -1,4 +1,5 @@
1
1
  import { AaveV2PositionData, AaveV3PositionData, AaveVersions } from './aave';
2
+ import { AaveV4AccountData, AaveV4SpokesType } from './aaveV4';
2
3
  import { EthAddress } from './common';
3
4
  import { CompoundV2PositionData, CompoundV3PositionData, CompoundVersions } from './compound';
4
5
  import { CrvUSDUserData, CrvUSDVersions } from './curveUsd';
@@ -54,6 +55,9 @@ export interface PortfolioPositionsDataForAddress {
54
55
  [key: string]: FluidVaultData;
55
56
  };
56
57
  };
58
+ aaveV4: {
59
+ [key in AaveV4SpokesType]?: PortfolioProtocolData<AaveV4AccountData>;
60
+ };
57
61
  }
58
62
 
59
63
  export interface PortfolioPositionsData {