@defisaver/positions-sdk 2.1.161-portfolio-api-2-dev → 2.1.162-npm-token-dev

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 (101) hide show
  1. package/cjs/aaveV3/index.d.ts +12 -2
  2. package/cjs/aaveV3/index.js +7 -1
  3. package/cjs/aaveV3/sgho.d.ts +16 -2
  4. package/cjs/claiming/aaveV3.d.ts +2 -2
  5. package/cjs/claiming/ethena.d.ts +1 -2
  6. package/cjs/claiming/king.d.ts +1 -2
  7. package/cjs/claiming/spark.d.ts +2 -2
  8. package/cjs/fluid/index.d.ts +49 -5
  9. package/cjs/helpers/aaveHelpers/index.d.ts +9 -1
  10. package/cjs/helpers/aaveHelpers/index.js +23 -6
  11. package/cjs/liquity/index.d.ts +12 -2
  12. package/cjs/liquityV2/index.d.ts +23 -3
  13. package/cjs/portfolio/index.d.ts +5 -5
  14. package/cjs/portfolio/index.js +36 -51
  15. package/cjs/savings/index.js +5 -2
  16. package/cjs/savings/morphoVaults/index.d.ts +1 -1
  17. package/cjs/savings/morphoVaults/index.js +56 -18
  18. package/cjs/savings/morphoVaults/options.d.ts +2 -0
  19. package/cjs/savings/morphoVaults/options.js +61 -1
  20. package/cjs/spark/index.js +1 -0
  21. package/cjs/types/aave.d.ts +3 -98
  22. package/cjs/types/compound.d.ts +2 -26
  23. package/cjs/types/curveUsd.d.ts +1 -9
  24. package/cjs/types/fluid.d.ts +0 -13
  25. package/cjs/types/index.d.ts +0 -1
  26. package/cjs/types/index.js +0 -1
  27. package/cjs/types/liquity.d.ts +0 -12
  28. package/cjs/types/liquityV2.d.ts +0 -24
  29. package/cjs/types/llamaLend.d.ts +1 -12
  30. package/cjs/types/morphoBlue.d.ts +0 -2
  31. package/cjs/types/portfolio.d.ts +10 -88
  32. package/cjs/types/savings/morphoVaults.d.ts +10 -2
  33. package/cjs/types/savings/morphoVaults.js +9 -1
  34. package/cjs/types/spark.d.ts +1 -21
  35. package/cjs/umbrella/index.d.ts +3 -2
  36. package/esm/aaveV3/index.d.ts +12 -2
  37. package/esm/aaveV3/index.js +7 -1
  38. package/esm/aaveV3/sgho.d.ts +16 -2
  39. package/esm/claiming/aaveV3.d.ts +2 -2
  40. package/esm/claiming/aaveV3.js +1 -1
  41. package/esm/claiming/ethena.d.ts +1 -2
  42. package/esm/claiming/king.d.ts +1 -2
  43. package/esm/claiming/spark.d.ts +2 -2
  44. package/esm/claiming/spark.js +1 -1
  45. package/esm/fluid/index.d.ts +49 -5
  46. package/esm/helpers/aaveHelpers/index.d.ts +9 -1
  47. package/esm/helpers/aaveHelpers/index.js +20 -5
  48. package/esm/liquity/index.d.ts +12 -2
  49. package/esm/liquityV2/index.d.ts +23 -3
  50. package/esm/portfolio/index.d.ts +5 -5
  51. package/esm/portfolio/index.js +36 -51
  52. package/esm/savings/index.js +5 -2
  53. package/esm/savings/morphoVaults/index.d.ts +1 -1
  54. package/esm/savings/morphoVaults/index.js +57 -19
  55. package/esm/savings/morphoVaults/options.d.ts +2 -0
  56. package/esm/savings/morphoVaults/options.js +61 -1
  57. package/esm/spark/index.js +1 -0
  58. package/esm/types/aave.d.ts +3 -98
  59. package/esm/types/compound.d.ts +2 -26
  60. package/esm/types/curveUsd.d.ts +1 -9
  61. package/esm/types/fluid.d.ts +0 -13
  62. package/esm/types/index.d.ts +0 -1
  63. package/esm/types/index.js +0 -1
  64. package/esm/types/liquity.d.ts +0 -12
  65. package/esm/types/liquityV2.d.ts +0 -24
  66. package/esm/types/llamaLend.d.ts +1 -12
  67. package/esm/types/morphoBlue.d.ts +0 -2
  68. package/esm/types/portfolio.d.ts +10 -88
  69. package/esm/types/savings/morphoVaults.d.ts +10 -2
  70. package/esm/types/savings/morphoVaults.js +8 -0
  71. package/esm/types/spark.d.ts +1 -21
  72. package/esm/umbrella/index.d.ts +3 -2
  73. package/package.json +3 -2
  74. package/src/aaveV3/index.ts +7 -3
  75. package/src/aaveV3/sgho.ts +17 -2
  76. package/src/claiming/aaveV3.ts +2 -4
  77. package/src/claiming/ethena.ts +5 -5
  78. package/src/claiming/king.ts +3 -3
  79. package/src/claiming/spark.ts +4 -6
  80. package/src/fluid/index.ts +3 -4
  81. package/src/helpers/aaveHelpers/index.ts +24 -7
  82. package/src/liquity/index.ts +2 -2
  83. package/src/liquityV2/index.ts +3 -3
  84. package/src/portfolio/index.ts +57 -76
  85. package/src/savings/index.ts +5 -2
  86. package/src/savings/morphoVaults/index.ts +73 -21
  87. package/src/savings/morphoVaults/options.ts +67 -1
  88. package/src/spark/index.ts +1 -0
  89. package/src/types/aave.ts +2 -104
  90. package/src/types/compound.ts +2 -38
  91. package/src/types/curveUsd.ts +1 -17
  92. package/src/types/fluid.ts +0 -14
  93. package/src/types/index.ts +1 -2
  94. package/src/types/liquity.ts +1 -13
  95. package/src/types/liquityV2.ts +1 -26
  96. package/src/types/llamaLend.ts +1 -19
  97. package/src/types/morphoBlue.ts +0 -3
  98. package/src/types/portfolio.ts +11 -111
  99. package/src/types/savings/morphoVaults.ts +11 -1
  100. package/src/types/spark.ts +1 -25
  101. package/src/umbrella/index.ts +1 -2
