@defisaver/positions-sdk 2.1.79 → 2.1.80

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.
Files changed (75) hide show
  1. package/cjs/aaveV4/index.d.ts +7 -0
  2. package/cjs/aaveV4/index.js +245 -0
  3. package/cjs/aaveV4/lend.d.ts +55 -0
  4. package/cjs/aaveV4/lend.js +131 -0
  5. package/cjs/config/contracts.d.ts +1551 -0
  6. package/cjs/config/contracts.js +9 -0
  7. package/cjs/contracts.d.ts +32741 -0
  8. package/cjs/contracts.js +2 -1
  9. package/cjs/eulerV2/index.d.ts +1 -2
  10. package/cjs/eulerV2/index.js +4 -5
  11. package/cjs/helpers/aaveV4Helpers/index.d.ts +25 -0
  12. package/cjs/helpers/aaveV4Helpers/index.js +233 -0
  13. package/cjs/helpers/index.d.ts +1 -0
  14. package/cjs/helpers/index.js +2 -1
  15. package/cjs/index.d.ts +2 -1
  16. package/cjs/index.js +3 -1
  17. package/cjs/markets/aaveV4/index.d.ts +34 -0
  18. package/cjs/markets/aaveV4/index.js +182 -0
  19. package/cjs/markets/index.d.ts +1 -0
  20. package/cjs/markets/index.js +3 -1
  21. package/cjs/moneymarket/moneymarketCommonService.js +1 -1
  22. package/cjs/portfolio/index.js +20 -0
  23. package/cjs/services/utils.d.ts +1 -1
  24. package/cjs/services/utils.js +3 -3
  25. package/cjs/types/aaveV4.d.ts +159 -0
  26. package/cjs/types/aaveV4.js +22 -0
  27. package/cjs/types/index.d.ts +1 -0
  28. package/cjs/types/index.js +1 -0
  29. package/cjs/types/portfolio.d.ts +4 -0
  30. package/esm/aaveV4/index.d.ts +7 -0
  31. package/esm/aaveV4/index.js +202 -0
  32. package/esm/aaveV4/lend.d.ts +55 -0
  33. package/esm/aaveV4/lend.js +124 -0
  34. package/esm/config/contracts.d.ts +1551 -0
  35. package/esm/config/contracts.js +8 -0
  36. package/esm/contracts.d.ts +32741 -0
  37. package/esm/contracts.js +1 -0
  38. package/esm/eulerV2/index.d.ts +1 -2
  39. package/esm/eulerV2/index.js +5 -6
  40. package/esm/helpers/aaveV4Helpers/index.d.ts +25 -0
  41. package/esm/helpers/aaveV4Helpers/index.js +221 -0
  42. package/esm/helpers/index.d.ts +1 -0
  43. package/esm/helpers/index.js +1 -0
  44. package/esm/index.d.ts +2 -1
  45. package/esm/index.js +2 -1
  46. package/esm/markets/aaveV4/index.d.ts +34 -0
  47. package/esm/markets/aaveV4/index.js +161 -0
  48. package/esm/markets/index.d.ts +1 -0
  49. package/esm/markets/index.js +1 -0
  50. package/esm/moneymarket/moneymarketCommonService.js +1 -1
  51. package/esm/portfolio/index.js +21 -1
  52. package/esm/services/utils.d.ts +1 -1
  53. package/esm/services/utils.js +1 -1
  54. package/esm/types/aaveV4.d.ts +159 -0
  55. package/esm/types/aaveV4.js +19 -0
  56. package/esm/types/index.d.ts +1 -0
  57. package/esm/types/index.js +1 -0
  58. package/esm/types/portfolio.d.ts +4 -0
  59. package/package.json +2 -2
  60. package/src/aaveV4/index.ts +223 -0
  61. package/src/aaveV4/lend.ts +185 -0
  62. package/src/config/contracts.ts +8 -0
  63. package/src/contracts.ts +2 -0
  64. package/src/eulerV2/index.ts +12 -7
  65. package/src/helpers/aaveV4Helpers/index.ts +291 -0
  66. package/src/helpers/index.ts +1 -0
  67. package/src/index.ts +2 -0
  68. package/src/markets/aaveV4/index.ts +191 -0
  69. package/src/markets/index.ts +6 -1
  70. package/src/moneymarket/moneymarketCommonService.ts +1 -1
  71. package/src/portfolio/index.ts +20 -0
  72. package/src/services/utils.ts +1 -1
  73. package/src/types/aaveV4.ts +175 -0
  74. package/src/types/index.ts +2 -1
  75. package/src/types/portfolio.ts +4 -0
