@defisaver/positions-sdk 2.1.137 → 2.1.139

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 (94) hide show
  1. package/cjs/aaveV4/index.js +26 -10
  2. package/cjs/aaveV4/merkl.d.ts +2 -1
  3. package/cjs/aaveV4/merkl.js +42 -35
  4. package/cjs/config/contracts.d.ts +0 -938
  5. package/cjs/config/contracts.js +1 -8
  6. package/cjs/contracts.d.ts +0 -23433
  7. package/cjs/contracts.js +2 -3
  8. package/cjs/helpers/aaveV4Helpers/index.d.ts +7 -1
  9. package/cjs/helpers/aaveV4Helpers/index.js +22 -10
  10. package/cjs/helpers/index.d.ts +0 -1
  11. package/cjs/helpers/index.js +1 -2
  12. package/cjs/index.d.ts +1 -2
  13. package/cjs/index.js +1 -3
  14. package/cjs/markets/index.d.ts +0 -1
  15. package/cjs/markets/index.js +1 -3
  16. package/cjs/portfolio/discovery.d.ts +1 -1
  17. package/cjs/portfolio/discovery.js +1 -1
  18. package/cjs/portfolio/index.js +0 -32
  19. package/cjs/services/utils.d.ts +1 -0
  20. package/cjs/services/utils.js +3 -1
  21. package/cjs/staking/staking.js +1 -2
  22. package/cjs/types/aaveV4.d.ts +5 -0
  23. package/cjs/types/common.d.ts +4 -0
  24. package/cjs/types/common.js +6 -1
  25. package/cjs/types/index.d.ts +0 -1
  26. package/cjs/types/index.js +0 -1
  27. package/cjs/types/merkl.d.ts +3 -4
  28. package/cjs/types/portfolio.d.ts +0 -4
  29. package/esm/aaveV4/index.js +27 -11
  30. package/esm/aaveV4/merkl.d.ts +2 -1
  31. package/esm/aaveV4/merkl.js +41 -35
  32. package/esm/config/contracts.d.ts +0 -938
  33. package/esm/config/contracts.js +0 -6
  34. package/esm/contracts.d.ts +0 -23433
  35. package/esm/contracts.js +0 -1
  36. package/esm/helpers/aaveV4Helpers/index.d.ts +7 -1
  37. package/esm/helpers/aaveV4Helpers/index.js +21 -10
  38. package/esm/helpers/index.d.ts +0 -1
  39. package/esm/helpers/index.js +0 -1
  40. package/esm/index.d.ts +1 -2
  41. package/esm/index.js +1 -2
  42. package/esm/markets/index.d.ts +0 -1
  43. package/esm/markets/index.js +0 -1
  44. package/esm/portfolio/discovery.d.ts +1 -1
  45. package/esm/portfolio/discovery.js +1 -1
  46. package/esm/portfolio/index.js +1 -33
  47. package/esm/services/utils.d.ts +1 -0
  48. package/esm/services/utils.js +1 -0
  49. package/esm/staking/staking.js +1 -2
  50. package/esm/types/aaveV4.d.ts +5 -0
  51. package/esm/types/common.d.ts +4 -0
  52. package/esm/types/common.js +5 -0
  53. package/esm/types/index.d.ts +0 -1
  54. package/esm/types/index.js +0 -1
  55. package/esm/types/merkl.d.ts +3 -4
  56. package/esm/types/portfolio.d.ts +0 -4
  57. package/package.json +1 -1
  58. package/src/aaveV4/index.ts +29 -11
  59. package/src/aaveV4/merkl.ts +41 -32
  60. package/src/config/contracts.ts +0 -6
  61. package/src/contracts.ts +0 -1
  62. package/src/helpers/aaveV4Helpers/index.ts +22 -9
  63. package/src/helpers/index.ts +0 -1
  64. package/src/index.ts +0 -2
  65. package/src/markets/index.ts +0 -1
  66. package/src/portfolio/discovery.ts +1 -1
  67. package/src/portfolio/index.ts +0 -33
  68. package/src/services/utils.ts +2 -0
  69. package/src/staking/staking.ts +1 -2
  70. package/src/types/aaveV4.ts +5 -0
  71. package/src/types/common.ts +5 -0
  72. package/src/types/index.ts +0 -1
  73. package/src/types/merkl.ts +3 -3
  74. package/src/types/portfolio.ts +0 -4
  75. package/cjs/eulerV2/index.d.ts +0 -47
  76. package/cjs/eulerV2/index.js +0 -225
  77. package/cjs/helpers/eulerHelpers/index.d.ts +0 -22
  78. package/cjs/helpers/eulerHelpers/index.js +0 -231
  79. package/cjs/markets/euler/index.d.ts +0 -8
  80. package/cjs/markets/euler/index.js +0 -30
  81. package/cjs/types/euler.d.ts +0 -153
  82. package/cjs/types/euler.js +0 -14
  83. package/esm/eulerV2/index.d.ts +0 -47
  84. package/esm/eulerV2/index.js +0 -215
  85. package/esm/helpers/eulerHelpers/index.d.ts +0 -22
  86. package/esm/helpers/eulerHelpers/index.js +0 -218
  87. package/esm/markets/euler/index.d.ts +0 -8
  88. package/esm/markets/euler/index.js +0 -24
  89. package/esm/types/euler.d.ts +0 -153
  90. package/esm/types/euler.js +0 -11
  91. package/src/eulerV2/index.ts +0 -331
  92. package/src/helpers/eulerHelpers/index.ts +0 -232
  93. package/src/markets/euler/index.ts +0 -27
  94. package/src/types/euler.ts +0 -178