@@ -27,11 +27,7 @@ import {
27
27
  LlamaLendGlobalMarketData,
28
28
  MorphoBlueMarketInfo,
29
29
  MorphoMidnightMarketInfo,
30
- PortfolioDataOptions,
31
- PortfolioMarketsData,
32
30
  PortfolioPositionsData,
33
- PortfolioRewardsData,
34
- PortfolioStakingPositionsData,
35
31
  SparkMarketsData,
36
32
  } from '../types';
37
33
  import { _getCompoundV3AccountData, _getCompoundV3MarketsData } from '../compoundV3';
@@ -55,18 +51,13 @@ import { fetchEthenaAirdropRewards } from '../claiming/ethena';
55
51
  import { _getAaveV4AccountData, _getAaveV4SpokeData } from '../aaveV4';
56
52
  import { getUniswapRewards } from '../claiming/uniswap';
57
53
 
58
- export async function getPortfolioData(provider: EthereumProvider, network: NetworkNumber, defaultProvider: EthereumProvider, addresses: EthAddress[], isSim = false, options: PortfolioDataOptions = {}): Promise<{
54
+ export async function getPortfolioData(provider: EthereumProvider, network: NetworkNumber, defaultProvider: EthereumProvider, addresses: EthAddress[], isSim = false): Promise<{
59
55
  positions: PortfolioPositionsData;
60
- stakingPositions: PortfolioStakingPositionsData;
61
- rewardsData: PortfolioRewardsData;
62
- markets: PortfolioMarketsData;
56
+ stakingPositions: any;
57
+ rewardsData: any;
58
+ markets: any;
63
59
  }> {
64
60
  const isMainnet = network === NetworkNumber.Eth;
65
- const includeStaking = options.staking !== false;
66
- const includeRewards = options.rewards !== false;
67
- // The addresses staking and rewards are read for: none when left out, so their fetches below make no calls.
68
- const stakingAddresses = includeStaking ? addresses : [];
69
- const rewardsAddresses = includeRewards ? addresses : [];
70
61
  const isFluidSupported = [NetworkNumber.Eth, NetworkNumber.Arb, NetworkNumber.Base, NetworkNumber.Plasma].includes(network);
71
62
 
72
63
  const morphoMarkets = Object.values(MorphoBlueMarkets(network)).filter((market) => market.chainIds.includes(network));
@@ -105,7 +96,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
105
96
  const liquityV2MarketsData: Record<string, LiquityV2MarketData> = {};
106
97
  const aaveV4SpokesData: Record<string, AaveV4SpokeData> = {};
107
98
 
108
- const markets: PortfolioMarketsData = {
99
+ const markets = {
109
100
  morphoMarketsData,
110
101
  morphoMidnightMarketsData,
111
102
  compoundV3MarketsData,
@@ -120,8 +111,8 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
120
111
  };
121
112
 
122
113
  const positions: PortfolioPositionsData = {};
123
- const stakingPositions: PortfolioStakingPositionsData = {};
124
- const rewardsData: PortfolioRewardsData = {};
114
+ const stakingPositions: any = {};
115
+ const rewardsData: any = {};
125
116
  const allAddresses = [...addresses];
126
117
 
127
118
  for (const address of allAddresses) {
@@ -146,7 +137,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
146
137
  }
147
138
 
148
139
  // TODO: check default values, probably needed when fetching portfolio on unsupported networks
149
- for (const address of stakingAddresses) {
140
+ for (const address of addresses) {
150
141
  stakingPositions[address.toLowerCase() as EthAddress] = {
151
142
  aaveV3: {},
152
143
  morphoBlue: {},
@@ -160,21 +151,15 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
160
151
  error: '',
161
152
  data: {},
162
153
  },
163
- // Set by the sBOLD/yBOLD fetch below, whether it succeeds or fails.
164
- liquityV2SBoldYBold: { error: '', data: null },
165
154
  };
166
- }
167
155
 
168
- for (const address of rewardsAddresses) {
169
- // merkl, spk, king and uniswap are set by their fetches below, whether they succeed or fail.
170
156
  rewardsData[address.toLowerCase() as EthAddress] = {
171
- merkl: { error: '', data: null },
157
+ merkl: {},
172
158
  aaveV3: {},
173
159
  spark: {},
174
- spk: { error: '', data: null },
175
- king: { error: '', data: null },
160
+ spk: {},
161
+ king: {},
176
162
  ethena: {},
177
- uniswap: { error: '', data: null },
178
163
  };
179
164
  }
180
165
 
@@ -250,54 +235,54 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
250
235
  }),
251
236
 
252
237
  // === STAKING DATA (independent of market data) ===
253
- ...stakingAddresses.map(async (address) => {
238
+ ...addresses.map(async (address) => {
254
239
  try {
255
240
  if (!isFluidSupported) return;
256
- stakingPositions[address.toLowerCase() as EthAddress].fluid = await _getAllUserEarnPositionsWithFTokens(client, network, address);
241
+ stakingPositions[address.toLowerCase()].fluid = await _getAllUserEarnPositionsWithFTokens(client, network, address);
257
242
  } catch (error) {
258
243
  console.error(`Error fetching Fluid lend data for address ${address}:`, error);
259
- stakingPositions[address.toLowerCase() as EthAddress].fluid = { error: `Error fetching Fluid lend data for address ${address}`, data: null };
244
+ stakingPositions[address.toLowerCase()].fluid = { error: `Error fetching Fluid lend data for address ${address}`, data: null };
260
245
  }
261
246
  }),
262
- ...stakingAddresses.map(async (address) => {
247
+ ...addresses.map(async (address) => {
263
248
  try {
264
249
  if (!isMainnet) return;
265
- stakingPositions[address.toLowerCase() as EthAddress].liquity = await getLiquityStakingData(client, network, address);
250
+ stakingPositions[address.toLowerCase()].liquity = await getLiquityStakingData(client, network, address);
266
251
  } catch (error) {
267
252
  console.error(`Error fetching Liquity staking data for address ${address}:`, error);
268
- stakingPositions[address.toLowerCase() as EthAddress].liquity = { error: `Error fetching Liquity staking data for address ${address}`, data: null };
253
+ stakingPositions[address.toLowerCase()].liquity = { error: `Error fetching Liquity staking data for address ${address}`, data: null };
269
254
  }
270
255
  }),
271
- ...stakingAddresses.map(async (address) => {
256
+ ...addresses.map(async (address) => {
272
257
  try {
273
258
  if (!isMainnet) return;
274
- stakingPositions[address.toLowerCase() as EthAddress].aaveV3 = await getStakeAaveData(client, network, address);
259
+ stakingPositions[address.toLowerCase()].aaveV3 = await getStakeAaveData(client, network, address);
275
260
  } catch (error) {
276
261
  console.error(`Error fetching Aave V3 staking data for address ${address}:`, error);
277
- stakingPositions[address.toLowerCase() as EthAddress].aaveV3 = { error: `Error fetching Aave V3 staking data for address ${address}`, data: null };
262
+ stakingPositions[address.toLowerCase()].aaveV3 = { error: `Error fetching Aave V3 staking data for address ${address}`, data: null };
278
263
  }
279
264
  }),
280
- ...stakingAddresses.map(async (address) => {
265
+ ...addresses.map(async (address) => {
281
266
  try {
282
267
  if (!isMainnet) return;
283
- stakingPositions[address.toLowerCase() as EthAddress].umbrella = await getUmbrellaData(client, network, address);
268
+ stakingPositions[address.toLowerCase()].umbrella = await getUmbrellaData(client, network, address);
284
269
  } catch (error) {
285
270
  console.error(`Error fetching Umbrella staking data for address ${address}:`, error);
286
- stakingPositions[address.toLowerCase() as EthAddress].umbrella = { error: `Error fetching Umbrella staking data for address ${address}`, data: null };
271
+ stakingPositions[address.toLowerCase()].umbrella = { error: `Error fetching Umbrella staking data for address ${address}`, data: null };
287
272
  }
288
273
  }),
289
274
  // Liquity V2 staking
290
- ...liquityV2MarketsStaking.map(market => stakingAddresses.map(async (address) => {
275
+ ...liquityV2MarketsStaking.map(market => addresses.map(async (address) => {
291
276
  try {
292
277
  if (!isMainnet) {
293
- stakingPositions[address.toLowerCase() as EthAddress].liquityV2[market.value] = { error: '', data: null };
278
+ stakingPositions[address.toLowerCase()].liquityV2[market.value] = { error: '', data: null };
294
279
  return;
295
280
  }
296
281
  const liquityV2StakingData = await getLiquityV2Staking(client, network, market.value, address);
297
- stakingPositions[address.toLowerCase() as EthAddress].liquityV2[market.value] = { error: '', data: liquityV2StakingData };
282
+ stakingPositions[address.toLowerCase()].liquityV2[market.value] = { error: '', data: liquityV2StakingData };
298
283
  } catch (error) {
299
284
  console.error(`Error fetching Liquity V2 staking data for address ${address}, market ${market.value}:`, error);
300
- stakingPositions[address.toLowerCase() as EthAddress].liquityV2[market.value] = { error: `Error fetching Liquity V2 staking data for address ${address}`, data: null };
285
+ stakingPositions[address.toLowerCase()].liquityV2[market.value] = { error: `Error fetching Liquity V2 staking data for address ${address}`, data: null };
301
286
  }
302
287
  })).flat(),
303
288
 
@@ -306,13 +291,13 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
306
291
  (async () => {
307
292
  try {
308
293
  if (!isMainnet) {
309
- for (const address of rewardsAddresses) {
310
- rewardsData[address.toLowerCase() as EthAddress].king = { error: '', data: [] };
294
+ for (const address of addresses) {
295
+ rewardsData[address.toLowerCase()].king = { error: '', data: [] };
311
296
  }
312
297
  return;
313
298
  }
314
- const kingRewards = await getKingRewards(client, network, rewardsAddresses);
315
- for (const address of rewardsAddresses) {
299
+ const kingRewards = await getKingRewards(client, network, addresses);
300
+ for (const address of addresses) {
316
301
  const lowerAddress = address.toLowerCase() as EthAddress;
317
302
  rewardsData[lowerAddress].king = {
318
303
  error: '',
@@ -321,7 +306,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
321
306
  }
322
307
  } catch (error) {
323
308
  console.error('Error fetching King rewards data in batch:', error);
324
- for (const address of rewardsAddresses) {
309
+ for (const address of addresses) {
325
310
  rewardsData[address.toLowerCase() as EthAddress].king = {
326
311
  error: 'Error fetching King rewards data in batch',
327
312
  data: null,
@@ -333,13 +318,13 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
333
318
  (async () => {
334
319
  try {
335
320
  if (!isMainnet) {
336
- for (const address of rewardsAddresses) {
337
- rewardsData[address.toLowerCase() as EthAddress].uniswap = { error: '', data: [] };
321
+ for (const address of addresses) {
322
+ rewardsData[address.toLowerCase()].uniswap = { error: '', data: [] };
338
323
  }
339
324
  return;
340
325
  }
341
- const uniswapRewards = await getUniswapRewards(client, network, rewardsAddresses);
342
- for (const address of rewardsAddresses) {
326
+ const uniswapRewards = await getUniswapRewards(client, network, addresses);
327
+ for (const address of addresses) {
343
328
  const lowerAddress = address.toLowerCase() as EthAddress;
344
329
  rewardsData[lowerAddress].uniswap = {
345
330
  error: '',
@@ -348,7 +333,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
348
333
  }
349
334
  } catch (error) {
350
335
  console.error('Error fetching Uniswap rewards data in batch:', error);
351
- for (const address of rewardsAddresses) {
336
+ for (const address of addresses) {
352
337
  rewardsData[address.toLowerCase() as EthAddress].uniswap = {
353
338
  error: 'Error fetching Uniswap rewards data in batch',
354
339
  data: null,
@@ -356,10 +341,10 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
356
341
  }
357
342
  }
358
343
  })(),
359
- ...sparkMarkets.map((market) => rewardsAddresses.map(async address => {
344
+ ...sparkMarkets.map((market) => addresses.map(async address => {
360
345
  try {
361
346
  if (!isMainnet) {
362
- rewardsData[address.toLowerCase() as EthAddress].spark[market.value] = { error: '', data: [] };
347
+ rewardsData[address.toLowerCase()].spark[market.value] = { error: '', data: [] };
363
348
  return;
364
349
  }
365
350
  const sparkData = await fetchSparkRewards(client, network, address, market.providerAddress);
@@ -369,7 +354,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
369
354
  rewardsData[address.toLowerCase() as EthAddress].spark[market.value] = { error: `Error fetching Spark rewards data for address ${address}`, data: null };
370
355
  }
371
356
  })).flat(),
372
- ...rewardsAddresses.map(async (address) => {
357
+ ...addresses.map(async (address) => {
373
358
  try {
374
359
  const merklData = await getMerklUnclaimedRewards(address, network);
375
360
  rewardsData[address.toLowerCase() as EthAddress].merkl = { error: '', data: merklData };
@@ -378,28 +363,27 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
378
363
  rewardsData[address.toLowerCase() as EthAddress].merkl = { error: `Error fetching Merkl rewards data for address ${address}`, data: null };
379
364
  }
380
365
  }),
381
- ...aaveV3Markets.map(market => rewardsAddresses.map(async (address) => {
366
+ ...aaveV3Markets.map(market => addresses.map(async (address) => {
382
367
  try {
383
368
  const aaveData = await getUnclaimedRewardsForAllMarkets(client, network, address, market.providerAddress);
384
369
  rewardsData[address.toLowerCase() as EthAddress].aaveV3[market.value] = { error: '', data: aaveData };
385
370
  } catch (error) {
386
371
  console.error(`Error fetching Aave V3 Merit rewards data for address ${address}:`, error);
387
- // This market only: the other markets keep their rewards.
388
- rewardsData[address.toLowerCase() as EthAddress].aaveV3[market.value] = { error: `Error fetching Aave V3 rewards data for address ${address} on market ${market.value}`, data: null };
372
+ rewardsData[address.toLowerCase() as EthAddress].aaveV3 = { error: `Error fetching Aave V3 rewards data for address ${address}`, data: null };
389
373
  }
390
374
  })).flat(),
391
375
  // Batch Spark Airdrop rewards
392
376
  (async () => {
393
377
  try {
394
378
  if (!isMainnet) {
395
- for (const address of rewardsAddresses) {
396
- rewardsData[address.toLowerCase() as EthAddress].spk = { error: '', data: [] };
379
+ for (const address of addresses) {
380
+ rewardsData[address.toLowerCase()].spk = { error: '', data: [] };
397
381
  }
398
382
  return;
399
383
  }
400
384
 
401
- const sparkAirdropRewards = await fetchSparkAirdropRewards(client, network, rewardsAddresses);
402
- for (const address of rewardsAddresses) {
385
+ const sparkAirdropRewards = await fetchSparkAirdropRewards(client, network, addresses);
386
+ for (const address of addresses) {
403
387
  const lowerAddress = address.toLowerCase() as EthAddress;
404
388
  rewardsData[lowerAddress].spk = {
405
389
  error: '',
@@ -408,7 +392,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
408
392
  }
409
393
  } catch (error) {
410
394
  console.error('Error fetching Spark Airdrop rewards data in batch:', error);
411
- for (const address of rewardsAddresses) {
395
+ for (const address of addresses) {
412
396
  rewardsData[address.toLowerCase() as EthAddress].spk = {
413
397
  error: 'Error fetching Spark Airdrop rewards data in batch',
414
398
  data: null,
@@ -422,8 +406,8 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
422
406
  return;
423
407
  }
424
408
 
425
- const ethenaAirdropRewards = await fetchEthenaAirdropRewards(rewardsAddresses);
426
- for (const address of rewardsAddresses) {
409
+ const ethenaAirdropRewards = await fetchEthenaAirdropRewards(addresses);
410
+ for (const address of addresses) {
427
411
  const lowerAddress = address.toLowerCase() as EthAddress;
428
412
  rewardsData[lowerAddress].ethena = {
429
413
  error: '',
@@ -432,7 +416,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
432
416
  }
433
417
  } catch (error) {
434
418
  console.error('Error fetching Ethena Airdrop rewards data:', error);
435
- for (const address of rewardsAddresses) {
419
+ for (const address of addresses) {
436
420
  rewardsData[address.toLowerCase() as EthAddress].ethena = {
437
421
  error: 'Error fetching Ethena Airdrop rewards data in batch',
438
422
  data: null,
@@ -465,16 +449,15 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
465
449
  try {
466
450
  const [accDataPromise, earnDataPromise] = await Promise.allSettled([
467
451
  _getMorphoBlueAccountData(client, network, address, market, morphoMarketsData[market.value]),
468
- includeStaking ? getMorphoEarn(client, network, address, market, morphoMarketsData[market.value]) : null,
452
+ getMorphoEarn(client, network, address, market, morphoMarketsData[market.value]),
469
453
  ]);
470
454
  if (accDataPromise.status === 'rejected') {
471
455
  console.error(`Error fetching MorphoBlue account data for address ${address} on market ${market.value}:`, accDataPromise.reason);
472
456
  positions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: `Error fetching MorphoBlue account data for address ${address} on market ${market.value}`, data: null };
473
457
  }
474
458
  if (earnDataPromise.status === 'rejected') {
475
- // An Earn failure belongs to the staking entry: the lending position is not the one that failed.
476
- console.error(`Error fetching MorphoBlue earn data for address ${address} on market ${market.value}:`, earnDataPromise.reason);
477
- stakingPositions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: `Error fetching MorphoBlue earn data for address ${address} on market ${market.value}`, data: null };
459
+ console.error(`Error fetching MorphoBlue account data for address ${address} on market ${market.value}:`, earnDataPromise.reason);
460
+ positions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: `Error fetching MorphoBlue account data for address ${address} on market ${market.value}`, data: null };
478
461
  }
479
462
  if (accDataPromise.status !== 'rejected') {
480
463
  const accData = accDataPromise.value;
@@ -523,9 +506,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
523
506
  positions[address.toLowerCase() as EthAddress].spark[market.value] = { error: `Error fetching Spark account data for address ${address} on market ${market.value}`, data: null };
524
507
  }
525
508
  })).flat(),
526
- // flatMap, not map: each CDP's fetch has to be one of the awaited promises, or getPortfolioData can return
527
- // before a vault's data lands and the vault is missing from the result.
528
- ...addresses.flatMap((address) => (makerCdps[address.toLowerCase() as EthAddress] ?? []).map(async (cdpInfo) => {
509
+ ...addresses.map(async (address) => makerCdps[address.toLowerCase() as EthAddress]?.map(async (cdpInfo) => {
529
510
  try {
530
511
  const cdpData = await _getMakerCdpData(client, network, cdpInfo);
531
512
  if (cdpData) {
@@ -535,7 +516,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
535
516
  console.error(`Error fetching Maker CDP data for address ${address} with ID ${cdpInfo.id}:`, error);
536
517
  positions[address.toLowerCase() as EthAddress].maker[cdpInfo.id] = { error: `Error fetching Maker CDP data for address ${address} with ID ${cdpInfo.id}`, data: null };
537
518
  }
538
- })),
519
+ })).flat(),
539
520
  ...aaveV2Markets.map((market) => addresses.map(async (address) => {
540
521
  try {
541
522
  const accData = await _getAaveV2AccountData(client, network, address, aaveV2MarketsData[market.value].assetsData, market);
@@ -587,13 +568,13 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
587
568
  }
588
569
  })).flat(),
589
570
  // liquity sBold/yBold and staking options
590
- ...stakingAddresses.map(async (address) => {
571
+ ...addresses.map(async (address) => {
591
572
  try {
592
573
  if (!isMainnet) {
593
574
  stakingPositions[address.toLowerCase() as EthAddress].liquityV2SBoldYBold = { error: '', data: null };
594
575
  return;
595
576
  }
596
- const data = await getLiquitySAndYBold(client, network, stakingPositions[address.toLowerCase() as EthAddress].liquityV2, address);
577
+ const data = await getLiquitySAndYBold(client, network, stakingPositions[address.toLowerCase()].liquityV2, address);
597
578
  stakingPositions[address.toLowerCase() as EthAddress].liquityV2SBoldYBold = { error: '', data };
598
579
  } catch (error) {
599
580
  console.error(`Error fetching SBold/YBold data for address ${address}:`, error);
@@ -32,7 +32,8 @@ export const getSavingsData = async (
32
32
  network: NetworkNumber,
33
33
  accounts: EthAddress[],
34
34
  ) => {
35
- const morphoVaultsList = Object.keys(morphoVaults.morphoVaultsOptions.MORPHO_VAULTS) as MorphoVaultType[];
35
+ const morphoVaultsList = (Object.keys(morphoVaults.morphoVaultsOptions.MORPHO_VAULTS) as MorphoVaultType[])
36
+ .filter((key) => morphoVaults.morphoVaultsOptions.getMorphoVault(key).network === network);
36
37
  const yearnVaultsList = Object.keys(yearnVaults.yearnVaultsOptions.YEARN_VAULTS) as YearnVaultType[];
37
38
  const sparkSavingsVaultsList = Object.keys(sparkSavingsVaults.sparkSavingsVaultsOptions.SPARK_SAVINGS_VAULTS) as SparkSavingsVaultType[];
38
39
  const yearnV3VaultsList = Object.keys(yearnV3Vaults.yearnV3VaultsOptions.YEARN_V3_VAULTS) as YearnV3VaultType[];
@@ -42,7 +43,7 @@ export const getSavingsData = async (
42
43
  const savingsData: SavingsData = {};
43
44
 
44
45
  await Promise.all([
45
- ...(network === NetworkNumber.Eth ? [
46
+ ...(morphoVaultsList.length ? [
46
47
  (async () => {
47
48
  try {
48
49
  const vaults = morphoVaultsList.map((vaultKey) => morphoVaults.morphoVaultsOptions.getMorphoVault(vaultKey));
@@ -52,6 +53,8 @@ export const getSavingsData = async (
52
53
  console.error('[getSavingsData] Error fetching morpho vaults:', err);
53
54
  }
54
55
  })(),
56
+ ] : []),
57
+ ...(network === NetworkNumber.Eth ? [
55
58
  ...yearnVaultsList.map(async (vaultKey) => {
56
59
  try {
57
60
  const vault = yearnVaults.yearnVaultsOptions.getYearnVault(vaultKey);
@@ -4,10 +4,10 @@ import { request as graphqlRequest } from 'graphql-request';
4
4
  import { assetAmountInEth } from '@defisaver/tokens';
5
5
  import * as morphoVaultsOptions from './options';
6
6
  import {
7
- EthAddress, EthereumProvider, NetworkNumber, MorphoVault, MorphoVaultType, SavingsVaultData,
7
+ EthAddress, EthereumProvider, NetworkNumber, MorphoVault, MorphoVaultType, MorphoVaultVersion, SavingsVaultData,
8
8
  } from '../../types';
9
9
  import { getViemProvider } from '../../services/viem';
10
- import { getMorphoVaultContractViem } from '../../contracts';
10
+ import { getErc4626ContractViem, getMorphoVaultContractViem } from '../../contracts';
11
11
  import { MORPHO_API_URL } from '../../constants';
12
12
 
13
13
  export {
@@ -17,38 +17,49 @@ export {
17
17
  // Morpho API caps list page size at 100 items
18
18
  const MORPHO_API_PAGE_SIZE = 100;
19
19
 
20
- const vaultsLiquidityQuery = `
20
+ const buildLiquidityQuery = (listField: 'vaults' | 'vaultV2s') => `
21
21
  query VaultsLiquidity($addresses: [String!], $chainIds: [Int!]) {
22
- vaults(first: ${MORPHO_API_PAGE_SIZE}, where: { address_in: $addresses, chainId_in: $chainIds }) {
22
+ ${listField}(first: ${MORPHO_API_PAGE_SIZE}, where: { address_in: $addresses, chainId_in: $chainIds }) {
23
23
  items {
24
24
  address
25
- liquidity {
26
- underlying
27
- }
25
+ liquidity${listField === 'vaults' ? ' {\n underlying\n }' : ''}
28
26
  }
29
27
  }
30
28
  }`;
31
29
 
30
+ const liquidityQueries = {
31
+ [MorphoVaultVersion.V1]: { field: 'vaults', query: buildLiquidityQuery('vaults') },
32
+ [MorphoVaultVersion.V2]: { field: 'vaultV2s', query: buildLiquidityQuery('vaultV2s') },
33
+ } as const;
34
+
35
+ type LiquidityItem = { address: string, liquidity: { underlying: string | number | null } | string | number | null };
36
+
32
37
  /**
33
- * Fetches liquidity for all given vaults in a single API request (chunked if over the page cap),
38
+ * Fetches liquidity for all given vaults, one API request per vault version (chunked if over the page cap),
34
39
  * returned as a map keyed by lowercased vault address. A vault missing from the API response is
35
40
  * absent from the map.
36
41
  */
37
42
  export const fetchMorphoVaultsLiquidity = async (network: NetworkNumber, vaults: MorphoVault[]): Promise<Record<string, string>> => {
38
- const chunks: MorphoVault[][] = [];
39
- for (let i = 0; i < vaults.length; i += MORPHO_API_PAGE_SIZE) chunks.push(vaults.slice(i, i + MORPHO_API_PAGE_SIZE));
43
+ const requests: { version: MorphoVaultVersion, chunk: MorphoVault[] }[] = [];
44
+ [MorphoVaultVersion.V1, MorphoVaultVersion.V2].forEach((version) => {
45
+ const versionVaults = vaults.filter((vault) => vault.version === version);
46
+ for (let i = 0; i < versionVaults.length; i += MORPHO_API_PAGE_SIZE) requests.push({ version, chunk: versionVaults.slice(i, i + MORPHO_API_PAGE_SIZE) });
47
+ });
40
48
 
41
49
  const liquidityByAddress: Record<string, string> = {};
42
- await Promise.all(chunks.map(async (chunk) => {
43
- const data = await graphqlRequest(MORPHO_API_URL, vaultsLiquidityQuery, {
50
+ await Promise.all(requests.map(async ({ version, chunk }) => {
51
+ const { field, query } = liquidityQueries[version];
52
+ const data = await graphqlRequest(MORPHO_API_URL, query, {
44
53
  addresses: chunk.map((vault) => vault.address),
45
54
  chainIds: [network],
46
55
  // the BigInt scalar serializes as a JSON number when it fits in Number.MAX_SAFE_INTEGER, a string otherwise
47
- }) as { vaults: { items: { address: string, liquidity: { underlying: string | number | null } | null }[] | null } };
56
+ }) as Record<string, { items: LiquidityItem[] | null }>;
48
57
 
49
- (data.vaults.items || []).forEach((item) => {
50
- if (item?.liquidity?.underlying !== undefined && item.liquidity?.underlying !== null) {
51
- liquidityByAddress[item.address.toLowerCase()] = String(item.liquidity.underlying);
58
+ (data[field].items || []).forEach((item) => {
59
+ // V1 exposes liquidity as { underlying }, V2 as a plain BigInt scalar
60
+ const underlying = item?.liquidity !== null && typeof item?.liquidity === 'object' ? item.liquidity.underlying : item?.liquidity;
61
+ if (underlying !== undefined && underlying !== null) {
62
+ liquidityByAddress[item.address.toLowerCase()] = String(underlying);
52
63
  }
53
64
  });
54
65
  }));
@@ -63,7 +74,22 @@ const getBatchedViemProvider = (provider: EthereumProvider, network: NetworkNumb
63
74
  },
64
75
  });
65
76
 
66
- const getMorphoVaultChainData = async (provider: Client, morphoVault: MorphoVault, accounts: EthAddress[]) => {
77
+ // Vault V2 is ERC-4626 but has no decimals offset, so shares are converted with convertToAssets
78
+ const getMorphoVaultV2ChainData = async (provider: Client, morphoVault: MorphoVault, accounts: EthAddress[]) => {
79
+ const vaultContract = getErc4626ContractViem(provider, morphoVault.address);
80
+
81
+ const [totalAssets, suppliedAssets] = await Promise.all([
82
+ vaultContract.read.totalAssets(),
83
+ Promise.all(accounts.map(async (account) => {
84
+ const share = await vaultContract.read.balanceOf([account]);
85
+ return share === BigInt(0) ? BigInt(0) : vaultContract.read.convertToAssets([share]);
86
+ })),
87
+ ]);
88
+
89
+ return { totalAssets, suppliedAssets };
90
+ };
91
+
92
+ const getMorphoVaultV1ChainData = async (provider: Client, morphoVault: MorphoVault, accounts: EthAddress[]) => {
67
93
  const morphoVaultContract = getMorphoVaultContractViem(provider, morphoVault.address);
68
94
 
69
95
  const shares: Record<EthAddress, bigint> = {};
@@ -84,20 +110,46 @@ const getMorphoVaultChainData = async (provider: Client, morphoVault: MorphoVaul
84
110
  };
85
111
  };
86
112
 
113
+ type MorphoVaultChainData = {
114
+ v1?: Awaited<ReturnType<typeof getMorphoVaultV1ChainData>>,
115
+ v2?: Awaited<ReturnType<typeof getMorphoVaultV2ChainData>>,
116
+ };
117
+
118
+ const getMorphoVaultChainData = async (provider: Client, morphoVault: MorphoVault, accounts: EthAddress[]): Promise<MorphoVaultChainData> => (
119
+ morphoVault.version === MorphoVaultVersion.V2
120
+ ? { v2: await getMorphoVaultV2ChainData(provider, morphoVault, accounts) }
121
+ : { v1: await getMorphoVaultV1ChainData(provider, morphoVault, accounts) }
122
+ );
123
+
87
124
  const formatMorphoVaultData = (
88
125
  morphoVault: MorphoVault,
89
- chainData: Awaited<ReturnType<typeof getMorphoVaultChainData>>,
126
+ chainData: MorphoVaultChainData,
90
127
  liquidityUnderlying: string,
91
128
  accounts: EthAddress[],
92
129
  ): SavingsVaultData => {
130
+ const liquidity = assetAmountInEth(liquidityUnderlying, morphoVault.asset);
131
+ const supplied: Record<EthAddress, string> = {};
132
+
133
+ if (chainData.v2) {
134
+ const { totalAssets, suppliedAssets } = chainData.v2;
135
+ accounts.forEach((account, i) => {
136
+ supplied[account.toLowerCase() as EthAddress] = assetAmountInEth(suppliedAssets[i].toString(), morphoVault.asset);
137
+ });
138
+ return {
139
+ poolSize: assetAmountInEth(totalAssets.toString(), morphoVault.asset),
140
+ supplied,
141
+ liquidity,
142
+ asset: morphoVault.asset,
143
+ optionType: morphoVault.type,
144
+ };
145
+ }
146
+
93
147
  const {
94
148
  totalAssets, totalSupply, decimals, decimalsOffset, shares,
95
- } = chainData;
149
+ } = chainData.v1!;
96
150
 
97
151
  const poolSize = assetAmountInEth(totalAssets.toString(), morphoVault.asset);
98
- const liquidity = assetAmountInEth(liquidityUnderlying, morphoVault.asset);
99
152
 
100
- const supplied: Record<EthAddress, string> = {};
101
153
  accounts.forEach((account) => {
102
154
  const share = shares[account] || BigInt(0);
103
155
  supplied[account.toLowerCase() as EthAddress] = new Dec(new Dec(share.toString()).mul(new Dec(totalAssets.toString()).add(1)).div(new Dec(totalSupply.toString()).add(10 ** decimalsOffset)).div(10 ** 18)