@@ -31,7 +31,7 @@ export const mapRange = (input, minInput, maxInput, minOutput, maxOutput) => {
31
31
  // eslint-disable-next-line no-bitwise
32
32
  export const isEnabledOnBitmap = (bitmap, assetId) => (BigInt(bitmap) >> BigInt(assetId)) & BigInt(1);
33
33
  export const MAXUINT = '115792089237316195423570985008687907853269984665640564039457584007913129639935';
34
- export const isMaxuint = (amount) => compareAddresses(MAXUINT, amount);
34
+ export const isMaxUint = (amount) => compareAddresses(MAXUINT, amount);
35
35
  export const isMainnetNetwork = (network) => network === NetworkNumber.Eth;
36
36
  export const DEFAULT_TIMEOUT = 2000; // 2 seconds
37
37
  export const LONGER_TIMEOUT = 5000; // 5 seconds
@@ -0,0 +1,159 @@
1
+ import { EthAddress, IncentiveData, LeverageType, NetworkNumber } from './common';
2
+ export declare enum AaveV4HubsType {
3
+ AaveV4CoreHub = "aave_v4_core_hub",
4
+ AaveV4PlusHub = "aave_v4_plus_hub",
5
+ AaveV4PrimeHub = "aave_v4_prime_hub"
6
+ }
7
+ export declare enum AaveV4SpokesType {
8
+ AaveV4BluechipSpoke = "aave_v4_bluechip_spoke",
9
+ AaveV4EthenaCorrelatedSpoke = "aave_v4_ethena_correlated_spoke",
10
+ AaveV4EthenaEcosystemSpoke = "aave_v4_ethena_ecosystem_spoke",
11
+ AaveV4EtherfiSpoke = "aave_v4_etherfi_spoke",
12
+ AaveV4ForexSpoke = "aave_v4_forex_spoke",
13
+ AaveV4GoldSpoke = "aave_v4_gold_spoke",
14
+ AaveV4KelpSpoke = "aave_v4_kelp_spoke",
15
+ AaveV4LidoSpoke = "aave_v4_lido_spoke",
16
+ AaveV4LombardBtcSpoke = "aave_v4_lombard_btc_spoke",
17
+ AaveV4MainSpoke = "aave_v4_main_spoke"
18
+ }
19
+ export interface AaveV4HubInfo {
20
+ chainIds: NetworkNumber[];
21
+ label: string;
22
+ value: AaveV4HubsType;
23
+ address: EthAddress;
24
+ }
25
+ export interface AaveV4HubAssetOnChainData {
26
+ assetId: number;
27
+ drawnRate: bigint;
28
+ liquidity: bigint;
29
+ liquidityFee: number;
30
+ swept: bigint;
31
+ totalDrawn: bigint;
32
+ totalDrawnShares: bigint;
33
+ totalPremiumShares: bigint;
34
+ }
35
+ export interface AaveV4HubOnChainData {
36
+ assets: Record<number, AaveV4HubAssetOnChainData>;
37
+ }
38
+ export interface AaveV4SpokeInfo {
39
+ chainIds: NetworkNumber[];
40
+ label: string;
41
+ value: AaveV4SpokesType;
42
+ url: string;
43
+ address: EthAddress;
44
+ hubs: EthAddress[];
45
+ }
46
+ export interface AaveV4SpokeData {
47
+ assetsData: AaveV4AssetsData;
48
+ oracle: EthAddress;
49
+ oracleDecimals: number;
50
+ address: EthAddress;
51
+ }
52
+ export interface AaveV4ReserveAssetOnChain {
53
+ underlying: EthAddress;
54
+ hub: EthAddress;
55
+ assetId: number;
56
+ decimals: number;
57
+ paused: boolean;
58
+ frozen: boolean;
59
+ borrowable: boolean;
60
+ collateralRisk: number;
61
+ collateralFactor: number;
62
+ maxLiquidationBonus: number;
63
+ liquidationFee: number;
64
+ price: bigint;
65
+ totalSupplied: bigint;
66
+ totalDrawn: bigint;
67
+ totalPremium: bigint;
68
+ totalDebt: bigint;
69
+ supplyCap: bigint;
70
+ borrowCap: bigint;
71
+ deficitRay: bigint;
72
+ spokeActive: boolean;
73
+ spokeHalted: boolean;
74
+ }
75
+ export interface AaveV4ReserveAssetData {
76
+ symbol: string;
77
+ underlying: EthAddress;
78
+ hub: EthAddress;
79
+ hubName: string;
80
+ assetId: number;
81
+ reserveId: number;
82
+ paused: boolean;
83
+ frozen: boolean;
84
+ borrowable: boolean;
85
+ collateralRisk: number;
86
+ collateralFactor: number;
87
+ liquidationFee: number;
88
+ maxLiquidationBonus: number;
89
+ price: string;
90
+ totalSupplied: string;
91
+ totalDrawn: string;
92
+ totalPremium: string;
93
+ totalDebt: string;
94
+ supplyCap: string;
95
+ borrowCap: string;
96
+ spokeActive: boolean;
97
+ spokeHalted: boolean;
98
+ drawnRate: string;
99
+ supplyRate: string;
100
+ borrowRate: string;
101
+ supplyIncentives: IncentiveData[];
102
+ borrowIncentives: IncentiveData[];
103
+ canBeBorrowed: boolean;
104
+ canBeSupplied: boolean;
105
+ canBeWithdrawn: boolean;
106
+ canBePayBacked: boolean;
107
+ utilization: string;
108
+ }
109
+ export type AaveV4AssetsData = Record<string, AaveV4ReserveAssetData>;
110
+ export interface AaveV4UsedReserveAsset {
111
+ symbol: string;
112
+ hubName: string;
113
+ assetId: number;
114
+ reserveId: number;
115
+ supplied: string;
116
+ suppliedUsd: string;
117
+ drawn: string;
118
+ drawnUsd: string;
119
+ premium: string;
120
+ premiumUsd: string;
121
+ borrowed: string;
122
+ borrowedUsd: string;
123
+ isSupplied: boolean;
124
+ isBorrowed: boolean;
125
+ collateral: boolean;
126
+ collateralFactor: number;
127
+ }
128
+ export interface AaveV4AggregatedPositionData {
129
+ suppliedUsd: string;
130
+ suppliedCollateralUsd: string;
131
+ borrowLimitUsd: string;
132
+ liquidationLimitUsd: string;
133
+ borrowedUsd: string;
134
+ drawnUsd: string;
135
+ premiumUsd: string;
136
+ leftToBorrowUsd: string;
137
+ ratio: string;
138
+ collRatio: string;
139
+ liqRatio: string;
140
+ liqPercent: string;
141
+ leveragedType: LeverageType;
142
+ leveragedAsset: string;
143
+ liquidationPrice: string;
144
+ minCollRatio: string;
145
+ collLiquidationRatio: string;
146
+ minHealthRatio: string;
147
+ healthRatio: string;
148
+ netApy: string;
149
+ incentiveUsd: string;
150
+ totalInterestUsd: string;
151
+ currentVolatilePairRatio?: string;
152
+ minRatio: string;
153
+ }
154
+ export type AaveV4UsedReserveAssets = Record<string, AaveV4UsedReserveAsset>;
155
+ export interface AaveV4AccountData extends AaveV4AggregatedPositionData {
156
+ usedAssets: AaveV4UsedReserveAssets;
157
+ healthFactor: string;
158
+ riskPremiumBps: number;
159
+ }
@@ -0,0 +1,19 @@
1
+ export var AaveV4HubsType;
2
+ (function (AaveV4HubsType) {
3
+ AaveV4HubsType["AaveV4CoreHub"] = "aave_v4_core_hub";
4
+ AaveV4HubsType["AaveV4PlusHub"] = "aave_v4_plus_hub";
5
+ AaveV4HubsType["AaveV4PrimeHub"] = "aave_v4_prime_hub";
6
+ })(AaveV4HubsType || (AaveV4HubsType = {}));
7
+ export var AaveV4SpokesType;
8
+ (function (AaveV4SpokesType) {
9
+ AaveV4SpokesType["AaveV4BluechipSpoke"] = "aave_v4_bluechip_spoke";
10
+ AaveV4SpokesType["AaveV4EthenaCorrelatedSpoke"] = "aave_v4_ethena_correlated_spoke";
11
+ AaveV4SpokesType["AaveV4EthenaEcosystemSpoke"] = "aave_v4_ethena_ecosystem_spoke";
12
+ AaveV4SpokesType["AaveV4EtherfiSpoke"] = "aave_v4_etherfi_spoke";
13
+ AaveV4SpokesType["AaveV4ForexSpoke"] = "aave_v4_forex_spoke";
14
+ AaveV4SpokesType["AaveV4GoldSpoke"] = "aave_v4_gold_spoke";
15
+ AaveV4SpokesType["AaveV4KelpSpoke"] = "aave_v4_kelp_spoke";
16
+ AaveV4SpokesType["AaveV4LidoSpoke"] = "aave_v4_lido_spoke";
17
+ AaveV4SpokesType["AaveV4LombardBtcSpoke"] = "aave_v4_lombard_btc_spoke";
18
+ AaveV4SpokesType["AaveV4MainSpoke"] = "aave_v4_main_spoke";
19
+ })(AaveV4SpokesType || (AaveV4SpokesType = {}));
@@ -14,3 +14,4 @@ export * from './merit';
14
14
  export * from './merkl';
15
15
  export * from './savings';
16
16
  export * from './common';
17
+ export * from './aaveV4';
@@ -14,3 +14,4 @@ export * from './merit';
14
14
  export * from './merkl';
15
15
  export * from './savings';
16
16
  export * from './common';
17
+ export * from './aaveV4';
@@ -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.79",
3
+ "version": "2.1.80",
4
4
  "description": "",
5
5
  "main": "./cjs/index.js",
6
6
  "module": "./esm/index.js",
@@ -21,7 +21,7 @@
21
21
  "author": "",
22
22
  "license": "ISC",
23
23
  "dependencies": {
24
- "@defisaver/tokens": "^1.7.26",
24
+ "@defisaver/tokens": "^1.7.27",
25
25
  "@types/lodash": "^4.17.15",
26
26
  "@types/memoizee": "^0.4.12",
27
27
  "decimal.js": "^10.6.0",
@@ -0,0 +1,223 @@
1
+ import { Client } from 'viem';
2
+ import Dec from 'decimal.js';
3
+ import { assetAmountInEth, getAssetInfoByAddress } from '@defisaver/tokens';
4
+ import { getViemProvider } from '../services/viem';
5
+ import {
6
+ AaveV4AccountData,
7
+ AaveV4HubAssetOnChainData,
8
+ AaveV4HubOnChainData,
9
+ AaveV4ReserveAssetData,
10
+ AaveV4ReserveAssetOnChain,
11
+ AaveV4SpokeData,
12
+ AaveV4SpokeInfo,
13
+ AaveV4UsedReserveAssets,
14
+ EthAddress,
15
+ EthereumProvider,
16
+ IncentiveData,
17
+ IncentiveKind,
18
+ NetworkNumber,
19
+ } from '../types';
20
+ import { AaveV4ViewContractViem } from '../contracts';
21
+ import { getStakingApy, STAKING_ASSETS } from '../staking';
22
+ import { isMaxUint, wethToEth } from '../services/utils';
23
+ import { aaveV4GetAggregatedPositionData, calcUserRiskPremiumBps } from '../helpers/aaveV4Helpers';
24
+ import { getAaveV4HubByAddress } from '../markets/aaveV4';
25
+ import { aprToApy } from '../moneymarket';
26
+
27
+ export * as lend from './lend';
28
+
29
+ const fetchHubData = async (viewContract: ReturnType<typeof AaveV4ViewContractViem>, hubAddress: EthAddress): Promise<AaveV4HubOnChainData> => {
30
+ const hubData = await viewContract.read.getHubAllAssetsData([hubAddress]);
31
+ return {
32
+ assets: hubData.reduce((acc: Record<number, AaveV4HubAssetOnChainData>, assetOnChainData) => {
33
+ acc[assetOnChainData.assetId] = {
34
+ assetId: assetOnChainData.assetId,
35
+ drawnRate: assetOnChainData.drawnRate,
36
+ liquidity: assetOnChainData.liquidity,
37
+ liquidityFee: assetOnChainData.liquidityFee,
38
+ swept: assetOnChainData.swept,
39
+ totalDrawn: assetOnChainData.totalDrawn,
40
+ totalDrawnShares: assetOnChainData.totalDrawnShares,
41
+ totalPremiumShares: assetOnChainData.totalPremiumShares,
42
+ };
43
+ return acc;
44
+ }, {}),
45
+ };
46
+ };
47
+
48
+
49
+ const formatReserveAsset = async (reserveAsset: AaveV4ReserveAssetOnChain, hubAsset: AaveV4HubAssetOnChainData, reserveId: number, oracleDecimals: number, network: NetworkNumber): Promise<AaveV4ReserveAssetData> => {
50
+ const assetInfo = getAssetInfoByAddress(reserveAsset.underlying, network);
51
+ const symbol = wethToEth(assetInfo.symbol);
52
+ const hubInfo = getAaveV4HubByAddress(network, reserveAsset.hub);
53
+ if (!hubInfo) {
54
+ throw new Error(`Hub not found with address: ${reserveAsset.hub}`);
55
+ }
56
+
57
+ const isStakingAsset = STAKING_ASSETS.includes(symbol);
58
+ const supplyIncentives: IncentiveData[] = [];
59
+ const borrowIncentives: IncentiveData[] = [];
60
+
61
+ if (isStakingAsset) {
62
+ const yieldApy = await getStakingApy(symbol, network as NetworkNumber);
63
+ supplyIncentives.push({
64
+ apy: yieldApy,
65
+ token: symbol,
66
+ incentiveKind: IncentiveKind.Staking,
67
+ description: `Native ${symbol} yield.`,
68
+ });
69
+ if (reserveAsset.borrowable) {
70
+ // When borrowing assets whose value increases over time
71
+ borrowIncentives.push({
72
+ apy: new Dec(yieldApy).mul(-1).toString(),
73
+ token: symbol,
74
+ incentiveKind: IncentiveKind.Reward,
75
+ description: `Due to the native yield of ${symbol}, the value of the debt would increase over time.`,
76
+ });
77
+ }
78
+ }
79
+
80
+ const totalSuppliedRaw = reserveAsset.totalSupplied ?? 0;
81
+ const totalDrawnRaw = reserveAsset.totalDrawn ?? 0;
82
+ const totalPremiumRaw = reserveAsset.totalPremium ?? 0;
83
+ const totalDebtRaw = reserveAsset.totalDebt ?? 0;
84
+ const supplyCapRaw = reserveAsset.supplyCap ?? 0;
85
+ const borrowCapRaw = reserveAsset.borrowCap ?? 0;
86
+ const totalSuppliedDec = isMaxUint(totalSuppliedRaw.toString()) ? new Dec(0) : new Dec(totalSuppliedRaw.toString());
87
+ const totalDrawnDec = isMaxUint(totalDrawnRaw.toString()) ? new Dec(0) : new Dec(totalDrawnRaw.toString());
88
+ const utilization = totalSuppliedDec.isZero() ? '0' : totalDrawnDec.times(100).div(totalSuppliedDec).toString();
89
+
90
+ /** @DEV Hub related calculations */
91
+ const drawnRate = new Dec(hubAsset.drawnRate.toString()).div(new Dec(10).pow(27));
92
+ const borrowApr = drawnRate.mul(100);
93
+ const totalDrawn = new Dec(hubAsset.totalDrawn.toString());
94
+ const liquidity = new Dec(hubAsset.liquidity.toString());
95
+ const swept = new Dec(hubAsset.swept.toString());
96
+ const hubUtilizationDenominator = totalDrawn.add(swept).add(liquidity);
97
+ const hubUtilization = hubUtilizationDenominator.isZero() ? new Dec(0) : totalDrawn.div(hubUtilizationDenominator);
98
+ const liquidityFee = new Dec(hubAsset.liquidityFee.toString()).div(new Dec(10).pow(4));
99
+ const totalDrawnShares = new Dec(hubAsset.totalDrawnShares.toString());
100
+ const totalPremiumShares = new Dec(hubAsset.totalPremiumShares.toString());
101
+ const premiumMultiplier = totalDrawnShares.isZero() ? new Dec(1) : totalDrawnShares.add(totalPremiumShares).div(totalDrawnShares);
102
+ const supplyApr = borrowApr.mul(hubUtilization).mul(premiumMultiplier).mul(new Dec(1).minus(liquidityFee));
103
+
104
+ return ({
105
+ symbol,
106
+ underlying: reserveAsset.underlying,
107
+ hub: hubInfo.address,
108
+ hubName: hubInfo?.label,
109
+ assetId: reserveAsset.assetId,
110
+ reserveId,
111
+ paused: reserveAsset.paused,
112
+ frozen: reserveAsset.frozen,
113
+ borrowable: reserveAsset.borrowable,
114
+ collateralRisk: new Dec(reserveAsset.collateralRisk).div(10000).toNumber(),
115
+ collateralFactor: new Dec(reserveAsset.collateralFactor).div(10000).toNumber(),
116
+ liquidationFee: new Dec(reserveAsset.liquidationFee).div(10000).toNumber(),
117
+ maxLiquidationBonus: new Dec(reserveAsset.maxLiquidationBonus).div(10000).toNumber(),
118
+ price: new Dec(reserveAsset.price).div(new Dec(10).pow(oracleDecimals)).toString(),
119
+ totalSupplied: isMaxUint(totalSuppliedRaw.toString()) ? totalSuppliedRaw.toString() : assetAmountInEth(totalSuppliedRaw.toString(), symbol),
120
+ totalDrawn: isMaxUint(totalDrawnRaw.toString()) ? totalDrawnRaw.toString() : assetAmountInEth(totalDrawnRaw.toString(), symbol),
121
+ totalPremium: isMaxUint(totalPremiumRaw.toString()) ? totalPremiumRaw.toString() : assetAmountInEth(totalPremiumRaw.toString(), symbol),
122
+ totalDebt: isMaxUint(totalDebtRaw.toString()) ? totalDebtRaw.toString() : assetAmountInEth(totalDebtRaw.toString(), symbol),
123
+ supplyCap: isMaxUint(supplyCapRaw.toString()) ? supplyCapRaw.toString() : assetAmountInEth(supplyCapRaw.toString(), symbol),
124
+ borrowCap: isMaxUint(borrowCapRaw.toString()) ? borrowCapRaw.toString() : assetAmountInEth(borrowCapRaw.toString(), symbol),
125
+ spokeActive: reserveAsset.spokeActive,
126
+ spokeHalted: reserveAsset.spokeHalted,
127
+ drawnRate: drawnRate.toString(),
128
+ borrowRate: aprToApy(borrowApr.toString()),
129
+ supplyRate: aprToApy(supplyApr.toString()),
130
+ supplyIncentives,
131
+ borrowIncentives,
132
+ canBeBorrowed: reserveAsset.spokeActive && !reserveAsset.spokeHalted && !reserveAsset.paused && !reserveAsset.frozen && reserveAsset.borrowable,
133
+ canBeSupplied: reserveAsset.spokeActive && !reserveAsset.spokeHalted && !reserveAsset.paused && !reserveAsset.frozen,
134
+ canBeWithdrawn: reserveAsset.spokeActive && !reserveAsset.spokeHalted && !reserveAsset.paused,
135
+ canBePayBacked: reserveAsset.spokeActive && !reserveAsset.spokeHalted && !reserveAsset.paused,
136
+ utilization,
137
+ });
138
+ };
139
+
140
+ export async function _getAaveV4SpokeData(provider: Client, network: NetworkNumber, market: AaveV4SpokeInfo, blockNumber: 'latest' | number = 'latest'): Promise<AaveV4SpokeData> {
141
+ const viewContract = AaveV4ViewContractViem(provider, network, blockNumber);
142
+
143
+ const hubsData: Record<EthAddress, AaveV4HubOnChainData> = {};
144
+ const [spokeData] = await Promise.all([
145
+ viewContract.read.getSpokeDataFull([market.address]),
146
+ ...market.hubs.map(async (hubAddress) => {
147
+ hubsData[hubAddress] = await fetchHubData(viewContract, hubAddress);
148
+ }),
149
+ ]);
150
+
151
+ const reserveAssetsArray = await Promise.all(spokeData[1].map(async (reserveAssetOnChain: AaveV4ReserveAssetOnChain, index: number) => formatReserveAsset(reserveAssetOnChain, hubsData[reserveAssetOnChain.hub].assets[reserveAssetOnChain.assetId], index, +spokeData[0].oracleDecimals.toString(), network)));
152
+
153
+ return {
154
+ assetsData: reserveAssetsArray.reduce((acc: Record<string, AaveV4ReserveAssetData>, reserveAsset: AaveV4ReserveAssetData) => {
155
+ acc[`${reserveAsset.symbol}-${reserveAsset.reserveId}`] = reserveAsset;
156
+ return acc;
157
+ }, {}),
158
+ oracle: spokeData[0].oracle,
159
+ oracleDecimals: +spokeData[0].oracleDecimals.toString(),
160
+ address: market.address,
161
+ };
162
+ }
163
+
164
+ export async function getAaveV4SpokeData(provider: EthereumProvider, network: NetworkNumber, spoke: AaveV4SpokeInfo, blockNumber: 'latest' | number = 'latest'): Promise<AaveV4SpokeData> {
165
+ return _getAaveV4SpokeData(getViemProvider(provider, network), network, spoke, blockNumber);
166
+ }
167
+
168
+ export async function _getAaveV4AccountData(provider: Client, network: NetworkNumber, spokeData: AaveV4SpokeData, address: EthAddress, blockNumber: 'latest' | number = 'latest'): Promise<AaveV4AccountData> {
169
+ const viewContract = AaveV4ViewContractViem(provider, network, blockNumber);
170
+
171
+ const loanData = await viewContract.read.getLoanData([spokeData.address, address]);
172
+
173
+ const healthFactorFromContract = new Dec(loanData.healthFactor.toString());
174
+ const healthFactor = isMaxUint(healthFactorFromContract.toString()) ? 'Infinity' : healthFactorFromContract.div(1e18).toString();
175
+ const usedAssets = loanData.reserves.reduce((acc: AaveV4UsedReserveAssets, usedReserveAsset) => {
176
+ const identifier = `${wethToEth(getAssetInfoByAddress(usedReserveAsset.underlying, network).symbol)}-${+usedReserveAsset.reserveId.toString()}`;
177
+ const reserveData = spokeData.assetsData[identifier];
178
+ const price = reserveData.price;
179
+ const supplied = isMaxUint(usedReserveAsset.supplied.toString()) ? usedReserveAsset.supplied.toString() : assetAmountInEth(usedReserveAsset.supplied.toString(), reserveData.symbol);
180
+ const drawn = isMaxUint(usedReserveAsset.drawn.toString()) ? usedReserveAsset.drawn.toString() : assetAmountInEth(usedReserveAsset.drawn.toString(), reserveData.symbol);
181
+ const premium = isMaxUint(usedReserveAsset.premium.toString()) ? usedReserveAsset.premium.toString() : assetAmountInEth(usedReserveAsset.premium.toString(), reserveData.symbol);
182
+ const borrowed = isMaxUint(usedReserveAsset.totalDebt.toString()) ? usedReserveAsset.totalDebt.toString() : assetAmountInEth(usedReserveAsset.totalDebt.toString(), reserveData.symbol);
183
+ acc[identifier] = {
184
+ symbol: reserveData.symbol,
185
+ hubName: reserveData.hubName,
186
+ assetId: reserveData.assetId,
187
+ reserveId: +usedReserveAsset.reserveId.toString(),
188
+ supplied,
189
+ suppliedUsd: new Dec(supplied).mul(price).toString(),
190
+ drawn,
191
+ drawnUsd: new Dec(drawn).mul(price).toString(),
192
+ premium,
193
+ premiumUsd: new Dec(premium).mul(price).toString(),
194
+ borrowed,
195
+ borrowedUsd: new Dec(borrowed).mul(price).toString(),
196
+ isSupplied: !new Dec(supplied).eq(0),
197
+ isBorrowed: usedReserveAsset.isBorrowing,
198
+ collateral: usedReserveAsset.isUsingAsCollateral,
199
+ collateralFactor: new Dec(usedReserveAsset.collateralFactor).div(10000).toNumber(),
200
+ };
201
+ return acc;
202
+ }, {});
203
+
204
+ const aggregated = aaveV4GetAggregatedPositionData({
205
+ usedAssets,
206
+ assetsData: spokeData.assetsData,
207
+ network,
208
+ useUserCollateralFactor: true,
209
+ });
210
+
211
+ const riskPremiumBps = calcUserRiskPremiumBps(usedAssets, spokeData.assetsData);
212
+
213
+ return {
214
+ ...aggregated,
215
+ usedAssets,
216
+ healthFactor,
217
+ riskPremiumBps,
218
+ };
219
+ }
220
+
221
+ export async function getAaveV4AccountData(provider: EthereumProvider, network: NetworkNumber, marketData: AaveV4SpokeData, address: EthAddress, blockNumber: 'latest' | number = 'latest'): Promise<any> {
222
+ return _getAaveV4AccountData(getViemProvider(provider, network), network, marketData, address, blockNumber);
223
+ }
@@ -0,0 +1,185 @@
1
+ import { assetAmountInEth, getAssetInfoByAddress } from '@defisaver/tokens';
2
+ import { AaveV4ViewContractViem } from '../contracts';
3
+ import { getViemProvider } from '../services/viem';
4
+ import { wethToEth } from '../services/utils';
5
+ import {
6
+ EthAddress,
7
+ EthereumProvider,
8
+ NetworkNumber,
9
+ } from '../types/common';
10
+
11
+ export interface AaveV4TokenizationSpokeData {
12
+ underlyingAsset: EthAddress;
13
+ assetId: string;
14
+ decimals: number;
15
+ spoke: EthAddress;
16
+ spokeActive: boolean;
17
+ spokeHalted: boolean;
18
+ spokeDepositCap: string;
19
+ spokeTotalAssets: string;
20
+ spokeTotalShares: string;
21
+ hub: EthAddress;
22
+ hubLiquidity: string;
23
+ hubDrawnRate: string;
24
+ convertToShares: string;
25
+ convertToAssets: string;
26
+ user: EthAddress;
27
+ userSuppliedAssets: string;
28
+ userSuppliedShares: string;
29
+ }
30
+
31
+ export const AAVE_V4_TOKENIZED_SPOKES: Record<string, EthAddress> = {
32
+ EURC_CORE: '0x6D9e2Cdd61CaF69af99b275704B6e272C41c6718',
33
+ GHO_CORE: '0x58C14a5E061c9bC6926c5b853445290F296C2F7B',
34
+ RLUSD_CORE: '0xC8a125AE4275a78AADc53B46Ca10566Bc9B249E0',
35
+ USDC_CORE: '0x531E90a2376902DE8915789Fcc1075e3B0c153E7',
36
+ USDG_CORE: '0xAC2435E3C25e8246870D33ce0a26988A46d5DB68',
37
+ USDT_CORE: '0x5eC44a70F309854fe04d495cFE1B5dA63DD1cc73',
38
+ WBTC_CORE: '0x82A9CC4656784E55Ef2E78F704028B5E1Bfc1732',
39
+ WETH_CORE: '0x7320CF22Ac095bA2a2e0a652F77efB836c2E751b',
40
+ cbBTC_CORE: '0x33B41B74366F55327d959FfF6D6b6fBc2853dbB1',
41
+ frxUSD_CORE: '0x2226749630775ee20230Ad65214fB339087eF30D',
42
+
43
+ GHO_PLUS: '0xA54382db40EC602c0a173A08f9E86Ed40F9D4D10',
44
+ USDC_PLUS: '0xc94bdd83D2c7655C280655D60954e79E88D4F949',
45
+ USDT_PLUS: '0x80835EB50694EE0e519743f67e5401e6FD300006',
46
+ USDe_PLUS: '0x502Cd81da6a8F1785eb2eEE72713B7388E16A854',
47
+
48
+ GHO_PRIME: '0x900fD46d565d1ac8995928c0179052ec02a6D0E1',
49
+ USDC_PRIME: '0x486415fb1F8b062c89ED548f871cf64304AACb31',
50
+ USDT_PRIME: '0x46c588DD8453aC259c1f6a54b4C9A93C2aC3762D',
51
+ };
52
+
53
+ export const AAVE_V4_TOKENIZED_SPOKE_ADDRESSES: Partial<Record<NetworkNumber, EthAddress[]>> = {
54
+ [NetworkNumber.Eth]: Object.values(AAVE_V4_TOKENIZED_SPOKES),
55
+ };
56
+
57
+ export type AaveV4TokenizedHubKey = 'CORE' | 'PLUS' | 'PRIME';
58
+
59
+ export const aaveV4GetTokenizedHubKey = (hubNameOrKey?: string | null): AaveV4TokenizedHubKey | null => {
60
+ if (!hubNameOrKey) return null;
61
+ const normalized = hubNameOrKey.trim().toUpperCase();
62
+
63
+ if (normalized === 'CORE' || normalized === 'CORE HUB') return 'CORE';
64
+ if (normalized === 'PLUS' || normalized === 'PLUS HUB') return 'PLUS';
65
+ if (normalized === 'PRIME' || normalized === 'PRIME HUB') return 'PRIME';
66
+
67
+ if (normalized.includes('CORE')) return 'CORE';
68
+ if (normalized.includes('PLUS')) return 'PLUS';
69
+ if (normalized.includes('PRIME')) return 'PRIME';
70
+
71
+ return null;
72
+ };
73
+
74
+ export const aaveV4GetTokenizedVaultKey = (
75
+ symbol: string,
76
+ hubNameOrKey?: string | null,
77
+ ): string | null => {
78
+ if (!symbol) return null;
79
+ const hubKey = aaveV4GetTokenizedHubKey(hubNameOrKey);
80
+ if (!hubKey) return null;
81
+
82
+ const normalizedSymbol = symbol.trim().replace(/-/g, '_');
83
+
84
+ return `${normalizedSymbol}_${hubKey}`;
85
+ };
86
+
87
+ export const aaveV4GetTokenizedVaultAddress = (
88
+ network: NetworkNumber,
89
+ symbol: string,
90
+ hubNameOrKey?: string | null,
91
+ ): EthAddress | undefined => {
92
+ if (network !== NetworkNumber.Eth) return undefined;
93
+ const key = aaveV4GetTokenizedVaultKey(symbol, hubNameOrKey);
94
+ if (!key) return undefined;
95
+ return AAVE_V4_TOKENIZED_SPOKES[key];
96
+ };
97
+
98
+ /** Parsed tokenization spoke data with human-readable supplied amounts for display */
99
+ export interface AaveV4TokenizationSpokeDataParsed {
100
+ vaultAddress: EthAddress;
101
+ key: string | null;
102
+ symbol: string;
103
+ hubKey: string;
104
+ // ---- Spoke ----
105
+ spokeActive: boolean;
106
+ spokeHalted: boolean;
107
+ /** Deposit cap in asset units (wei string) */
108
+ spokeDepositCap: string;
109
+ /** Total assets currently in spoke in asset units (wei string) */
110
+ spokeTotalAssets: string;
111
+ // ---- Hub ----
112
+ /** Available hub liquidity in asset units (wei string) */
113
+ hubLiquidity: string;
114
+ /** The conversion rate from assets to shares expressed in asset units. */
115
+ convertToShares: string;
116
+ // ---- User ----
117
+ userSuppliedAssetsEth: string;
118
+ userSuppliedSharesEth: string;
119
+ userSuppliedAssets: string;
120
+ userSuppliedShares: string;
121
+ underlyingAsset: EthAddress;
122
+ spoke: EthAddress;
123
+ decimals: number;
124
+ }
125
+
126
+ const AAVE_V4_TOKENIZED_SPOKE_ADDRESS_TO_KEY: Record<string, string> = Object.entries(
127
+ AAVE_V4_TOKENIZED_SPOKES,
128
+ ).reduce((acc, [k, v]) => {
129
+ acc[v.toLowerCase()] = k;
130
+ return acc;
131
+ }, {} as Record<string, string>);
132
+
133
+ /**
134
+ * Fetches tokenization vault data for the given user via getTokenizationSpokesData.
135
+ * Returns parsed data including userSuppliedAssets in human-readable form for each vault.
136
+ */
137
+ export async function getAaveV4TokenizationSpokesData(
138
+ provider: EthereumProvider,
139
+ network: NetworkNumber,
140
+ userAddress: EthAddress,
141
+ ): Promise<AaveV4TokenizationSpokeDataParsed[]> {
142
+ const spokes = AAVE_V4_TOKENIZED_SPOKE_ADDRESSES[network] ?? [];
143
+ if (spokes.length === 0) return [];
144
+
145
+ const client = getViemProvider(provider, network);
146
+ const viewContract = AaveV4ViewContractViem(client, network);
147
+ const raw = await viewContract.read.getTokenizationSpokesData([spokes, userAddress]);
148
+
149
+ return raw.map((r: any, i: number) => {
150
+ const vaultAddress = spokes[i];
151
+ const key = AAVE_V4_TOKENIZED_SPOKE_ADDRESS_TO_KEY[vaultAddress.toLowerCase()] ?? null;
152
+ const symbol = wethToEth(getAssetInfoByAddress(r.underlyingAsset, network).symbol);
153
+ if (symbol === '?') { // unsupported asset
154
+ return null;
155
+ }
156
+ const hubKey = key ? key.split('_').pop() ?? '' : '';
157
+ const decimals = Number(r.decimals ?? 18);
158
+ const userSuppliedAssetsRaw = r.userSuppliedAssets ?? 0;
159
+ const userSuppliedSharesRaw = r.userSuppliedShares ?? 0;
160
+
161
+ const userSuppliedAssetsEth = assetAmountInEth(userSuppliedAssetsRaw.toString(), symbol);
162
+ const userSuppliedSharesEth = assetAmountInEth(userSuppliedSharesRaw.toString(), symbol);
163
+ return {
164
+ vaultAddress,
165
+ key,
166
+ symbol,
167
+ hubKey,
168
+ spokeActive: r.spokeActive ?? true,
169
+ spokeHalted: r.spokeHalted ?? false,
170
+ spokeDepositCap: (r.spokeDepositCap ?? 0).toString(),
171
+ spokeTotalAssets: (r.spokeTotalAssets ?? 0).toString(),
172
+ hubLiquidity: (r.hubLiquidity ?? 0).toString(),
173
+ convertToShares: (r.convertToShares ?? 0).toString(),
174
+ userSuppliedAssetsEth,
175
+ userSuppliedSharesEth,
176
+ userSuppliedAssets: userSuppliedAssetsRaw.toString(),
177
+ userSuppliedShares: userSuppliedSharesRaw.toString(),
178
+ underlyingAsset: r.underlyingAsset,
179
+ spoke: r.spoke,
180
+ decimals,
181
+ };
182
+ }).filter(item => item != null);
183
+ }
184
+
185
+