package/esm/contracts.js CHANGED
@@ -106,7 +106,6 @@ export const WstETHPriceFeedContractViem = createViemContractFromConfigFunc('Wst
106
106
  export const CompV3ViewContractViem = createViemContractFromConfigFunc('CompV3View');
107
107
  export const SparkViewContractViem = createViemContractFromConfigFunc('SparkView');
108
108
  export const SparkIncentiveDataProviderContractViem = createViemContractFromConfigFunc('SparkIncentiveDataProvider');
109
- export const EulerV2ViewContractViem = createViemContractFromConfigFunc('EulerV2View');
110
109
  export const CrvUSDViewContractViem = createViemContractFromConfigFunc('crvUSDView');
111
110
  export const CrvUSDFactoryContractViem = createViemContractFromConfigFunc('crvUSDFactory');
112
111
  export const LlamaLendViewContractViem = createViemContractFromConfigFunc('LlamaLendView');
@@ -1,5 +1,11 @@
1
- import { AaveV4AggregatedPositionData, AaveV4AssetsData, AaveV4ReserveAssetData, AaveV4SpokeInfo, AaveV4UsedReserveAsset, AaveV4UsedReserveAssets, EthereumProvider, LeverageType, NetworkNumber } from '../../types';
1
+ import { AaveV4AggregatedPositionData, AaveV4AssetsData, AaveV4ReserveAssetData, AaveV4SpokeInfo, AaveV4UsedReserveAsset, AaveV4UsedReserveAssets, EthereumProvider, IncentiveData, IncentiveSide, LeverageType, NetworkNumber } from '../../types';
2
2
  export declare const calcUserRiskPremiumBps: (usedAssets: AaveV4UsedReserveAssets, assetsData: AaveV4AssetsData) => number;
3
+ /**
4
+ * The incentives that actually accrue to a position on this reserve for the given side: the
5
+ * intrinsic (staking) incentives plus the single applicable Merkl reward. Display surfaces should
6
+ * use this rather than picking a scoped list directly, so badges always match the net APY math.
7
+ */
8
+ export declare const getAaveV4ApplicableIncentives: (assetData: AaveV4ReserveAssetData, side: IncentiveSide) => IncentiveData[];
3
9
  export declare const calculateNetApyAaveV4: ({ usedAssets, assetsData, }: {
4
10
  usedAssets: AaveV4UsedReserveAssets;
5
11
  assetsData: AaveV4AssetsData;
@@ -11,7 +11,7 @@ import Dec from 'decimal.js';
11
11
  import { assetAmountInWei } from '@defisaver/tokens';
12
12
  import { aprToApy, calcLeverageLiqPrice, getAssetsTotal, STABLE_ASSETS, } from '../../moneymarket';
13
13
  import { calculateInterestEarned } from '../../staking';
14
- import { LeverageType, } from '../../types';
14
+ import { IncentiveSide, LeverageType, } from '../../types';
15
15
  import { borrowOperations } from '../../constants';
16
16
  import { AaveV4ViewContractViem } from '../../contracts';
17
17
  import { getViemProvider } from '../../services/viem';
@@ -69,14 +69,25 @@ export const calcUserRiskPremiumBps = (usedAssets, assetsData) => {
69
69
  };
70
70
  /**
71
71
  * `spokeXIncentives`/`hubXIncentives` are each the intrinsic `base` list with at most one Merkl
72
- * reward appended (see attachAaveV4MerklIncentives). Both scopes can apply to the same position at
73
- * once (spoke-specific + hub-wide), so the full applicable set is base + whatever each scope appended.
72
+ * reward appended (see attachAaveV4MerklIncentives). Merkl regularly publishes the same reward
73
+ * stream at both scopes (a spoke campaign and a hub campaign covering the same borrows, e.g. USDC
74
+ * borrowed from the Prime Hub via the Bluechip Spoke), so the scopes must never be summed — the
75
+ * more specific spoke reward wins and the hub reward only applies when no spoke campaign exists,
76
+ * which is also what every per-asset APY badge (and Aave's own UI) shows.
74
77
  */
75
- const mergeScopedIncentives = (base = [], spokeScoped, hubScoped) => [
76
- ...base,
77
- ...(spokeScoped ? spokeScoped.slice(base.length) : []),
78
- ...(hubScoped ? hubScoped.slice(base.length) : []),
79
- ];
78
+ const mergeScopedIncentives = (base = [], spokeScoped, hubScoped) => {
79
+ const spokeExtras = spokeScoped ? spokeScoped.slice(base.length) : [];
80
+ const hubExtras = hubScoped ? hubScoped.slice(base.length) : [];
81
+ return [...base, ...(spokeExtras.length ? spokeExtras : hubExtras)];
82
+ };
83
+ /**
84
+ * The incentives that actually accrue to a position on this reserve for the given side: the
85
+ * intrinsic (staking) incentives plus the single applicable Merkl reward. Display surfaces should
86
+ * use this rather than picking a scoped list directly, so badges always match the net APY math.
87
+ */
88
+ export const getAaveV4ApplicableIncentives = (assetData, side) => (side === IncentiveSide.Supply
89
+ ? mergeScopedIncentives(assetData.supplyIncentives, assetData.spokeSupplyIncentives, assetData.hubSupplyIncentives)
90
+ : mergeScopedIncentives(assetData.borrowIncentives, assetData.spokeBorrowIncentives, assetData.hubBorrowIncentives));
80
91
  export const calculateNetApyAaveV4 = ({ usedAssets, assetsData, }) => {
81
92
  const riskPremiumBps = calcUserRiskPremiumBps(usedAssets, assetsData);
82
93
  const riskPremiumFraction = new Dec(riskPremiumBps).div(10000);
@@ -90,7 +101,7 @@ export const calculateNetApyAaveV4 = ({ usedAssets, assetsData, }) => {
90
101
  acc.suppliedUsd = new Dec(acc.suppliedUsd).add(amount).toString();
91
102
  const supplyInterest = calculateInterestEarned(amount, assetData.supplyRate, 'year', true);
92
103
  acc.supplyInterest = new Dec(acc.supplyInterest).add(supplyInterest.toString()).toString();
93
- const supplyIncentives = mergeScopedIncentives(assetData.supplyIncentives, assetData.spokeSupplyIncentives, assetData.hubSupplyIncentives);
104
+ const supplyIncentives = getAaveV4ApplicableIncentives(assetData, IncentiveSide.Supply);
94
105
  for (const supplyIncentive of supplyIncentives) {
95
106
  const incentiveInterest = calculateInterestEarned(amount, supplyIncentive.apy, 'year', true);
96
107
  acc.incentiveUsd = new Dec(acc.incentiveUsd).add(incentiveInterest).toString();
@@ -106,7 +117,7 @@ export const calculateNetApyAaveV4 = ({ usedAssets, assetsData, }) => {
106
117
  const userBorrowRate = aprToApy(userBorrowApr.toString());
107
118
  const borrowInterest = calculateInterestEarned(amount, userBorrowRate, 'year', true);
108
119
  acc.borrowInterest = new Dec(acc.borrowInterest).sub(borrowInterest.toString()).toString();
109
- const borrowIncentives = mergeScopedIncentives(assetData.borrowIncentives, assetData.spokeBorrowIncentives, assetData.hubBorrowIncentives);
120
+ const borrowIncentives = getAaveV4ApplicableIncentives(assetData, IncentiveSide.Borrow);
110
121
  for (const borrowIncentive of borrowIncentives) {
111
122
  const incentiveInterest = calculateInterestEarned(amount, borrowIncentive.apy, 'year', true);
112
123
  acc.incentiveUsd = new Dec(acc.incentiveUsd).add(incentiveInterest).toString();
@@ -6,6 +6,5 @@ export * as makerHelpers from './makerHelpers';
6
6
  export * as morphoBlueHelpers from './morphoBlueHelpers';
7
7
  export * as llamaLendHelpers from './llamaLendHelpers';
8
8
  export * as liquityV2Helpers from './liquityV2Helpers';
9
- export * as eulerV2Helpers from './eulerHelpers';
10
9
  export * as fluidHelpers from './fluidHelpers';
11
10
  export * as aaveV4Helpers from './aaveV4Helpers';
@@ -6,6 +6,5 @@ export * as makerHelpers from './makerHelpers';
6
6
  export * as morphoBlueHelpers from './morphoBlueHelpers';
7
7
  export * as llamaLendHelpers from './llamaLendHelpers';
8
8
  export * as liquityV2Helpers from './liquityV2Helpers';
9
- export * as eulerV2Helpers from './eulerHelpers';
10
9
  export * as fluidHelpers from './fluidHelpers';
11
10
  export * as aaveV4Helpers from './aaveV4Helpers';
package/esm/index.d.ts CHANGED
@@ -17,9 +17,8 @@ import * as helpers from './helpers';
17
17
  import * as exchange from './exchange';
18
18
  import * as morphoBlue from './morphoBlue';
19
19
  import * as llamaLend from './llamaLend';
20
- import * as eulerV2 from './eulerV2';
21
20
  import * as portfolio from './portfolio';
22
21
  import * as claiming from './claiming';
23
22
  import * as savings from './savings';
24
23
  export * from './types';
25
- export { aaveV2, aaveV3, aaveV4, compoundV2, compoundV3, spark, curveUsd, liquity, liquityV2, maker, exchange, staking, moneymarket, markets, helpers, morphoBlue, llamaLend, eulerV2, fluid, portfolio, claiming, savings, };
24
+ export { aaveV2, aaveV3, aaveV4, compoundV2, compoundV3, spark, curveUsd, liquity, liquityV2, maker, exchange, staking, moneymarket, markets, helpers, morphoBlue, llamaLend, fluid, portfolio, claiming, savings, };
package/esm/index.js CHANGED
@@ -17,9 +17,8 @@ import * as helpers from './helpers';
17
17
  import * as exchange from './exchange';
18
18
  import * as morphoBlue from './morphoBlue';
19
19
  import * as llamaLend from './llamaLend';
20
- import * as eulerV2 from './eulerV2';
21
20
  import * as portfolio from './portfolio';
22
21
  import * as claiming from './claiming';
23
22
  import * as savings from './savings';
24
23
  export * from './types';
25
- export { aaveV2, aaveV3, aaveV4, compoundV2, compoundV3, spark, curveUsd, liquity, liquityV2, maker, exchange, staking, moneymarket, markets, helpers, morphoBlue, llamaLend, eulerV2, fluid, portfolio, claiming, savings, };
24
+ export { aaveV2, aaveV3, aaveV4, compoundV2, compoundV3, spark, curveUsd, liquity, liquityV2, maker, exchange, staking, moneymarket, markets, helpers, morphoBlue, llamaLend, fluid, portfolio, claiming, savings, };
@@ -5,6 +5,5 @@ export { CrvUsdMarkets } from './curveUsd';
5
5
  export { MorphoBlueMarkets, findMorphoBlueMarket } from './morphoBlue';
6
6
  export { LlamaLendMarkets } from './llamaLend';
7
7
  export { LiquityV2Markets, findLiquityV2MarketByAddress } from './liquityV2';
8
- export { EulerV2Markets } from './euler';
9
8
  export { FluidMarkets, getFluidVersionsDataForNetwork, getFluidMarketInfoById, getFTokenAddress, getFluidMarketInfoByAddress, } from './fluid';
10
9
  export { AaveV4Spokes, findAaveV4SpokeByAddress } from './aaveV4';
@@ -5,6 +5,5 @@ export { CrvUsdMarkets } from './curveUsd';
5
5
  export { MorphoBlueMarkets, findMorphoBlueMarket } from './morphoBlue';
6
6
  export { LlamaLendMarkets } from './llamaLend';
7
7
  export { LiquityV2Markets, findLiquityV2MarketByAddress } from './liquityV2';
8
- export { EulerV2Markets } from './euler';
9
8
  export { FluidMarkets, getFluidVersionsDataForNetwork, getFluidMarketInfoById, getFTokenAddress, getFluidMarketInfoByAddress, } from './fluid';
10
9
  export { AaveV4Spokes, findAaveV4SpokeByAddress } from './aaveV4';
@@ -7,7 +7,7 @@ import { EthAddress, EthereumProvider, NetworkNumber } from '../types/common';
7
7
  * Uses one shared, multicall-batched viem client (same pattern as getPortfolioData) so all
8
8
  * per-market reads collapse into a handful of RPC round trips.
9
9
  *
10
- * Not covered: Maker, Liquity, Liquity V2, Fluid, Euler V2 — these already expose cheap,
10
+ * Not covered: Maker, Liquity, Liquity V2, Fluid — these already expose cheap,
11
11
  * user-scoped discovery entry points elsewhere in the SDK (e.g. _getUserCdps, _getUserPositionsPortfolio).
12
12
  */
13
13
  export declare function getUserPositionsExistence(provider: EthereumProvider, network: NetworkNumber, address: EthAddress, isSim?: boolean): Promise<Record<string, boolean>>;
@@ -28,7 +28,7 @@ const hasAnyBalance = (balances) => ([balances.collateral, balances.debt].some((
28
28
  * Uses one shared, multicall-batched viem client (same pattern as getPortfolioData) so all
29
29
  * per-market reads collapse into a handful of RPC round trips.
30
30
  *
31
- * Not covered: Maker, Liquity, Liquity V2, Fluid, Euler V2 — these already expose cheap,
31
+ * Not covered: Maker, Liquity, Liquity V2, Fluid — these already expose cheap,
32
32
  * user-scoped discovery entry points elsewhere in the SDK (e.g. _getUserCdps, _getUserPositionsPortfolio).
33
33
  */
34
34
  export function getUserPositionsExistence(provider_1, network_1, address_1) {
@@ -9,12 +9,11 @@ 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, AaveV4Spokes, CompoundMarkets, CrvUsdMarkets, EulerV2Markets, LiquityV2Markets, LlamaLendMarkets, MorphoBlueMarkets, SparkMarkets, } from '../markets';
12
+ import { AaveMarkets, AaveV4Spokes, CompoundMarkets, CrvUsdMarkets, LiquityV2Markets, LlamaLendMarkets, MorphoBlueMarkets, SparkMarkets, } from '../markets';
13
13
  import { _getMorphoBlueAccountData, _getMorphoBluePortfolioMarketData, getMorphoEarn } from '../morphoBlue';
14
14
  import { AaveVersions, CompoundVersions, } from '../types';
15
15
  import { _getCompoundV3AccountData, _getCompoundV3MarketsData } from '../compoundV3';
16
16
  import { _getSparkAccountData, _getSparkMarketsData } from '../spark';
17
- import { _getEulerV2AccountData, _getEulerV2MarketsData } from '../eulerV2';
18
17
  import { _getCurveUsdGlobalData, _getCurveUsdUserData } from '../curveUsd';
19
18
  import { _getLlamaLendGlobalData, _getLlamaLendUserData } from '../llamaLend';
20
19
  import { _getAaveV3AccountData, _getAaveV3MarketData, getStakeAaveData } from '../aaveV3';
@@ -26,7 +25,6 @@ import { getViemProvider } from '../services/viem';
26
25
  import { _getLiquityTroveInfo, getLiquityStakingData } from '../liquity';
27
26
  import { _getLiquityV2MarketData, getLiquitySAndYBold, getLiquityV2Staking } from '../liquityV2';
28
27
  import { _getAllUserEarnPositionsWithFTokens, _getUserPositionsPortfolio } from '../fluid';
29
- import { getEulerV2SubAccounts } from '../helpers/eulerHelpers';
30
28
  import { getUmbrellaData } from '../umbrella';
31
29
  import { getMeritUnclaimedRewards, getUnclaimedRewardsForAllMarkets } from '../claiming/aaveV3';
32
30
  import { getCompoundV3Rewards } from '../claiming/compV3';
@@ -42,7 +40,6 @@ export function getPortfolioData(provider_1, network_1, defaultProvider_1, addre
42
40
  const morphoMarkets = Object.values(MorphoBlueMarkets(network)).filter((market) => market.chainIds.includes(network));
43
41
  const compoundV3Markets = Object.values(CompoundMarkets(network)).filter((market) => market.chainIds.includes(network) && market.value !== CompoundVersions.CompoundV2);
44
42
  const sparkMarkets = Object.values(SparkMarkets(network)).filter((market) => market.chainIds.includes(network));
45
- const eulerV2Markets = Object.values(EulerV2Markets(network)).filter((market) => market.chainIds.includes(network));
46
43
  const aaveV3Markets = [AaveVersions.AaveV3, AaveVersions.AaveV3Lido, AaveVersions.AaveV3Etherfi].map((version) => AaveMarkets(network)[version]).filter((market) => market.chainIds.includes(network));
47
44
  const aaveV2Markets = [AaveVersions.AaveV2].map((version) => AaveMarkets(network)[version]).filter((market) => market.chainIds.includes(network));
48
45
  const compoundV2Markets = [CompoundVersions.CompoundV2].map((version) => CompoundMarkets(network)[version]).filter((market) => market.chainIds.includes(network));
@@ -57,7 +54,6 @@ export function getPortfolioData(provider_1, network_1, defaultProvider_1, addre
57
54
  const morphoMarketsData = {};
58
55
  const compoundV3MarketsData = {};
59
56
  const sparkMarketsData = {};
60
- const eulerV2MarketsData = {};
61
57
  const aaveV3MarketsData = {};
62
58
  const makerCdps = {};
63
59
  const aaveV2MarketsData = {};
@@ -70,7 +66,6 @@ export function getPortfolioData(provider_1, network_1, defaultProvider_1, addre
70
66
  morphoMarketsData,
71
67
  compoundV3MarketsData,
72
68
  sparkMarketsData,
73
- eulerV2MarketsData,
74
69
  aaveV3MarketsData,
75
70
  aaveV2MarketsData,
76
71
  compoundV2MarketsData,
@@ -90,7 +85,6 @@ export function getPortfolioData(provider_1, network_1, defaultProvider_1, addre
90
85
  morphoBlue: {},
91
86
  compoundV3: {},
92
87
  spark: {},
93
- eulerV2: {},
94
88
  maker: {},
95
89
  aaveV2: {},
96
90
  compoundV2: {},
@@ -143,10 +137,6 @@ export function getPortfolioData(provider_1, network_1, defaultProvider_1, addre
143
137
  const marketData = yield _getSparkMarketsData(client, network, market);
144
138
  sparkMarketsData[market.value] = marketData;
145
139
  })),
146
- ...eulerV2Markets.map((market) => __awaiter(this, void 0, void 0, function* () {
147
- const marketData = yield _getEulerV2MarketsData(client, network, market);
148
- eulerV2MarketsData[market.value] = marketData;
149
- })),
150
140
  ...aaveV3Markets.map((market) => __awaiter(this, void 0, void 0, function* () {
151
141
  const marketData = yield _getAaveV3MarketData(client, network, market);
152
142
  aaveV3MarketsData[market.value] = marketData;
@@ -495,28 +485,6 @@ export function getPortfolioData(provider_1, network_1, defaultProvider_1, addre
495
485
  positions[address.toLowerCase()].spark[market.value] = { error: `Error fetching Spark account data for address ${address} on market ${market.value}`, data: null };
496
486
  }
497
487
  }))).flat(),
498
- ...eulerV2Markets.map((market) => addresses.map((address) => {
499
- const eulerV2SubAccounts = getEulerV2SubAccounts(address);
500
- const eulerV2Addresses = [address, ...eulerV2SubAccounts];
501
- return Promise.all(eulerV2Addresses.map((eulerAddress) => __awaiter(this, void 0, void 0, function* () {
502
- try {
503
- const accData = yield _getEulerV2AccountData(client, network, eulerAddress, eulerAddress, Object.assign({ selectedMarket: market }, eulerV2MarketsData[market.value]));
504
- if (new Dec(accData.suppliedUsd).gt(0) || new Dec(accData.borrowedUsd).gt(0)) {
505
- if (!positions[address.toLowerCase()].eulerV2[market.value]) {
506
- positions[address.toLowerCase()].eulerV2[market.value] = {};
507
- }
508
- positions[address.toLowerCase()].eulerV2[market.value][eulerAddress.toLowerCase()] = { error: '', data: accData };
509
- }
510
- }
511
- catch (error) {
512
- console.error(`Error fetching EulerV2 account data for address ${eulerAddress} on market ${market.value}:`, error);
513
- if (!positions[address.toLowerCase()].eulerV2[market.value]) {
514
- positions[address.toLowerCase()].eulerV2[market.value] = {};
515
- }
516
- positions[address.toLowerCase()].eulerV2[market.value][eulerAddress.toLowerCase()] = { error: `Error fetching EulerV2 account data for address ${eulerAddress} on market ${market.value}`, data: null };
517
- }
518
- })));
519
- }).flat()).flat(),
520
488
  ...addresses.map((address) => __awaiter(this, void 0, void 0, function* () {
521
489
  var _a;
522
490
  return (_a = makerCdps[address.toLowerCase()]) === null || _a === void 0 ? void 0 : _a.map((cdpInfo) => __awaiter(this, void 0, void 0, function* () {
@@ -8,6 +8,7 @@ export declare const getAbiItem: (abi: any, methodName: string) => any;
8
8
  export declare const ADDRESS_REGEX: RegExp;
9
9
  export declare const isAddress: (address: string) => boolean;
10
10
  export declare const compareAddresses: (addr1?: string, addr2?: string) => boolean;
11
+ export declare const shortenAddress: (address?: string, start?: number, end?: number) => string;
11
12
  export declare const getWeiAmountForDecimals: (amount: string | number, decimals: number) => string;
12
13
  export declare const getEthAmountForDecimals: (amount: string | number, decimals: string | number) => string;
13
14
  export declare const handleWbtcLegacy: (asset: string) => string;
@@ -10,6 +10,7 @@ export const getAbiItem = (abi, methodName) => abi.find((i) => i.name === method
10
10
  export const ADDRESS_REGEX = /0x[0-9a-fA-F]{40}/;
11
11
  export const isAddress = (address) => typeof address === 'string' && (new RegExp(ADDRESS_REGEX).test(address));
12
12
  export const compareAddresses = (addr1 = '', addr2 = '') => addr1.toLowerCase() === addr2.toLowerCase();
13
+ export const shortenAddress = (address = '', start = 8, end = 4) => `${address.substring(0, start)}...${address.slice(-end)}`;
13
14
  export const getWeiAmountForDecimals = (amount, decimals) => new Dec(amount).mul(Math.pow(10, decimals)).floor().toString();
14
15
  export const getEthAmountForDecimals = (amount, decimals) => new Dec(amount).div(Math.pow(10, +decimals)).toString();
15
16
  export const handleWbtcLegacy = (asset) => (asset === 'WBTC Legacy' ? 'WBTC' : asset);
@@ -184,9 +184,8 @@ export const calculateInterestEarned = (principal, interest, type, apy = false)
184
184
  };
185
185
  export const calculateNetApy = ({ usedAssets, assetsData, optionalData, }) => {
186
186
  const sumValues = Object.values(usedAssets).reduce((_acc, usedAsset) => {
187
- var _a;
188
187
  const acc = Object.assign({}, _acc);
189
- const assetData = assetsData[usedAsset.symbol] || assetsData[((_a = usedAsset.vaultAddress) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || ''];
188
+ const assetData = assetsData[usedAsset.symbol];
190
189
  if (usedAsset.isSupplied) {
191
190
  const amount = usedAsset.suppliedUsd;
192
191
  acc.suppliedUsd = new Dec(acc.suppliedUsd).add(amount).toString();
@@ -44,6 +44,11 @@ export interface AaveV4SpokeInfo {
44
44
  value: AaveV4SpokesType;
45
45
  url: string;
46
46
  address: EthAddress;
47
+ /**
48
+ * Known hub addresses, used only to prefetch hub data in parallel with the spoke data.
49
+ * The on-chain reserves are the source of truth — hubs found there but missing here are
50
+ * fetched dynamically, so this list going stale can't break spoke loading.
51
+ */
47
52
  hubs: EthAddress[];
48
53
  }
49
54
  export interface AaveV4SpokeData {
@@ -2,6 +2,10 @@ export declare enum IncentiveKind {
2
2
  Staking = "staking",
3
3
  Reward = "reward"
4
4
  }
5
+ export declare enum IncentiveSide {
6
+ Supply = "supply",
7
+ Borrow = "borrow"
8
+ }
5
9
  export declare enum IncentiveEligibilityId {
6
10
  AaveV3EthenaLiquidLeverage = "0x0dC599DBB180E64E42b87332FDeC3a551445ea44BORROW_BL",
7
11
  AaveV3ArbitrumEthSupply = "0x5d16261c6715a653248269861bbacf68a9774cde",
@@ -3,6 +3,11 @@ export var IncentiveKind;
3
3
  IncentiveKind["Staking"] = "staking";
4
4
  IncentiveKind["Reward"] = "reward";
5
5
  })(IncentiveKind || (IncentiveKind = {}));
6
+ export var IncentiveSide;
7
+ (function (IncentiveSide) {
8
+ IncentiveSide["Supply"] = "supply";
9
+ IncentiveSide["Borrow"] = "borrow";
10
+ })(IncentiveSide || (IncentiveSide = {}));
6
11
  export var IncentiveEligibilityId;
7
12
  (function (IncentiveEligibilityId) {
8
13
  IncentiveEligibilityId["AaveV3EthenaLiquidLeverage"] = "0x0dC599DBB180E64E42b87332FDeC3a551445ea44BORROW_BL";
@@ -7,7 +7,6 @@ export * from './liquityV2';
7
7
  export * from './maker';
8
8
  export * from './morphoBlue';
9
9
  export * from './llamaLend';
10
- export * from './euler';
11
10
  export * from './fluid';
12
11
  export * from './portfolio';
13
12
  export * from './merit';
@@ -7,7 +7,6 @@ export * from './liquityV2';
7
7
  export * from './maker';
8
8
  export * from './morphoBlue';
9
9
  export * from './llamaLend';
10
- export * from './euler';
11
10
  export * from './fluid';
12
11
  export * from './portfolio';
13
12
  export * from './merit';
@@ -1,4 +1,4 @@
1
- import { EthAddress, IncentiveData } from './common';
1
+ import { EthAddress, IncentiveData, IncentiveSide } from './common';
2
2
  export declare enum OpportunityAction {
3
3
  LEND = "LEND",
4
4
  BORROW = "BORROW"
@@ -74,12 +74,11 @@ export type MerkleRewardMap = Record<EthAddress, {
74
74
  borrow?: MerkleRewardInfo;
75
75
  }>;
76
76
  export type AaveV4MerklScopedReward = {
77
- supply?: IncentiveData;
78
- borrow?: IncentiveData;
77
+ [side in IncentiveSide]?: IncentiveData;
79
78
  };
80
79
  /**
81
80
  * Aave V4 Merkl reward campaigns split by scope:
82
- * - `hub`: keyed by underlying token address (lowercase) — rewards for supplying to a hub
81
+ * - `hub`: keyed by `${hubAddress}_${underlyingAddress}` (both lowercase) — rewards for supplying/borrowing via a hub
83
82
  * - `spoke`: keyed by `${spokeAddress}_${underlyingAddress}` (both lowercase) — rewards for supplying/borrowing on a spoke
84
83
  */
85
84
  export type AaveV4MerklRewardMap = {
@@ -3,7 +3,6 @@ import { AaveV4AccountData, AaveV4SpokesType } from './aaveV4';
3
3
  import { EthAddress } from './common';
4
4
  import { CompoundV2PositionData, CompoundV3PositionData, CompoundVersions } from './compound';
5
5
  import { CrvUSDUserData, CrvUSDVersions } from './curveUsd';
6
- import { EulerV2PositionData, EulerV2Versions } from './euler';
7
6
  import { FluidVaultData } from './fluid';
8
7
  import { LiquityTroveInfo } from './liquity';
9
8
  import { LlamaLendUserData, LlamaLendVersionsType } from './llamaLend';
@@ -27,9 +26,6 @@ export interface PortfolioPositionsDataForAddress {
27
26
  spark: {
28
27
  [key in SparkVersions]?: PortfolioProtocolData<SparkPositionData>;
29
28
  };
30
- eulerV2: {
31
- [key in EulerV2Versions]?: Record<EthAddress, PortfolioProtocolData<EulerV2PositionData>>;
32
- };
33
29
  maker: {
34
30
  [key: string]: PortfolioProtocolData<CdpData>;
35
31
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defisaver/positions-sdk",
3
- "version": "2.1.137",
3
+ "version": "2.1.139",
4
4
  "description": "",
5
5
  "main": "./cjs/index.js",
6
6
  "module": "./esm/index.js",
@@ -21,7 +21,9 @@ import {
21
21
  } from '../types';
22
22
  import { AaveV4ViewContractViem } from '../contracts';
23
23
  import { getStakingApy, STAKING_ASSETS } from '../staking';
24
- import { isMaxUint, wethToEth, wethToEthByAddress } from '../services/utils';
24
+ import {
25
+ isMaxUint, shortenAddress, wethToEth, wethToEthByAddress,
26
+ } from '../services/utils';
25
27
  import { aaveV4GetAggregatedPositionData, calcUserRiskPremiumBps } from '../helpers/aaveV4Helpers';
26
28
  import { findAaveV4SpokeByAddress, getAaveV4HubByAddress } from '../markets/aaveV4';
27
29
  import { aprToApy } from '../moneymarket';
@@ -56,10 +58,10 @@ const formatReserveAsset = async (reserveAsset: AaveV4ReserveAssetOnChain, hubAs
56
58
  // tokens package. Flag it so consumers can render it read-only instead of feeding NaN into amounts.
57
59
  const isUnsupported = assetInfo.symbol === '?';
58
60
  const symbol = wethToEth(assetInfo.symbol);
61
+ // The hub registry only provides display metadata — a hub missing from it (newly deployed by
62
+ // Aave, SDK not yet updated) must not prevent the reserve from loading, so fall back to a
63
+ // generated label instead of failing.
59
64
  const hubInfo = getAaveV4HubByAddress(network, reserveAsset.hub);
60
- if (!hubInfo) {
61
- throw new Error(`Hub not found with address: ${reserveAsset.hub}`);
62
- }
63
65
 
64
66
  const isStakingAsset = STAKING_ASSETS.includes(symbol);
65
67
  const supplyIncentives: IncentiveData[] = [];
@@ -124,8 +126,8 @@ const formatReserveAsset = async (reserveAsset: AaveV4ReserveAssetOnChain, hubAs
124
126
  decimals: reserveAsset.decimals,
125
127
  isUnsupported,
126
128
  underlying: reserveAsset.underlying,
127
- hub: hubInfo.address,
128
- hubName: hubInfo?.label,
129
+ hub: hubInfo?.address ?? reserveAsset.hub,
130
+ hubName: hubInfo?.label ?? `Hub ${shortenAddress(reserveAsset.hub)}`,
129
131
  assetId: reserveAsset.assetId,
130
132
  reserveId,
131
133
  paused: reserveAsset.paused,
@@ -163,16 +165,32 @@ const formatReserveAsset = async (reserveAsset: AaveV4ReserveAssetOnChain, hubAs
163
165
  export async function _getAaveV4SpokeData(provider: Client, network: NetworkNumber, market: AaveV4SpokeInfo, blockNumber: 'latest' | number = 'latest'): Promise<AaveV4SpokeData> {
164
166
  const viewContract = AaveV4ViewContractViem(provider, network, blockNumber);
165
167
 
166
- const hubsData: Record<EthAddress, AaveV4HubOnChainData> = {};
168
+ const hubsData: Record<string, AaveV4HubOnChainData> = {};
169
+ const loadHubData = async (hubAddress: EthAddress) => {
170
+ hubsData[hubAddress.toLowerCase()] = await fetchHubData(viewContract, hubAddress);
171
+ };
172
+
173
+ // market.hubs is only a prefetch hint (lets known hubs load in parallel with the spoke data), so
174
+ // failures are tolerated here — any hub the reserves actually reference is (re)fetched below.
167
175
  const [spokeData, merklCampaigns] = await Promise.all([
168
176
  viewContract.read.getSpokeDataFull([market.address]),
169
177
  getAaveV4MerkleCampaigns(network),
170
- ...market.hubs.map(async (hubAddress) => {
171
- hubsData[hubAddress] = await fetchHubData(viewContract, hubAddress);
172
- }),
178
+ ...market.hubs.map((hubAddress) => loadHubData(hubAddress).catch(() => {})),
173
179
  ]);
174
180
 
175
- 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)));
181
+ // The on-chain reserves are the source of truth for which hubs the spoke uses — fetch any hub
182
+ // the prefetch didn't cover, so an asset listed from a hub unknown to the SDK can't break the spoke.
183
+ const missingHubs = [...new Set(spokeData[1].map((reserveAsset: AaveV4ReserveAssetOnChain) => reserveAsset.hub.toLowerCase() as EthAddress))]
184
+ .filter((hubAddress) => !hubsData[hubAddress]);
185
+ await Promise.all(missingHubs.map(loadHubData));
186
+
187
+ const reserveAssetsArray = (await Promise.all(spokeData[1].map(async (reserveAssetOnChain: AaveV4ReserveAssetOnChain, index: number) => {
188
+ const hubAsset = hubsData[reserveAssetOnChain.hub.toLowerCase()]?.assets[reserveAssetOnChain.assetId];
189
+ // A reserve whose hub-side asset data can't be resolved is skipped instead of failing the
190
+ // whole spoke (position math degrades for that one asset only).
191
+ if (!hubAsset) return null;
192
+ return formatReserveAsset(reserveAssetOnChain, hubAsset, index, +spokeData[0].oracleDecimals.toString(), network);
193
+ }))).filter((asset): asset is AaveV4ReserveAssetData => asset !== null);
176
194
 
177
195
  const enrichedAssets = reserveAssetsArray.map((asset) => attachAaveV4MerklIncentives(asset, market.address, merklCampaigns));
178
196
 
@@ -5,6 +5,7 @@ import {
5
5
  AaveV4ReserveAssetData,
6
6
  IncentiveData,
7
7
  IncentiveKind,
8
+ IncentiveSide,
8
9
  MerklOpportunity,
9
10
  OpportunityAction,
10
11
  OpportunityStatus,
@@ -13,12 +14,15 @@ import {
13
14
 
14
15
  /**
15
16
  * Merkl tags Aave V4 reward campaigns by scope via the `type` field:
16
- * - AAVE_V4_HUB_SUPPLY / AAVE_V4_HUB_BORROW → reward tied to a hub (matched per underlying token)
17
+ * - AAVE_V4_HUB_SUPPLY / AAVE_V4_HUB_BORROW → reward tied to a hub (matched per hub contract + underlying)
17
18
  * - AAVE_V4_SPOKE_SUPPLY / AAVE_V4_SPOKE_BORROW → reward tied to a spoke (matched per spoke contract + underlying)
18
- * Hub campaigns identify the underlying via `tokens[0]`; spoke campaigns identify the spoke via `explorerAddress`.
19
+ * Campaigns identify the underlying via `tokens[0]` and the scoping contract (the hub or spoke) via
20
+ * `explorerAddress`. The same underlying exists on several hubs (e.g. USDC on Prime and Paxos), so a
21
+ * hub campaign matched by underlying alone would leak onto every hub's reserves — a campaign whose
22
+ * `explorerAddress` isn't a contract any fetched reserve points to simply never matches.
19
23
  */
20
24
 
21
- const spokeKey = (spokeAddress: string, underlying: string) => `${spokeAddress.toLowerCase()}_${underlying.toLowerCase()}`;
25
+ const scopeKey = (scopeAddress: string, underlying: string) => `${scopeAddress.toLowerCase()}_${underlying.toLowerCase()}`;
22
26
 
23
27
  const buildIncentive = (opportunity: MerklOpportunity): IncentiveData => {
24
28
  const rewardToken = opportunity.rewardsRecord?.breakdowns?.[0]?.token;
@@ -31,42 +35,47 @@ const buildIncentive = (opportunity: MerklOpportunity): IncentiveData => {
31
35
  };
32
36
  };
33
37
 
34
- export const getAaveV4MerkleCampaigns = async (chainId: NetworkNumber): Promise<AaveV4MerklRewardMap> => {
38
+ export const buildAaveV4MerklRewardMap = (opportunities: MerklOpportunity[], chainId: NetworkNumber): AaveV4MerklRewardMap => {
35
39
  const result: AaveV4MerklRewardMap = { hub: {}, spoke: {} };
40
+
41
+ opportunities
42
+ .filter((o) => o.chainId === chainId)
43
+ .filter((o) => o.status === OpportunityStatus.LIVE)
44
+ .filter((o) => typeof o.type === 'string' && o.type.startsWith('AAVE_V4_'))
45
+ .forEach((o) => {
46
+ const underlying = o.tokens?.[0]?.address?.toLowerCase();
47
+ if (!underlying) return;
48
+
49
+ const scopeAddress = o.explorerAddress?.toLowerCase();
50
+ if (!scopeAddress) return;
51
+
52
+ const side = o.action === OpportunityAction.BORROW ? IncentiveSide.Borrow : IncentiveSide.Supply;
53
+ const incentive = buildIncentive(o);
54
+ const key = scopeKey(scopeAddress, underlying);
55
+
56
+ if (o.type.includes('HUB')) {
57
+ if (!result.hub[key]) result.hub[key] = {};
58
+ result.hub[key][side] = incentive;
59
+ } else if (o.type.includes('SPOKE')) {
60
+ if (!result.spoke[key]) result.spoke[key] = {};
61
+ result.spoke[key][side] = incentive;
62
+ }
63
+ });
64
+
65
+ return result;
66
+ };
67
+
68
+ export const getAaveV4MerkleCampaigns = async (chainId: NetworkNumber): Promise<AaveV4MerklRewardMap> => {
36
69
  try {
37
70
  const res = await fetch('https://fe.defisaver.com/api/merkl/opportunities?mainProtocolId=aave&type=AAVE_V4_HUB_SUPPLY,AAVE_V4_HUB_BORROW,AAVE_V4_SPOKE_SUPPLY,AAVE_V4_SPOKE_BORROW', {
38
71
  signal: AbortSignal.timeout(LONGER_TIMEOUT),
39
72
  });
40
73
  if (!res.ok) throw new Error('Failed to fetch Aave V4 Merkle campaigns');
41
74
  const opportunities = await res.json() as MerklOpportunity[];
42
-
43
- opportunities
44
- .filter((o) => o.chainId === chainId)
45
- .filter((o) => o.status === OpportunityStatus.LIVE)
46
- .filter((o) => typeof o.type === 'string' && o.type.startsWith('AAVE_V4_'))
47
- .forEach((o) => {
48
- const underlying = o.tokens?.[0]?.address?.toLowerCase();
49
- if (!underlying) return;
50
-
51
- const side: 'supply' | 'borrow' = o.action === OpportunityAction.BORROW ? 'borrow' : 'supply';
52
- const incentive = buildIncentive(o);
53
-
54
- if (o.type.includes('HUB')) {
55
- if (!result.hub[underlying]) result.hub[underlying] = {};
56
- result.hub[underlying][side] = incentive;
57
- } else if (o.type.includes('SPOKE')) {
58
- const spokeAddress = o.explorerAddress?.toLowerCase();
59
- if (!spokeAddress) return;
60
- const key = spokeKey(spokeAddress, underlying);
61
- if (!result.spoke[key]) result.spoke[key] = {};
62
- result.spoke[key][side] = incentive;
63
- }
64
- });
65
-
66
- return result;
75
+ return buildAaveV4MerklRewardMap(opportunities, chainId);
67
76
  } catch (e) {
68
77
  console.error('Failed to fetch Aave V4 Merkle campaigns', e);
69
- return result;
78
+ return { hub: {}, spoke: {} };
70
79
  }
71
80
  };
72
81
 
@@ -79,8 +88,8 @@ export const attachAaveV4MerklIncentives = (asset: AaveV4ReserveAssetData, spoke
79
88
  const baseSupply = asset.supplyIncentives || [];
80
89
  const baseBorrow = asset.borrowIncentives || [];
81
90
 
82
- const spokeScoped = (spokeAddress && underlying) ? campaigns.spoke[spokeKey(spokeAddress, underlying)] : undefined;
83
- const hubScoped = underlying ? campaigns.hub[underlying] : undefined;
91
+ const spokeScoped = (spokeAddress && underlying) ? campaigns.spoke[scopeKey(spokeAddress, underlying)] : undefined;
92
+ const hubScoped = (asset.hub && underlying) ? campaigns.hub[scopeKey(asset.hub, underlying)] : undefined;
84
93
 
85
94
  return {
86
95
  ...asset,