@defisaver/positions-sdk 2.1.161-portfolio-api-1-dev → 2.1.161-portfolio-api-3-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 (193) hide show
  1. package/cjs/aaveV2/index.js +2 -0
  2. package/cjs/aaveV3/index.d.ts +41 -12
  3. package/cjs/aaveV3/index.js +237 -2
  4. package/cjs/aaveV3/sgho.d.ts +2 -16
  5. package/cjs/aaveV4/lend.d.ts +2 -43
  6. package/cjs/claiming/aaveV3.d.ts +2 -2
  7. package/cjs/claiming/ethena.d.ts +2 -1
  8. package/cjs/claiming/king.d.ts +2 -1
  9. package/cjs/claiming/spark.d.ts +2 -2
  10. package/cjs/curveUsd/index.d.ts +5 -2
  11. package/cjs/curveUsd/index.js +30 -15
  12. package/cjs/fluid/index.d.ts +10 -49
  13. package/cjs/fluid/index.js +36 -1
  14. package/cjs/helpers/aaveHelpers/index.d.ts +9 -1
  15. package/cjs/helpers/aaveHelpers/index.js +26 -6
  16. package/cjs/helpers/aaveV4Helpers/index.js +1 -0
  17. package/cjs/helpers/compoundHelpers/index.js +2 -0
  18. package/cjs/helpers/curveUsdHelpers/index.d.ts +3 -1
  19. package/cjs/helpers/curveUsdHelpers/index.js +15 -2
  20. package/cjs/helpers/fluidHelpers/index.js +1 -0
  21. package/cjs/helpers/liquityV2Helpers/index.js +1 -0
  22. package/cjs/helpers/llamaLendHelpers/index.js +1 -0
  23. package/cjs/helpers/makerHelpers/index.d.ts +1 -1
  24. package/cjs/helpers/makerHelpers/index.js +2 -2
  25. package/cjs/helpers/morphoBlueHelpers/index.js +2 -0
  26. package/cjs/helpers/morphoMidnightHelpers/index.js +2 -0
  27. package/cjs/helpers/sparkHelpers/index.js +1 -0
  28. package/cjs/liquity/index.d.ts +2 -12
  29. package/cjs/liquity/index.js +15 -2
  30. package/cjs/liquityV2/index.d.ts +8 -23
  31. package/cjs/liquityV2/index.js +37 -19
  32. package/cjs/maker/index.d.ts +7 -2
  33. package/cjs/maker/index.js +44 -12
  34. package/cjs/markets/index.d.ts +1 -0
  35. package/cjs/markets/index.js +3 -1
  36. package/cjs/markets/maker/index.d.ts +1 -0
  37. package/cjs/markets/maker/index.js +13 -0
  38. package/cjs/moneymarket/moneymarketCommonService.d.ts +9 -0
  39. package/cjs/moneymarket/moneymarketCommonService.js +13 -1
  40. package/cjs/portfolio/index.d.ts +17 -5
  41. package/cjs/portfolio/index.js +583 -381
  42. package/cjs/portfolio/lendingProtocols.d.ts +184 -0
  43. package/cjs/portfolio/lendingProtocols.js +196 -0
  44. package/cjs/savings/index.d.ts +8 -2
  45. package/cjs/savings/index.js +37 -5
  46. package/cjs/savings/morphoVaults/index.d.ts +1 -1
  47. package/cjs/savings/morphoVaults/index.js +56 -18
  48. package/cjs/savings/morphoVaults/options.d.ts +2 -0
  49. package/cjs/savings/morphoVaults/options.js +61 -1
  50. package/cjs/spark/index.js +1 -0
  51. package/cjs/staking/staking.d.ts +20 -0
  52. package/cjs/staking/staking.js +19 -1
  53. package/cjs/types/aave.d.ts +86 -0
  54. package/cjs/types/aaveV4.d.ts +45 -0
  55. package/cjs/types/compound.d.ts +2 -0
  56. package/cjs/types/curveUsd.d.ts +6 -0
  57. package/cjs/types/fluid.d.ts +14 -0
  58. package/cjs/types/index.d.ts +1 -0
  59. package/cjs/types/index.js +1 -0
  60. package/cjs/types/liquity.d.ts +16 -0
  61. package/cjs/types/liquityV2.d.ts +26 -0
  62. package/cjs/types/maker.d.ts +4 -0
  63. package/cjs/types/morphoBlue.d.ts +2 -0
  64. package/cjs/types/morphoMidnight.d.ts +1 -0
  65. package/cjs/types/portfolio.d.ts +127 -6
  66. package/cjs/types/savings/index.d.ts +9 -1
  67. package/cjs/types/savings/morphoVaults.d.ts +10 -2
  68. package/cjs/types/savings/morphoVaults.js +9 -1
  69. package/cjs/types/spark.d.ts +2 -0
  70. package/cjs/umbrella/index.d.ts +2 -3
  71. package/esm/aaveV2/index.js +2 -0
  72. package/esm/aaveV3/index.d.ts +41 -12
  73. package/esm/aaveV3/index.js +231 -2
  74. package/esm/aaveV3/sgho.d.ts +2 -16
  75. package/esm/aaveV4/lend.d.ts +2 -43
  76. package/esm/claiming/aaveV3.d.ts +2 -2
  77. package/esm/claiming/aaveV3.js +1 -1
  78. package/esm/claiming/ethena.d.ts +2 -1
  79. package/esm/claiming/king.d.ts +2 -1
  80. package/esm/claiming/spark.d.ts +2 -2
  81. package/esm/claiming/spark.js +1 -1
  82. package/esm/curveUsd/index.d.ts +5 -2
  83. package/esm/curveUsd/index.js +30 -15
  84. package/esm/fluid/index.d.ts +10 -49
  85. package/esm/fluid/index.js +33 -0
  86. package/esm/helpers/aaveHelpers/index.d.ts +9 -1
  87. package/esm/helpers/aaveHelpers/index.js +23 -5
  88. package/esm/helpers/aaveV4Helpers/index.js +1 -0
  89. package/esm/helpers/compoundHelpers/index.js +2 -0
  90. package/esm/helpers/curveUsdHelpers/index.d.ts +3 -1
  91. package/esm/helpers/curveUsdHelpers/index.js +16 -3
  92. package/esm/helpers/fluidHelpers/index.js +1 -0
  93. package/esm/helpers/liquityV2Helpers/index.js +1 -0
  94. package/esm/helpers/llamaLendHelpers/index.js +2 -1
  95. package/esm/helpers/makerHelpers/index.d.ts +1 -1
  96. package/esm/helpers/makerHelpers/index.js +2 -2
  97. package/esm/helpers/morphoBlueHelpers/index.js +2 -0
  98. package/esm/helpers/morphoMidnightHelpers/index.js +2 -0
  99. package/esm/helpers/sparkHelpers/index.js +1 -0
  100. package/esm/liquity/index.d.ts +2 -12
  101. package/esm/liquity/index.js +16 -3
  102. package/esm/liquityV2/index.d.ts +8 -23
  103. package/esm/liquityV2/index.js +35 -18
  104. package/esm/maker/index.d.ts +7 -2
  105. package/esm/maker/index.js +44 -14
  106. package/esm/markets/index.d.ts +1 -0
  107. package/esm/markets/index.js +1 -0
  108. package/esm/markets/maker/index.d.ts +1 -0
  109. package/esm/markets/maker/index.js +10 -0
  110. package/esm/moneymarket/moneymarketCommonService.d.ts +9 -0
  111. package/esm/moneymarket/moneymarketCommonService.js +11 -0
  112. package/esm/portfolio/index.d.ts +17 -5
  113. package/esm/portfolio/index.js +584 -385
  114. package/esm/portfolio/lendingProtocols.d.ts +184 -0
  115. package/esm/portfolio/lendingProtocols.js +189 -0
  116. package/esm/savings/index.d.ts +8 -2
  117. package/esm/savings/index.js +35 -4
  118. package/esm/savings/morphoVaults/index.d.ts +1 -1
  119. package/esm/savings/morphoVaults/index.js +57 -19
  120. package/esm/savings/morphoVaults/options.d.ts +2 -0
  121. package/esm/savings/morphoVaults/options.js +61 -1
  122. package/esm/spark/index.js +1 -0
  123. package/esm/staking/staking.d.ts +20 -0
  124. package/esm/staking/staking.js +17 -0
  125. package/esm/types/aave.d.ts +86 -0
  126. package/esm/types/aaveV4.d.ts +45 -0
  127. package/esm/types/compound.d.ts +2 -0
  128. package/esm/types/curveUsd.d.ts +6 -0
  129. package/esm/types/fluid.d.ts +14 -0
  130. package/esm/types/index.d.ts +1 -0
  131. package/esm/types/index.js +1 -0
  132. package/esm/types/liquity.d.ts +16 -0
  133. package/esm/types/liquityV2.d.ts +26 -0
  134. package/esm/types/maker.d.ts +4 -0
  135. package/esm/types/morphoBlue.d.ts +2 -0
  136. package/esm/types/morphoMidnight.d.ts +1 -0
  137. package/esm/types/portfolio.d.ts +127 -6
  138. package/esm/types/savings/index.d.ts +9 -1
  139. package/esm/types/savings/morphoVaults.d.ts +10 -2
  140. package/esm/types/savings/morphoVaults.js +8 -0
  141. package/esm/types/spark.d.ts +2 -0
  142. package/esm/umbrella/index.d.ts +2 -3
  143. package/package.json +3 -2
  144. package/src/aaveV2/index.ts +2 -0
  145. package/src/aaveV3/index.ts +293 -2
  146. package/src/aaveV3/sgho.ts +2 -17
  147. package/src/aaveV4/lend.ts +2 -48
  148. package/src/claiming/aaveV3.ts +4 -2
  149. package/src/claiming/ethena.ts +5 -5
  150. package/src/claiming/king.ts +3 -3
  151. package/src/claiming/spark.ts +6 -4
  152. package/src/curveUsd/index.ts +33 -16
  153. package/src/fluid/index.ts +44 -3
  154. package/src/helpers/aaveHelpers/index.ts +27 -7
  155. package/src/helpers/aaveV4Helpers/index.ts +1 -0
  156. package/src/helpers/compoundHelpers/index.ts +2 -0
  157. package/src/helpers/curveUsdHelpers/index.ts +23 -3
  158. package/src/helpers/fluidHelpers/index.ts +1 -0
  159. package/src/helpers/liquityV2Helpers/index.ts +1 -0
  160. package/src/helpers/llamaLendHelpers/index.ts +2 -1
  161. package/src/helpers/makerHelpers/index.ts +2 -1
  162. package/src/helpers/morphoBlueHelpers/index.ts +2 -0
  163. package/src/helpers/morphoMidnightHelpers/index.ts +2 -0
  164. package/src/helpers/sparkHelpers/index.ts +1 -0
  165. package/src/liquity/index.ts +19 -5
  166. package/src/liquityV2/index.ts +48 -22
  167. package/src/maker/index.ts +77 -30
  168. package/src/markets/index.ts +2 -1
  169. package/src/markets/maker/index.ts +10 -0
  170. package/src/moneymarket/moneymarketCommonService.ts +15 -0
  171. package/src/portfolio/index.ts +477 -248
  172. package/src/portfolio/lendingProtocols.ts +281 -0
  173. package/src/savings/index.ts +45 -5
  174. package/src/savings/morphoVaults/index.ts +73 -21
  175. package/src/savings/morphoVaults/options.ts +67 -1
  176. package/src/spark/index.ts +1 -0
  177. package/src/staking/staking.ts +28 -0
  178. package/src/types/aave.ts +90 -0
  179. package/src/types/aaveV4.ts +50 -0
  180. package/src/types/compound.ts +2 -0
  181. package/src/types/curveUsd.ts +7 -0
  182. package/src/types/fluid.ts +15 -0
  183. package/src/types/index.ts +2 -1
  184. package/src/types/liquity.ts +19 -1
  185. package/src/types/liquityV2.ts +28 -1
  186. package/src/types/maker.ts +6 -0
  187. package/src/types/morphoBlue.ts +2 -0
  188. package/src/types/morphoMidnight.ts +1 -0
  189. package/src/types/portfolio.ts +153 -7
  190. package/src/types/savings/index.ts +12 -1
  191. package/src/types/savings/morphoVaults.ts +11 -1
  192. package/src/types/spark.ts +2 -0
  193. package/src/umbrella/index.ts +2 -1
@@ -7,138 +7,160 @@ import {
7
7
  CrvUsdMarkets,
8
8
  LiquityV2Markets,
9
9
  LlamaLendMarkets,
10
+ MakerActiveIlks,
10
11
  MorphoBlueMarkets,
11
- MorphoMidnightMarkets,
12
12
  SparkMarkets,
13
13
  } from '../markets';
14
14
  import { _getMorphoBlueAccountData, _getMorphoBluePortfolioMarketData, getMorphoEarn } from '../morphoBlue';
15
- import { _getMorphoMidnightAccountData, _getMorphoMidnightMarketData } from '../morphoMidnight';
16
15
  import {
17
- AaveV2MarketData,
18
- AaveV3MarketData,
19
- AaveV4SpokeData,
20
16
  AaveVersions,
21
17
  CdpInfo,
22
- CompoundV2MarketsData,
23
- CompoundV3MarketsData,
24
18
  CompoundVersions,
25
- CrvUSDGlobalMarketData,
26
19
  LiquityV2MarketData,
27
- LlamaLendGlobalMarketData,
28
- MorphoBlueMarketInfo,
29
- MorphoMidnightMarketInfo,
20
+ LiquityV2Versions,
21
+ PortfolioData,
22
+ PortfolioDataOptions,
23
+ PortfolioLiquityV2Troves,
30
24
  PortfolioMarketsData,
25
+ PortfolioMarketsErrors,
26
+ PortfolioMarketsResult,
31
27
  PortfolioPositionsData,
32
- SparkMarketsData,
28
+ PortfolioPositionsDataForAddress,
29
+ PortfolioRewardsData,
30
+ PortfolioStakingPositionsData,
31
+ PortfolioUserData,
32
+ ShifterPortfolioMarketsData,
33
33
  } from '../types';
34
34
  import { _getCompoundV3AccountData, _getCompoundV3MarketsData } from '../compoundV3';
35
35
  import { _getSparkAccountData, _getSparkMarketsData } from '../spark';
36
36
  import { _getCurveUsdGlobalData, _getCurveUsdUserData } from '../curveUsd';
37
37
  import { _getLlamaLendGlobalData, _getLlamaLendUserData } from '../llamaLend';
38
38
  import { _getAaveV3AccountData, _getAaveV3MarketData, getStakeAaveData } from '../aaveV3';
39
+ import { _getAaveV4AccountData, _getAaveV4SpokeData } from '../aaveV4';
39
40
  import { ZERO_ADDRESS } from '../constants';
40
- import { _getMakerCdpData, _getUserCdps } from '../maker';
41
+ import { _getMakerCdpData, _getMakerIlksData, _getUserCdps } from '../maker';
41
42
  import { _getAaveV2AccountData, _getAaveV2MarketsData } from '../aaveV2';
42
43
  import { _getCompoundV2AccountData, _getCompoundV2MarketsData } from '../compoundV2';
43
44
  import { getViemProvider } from '../services/viem';
44
45
  import { _getLiquityTroveInfo, getLiquityStakingData } from '../liquity';
45
- import { _getLiquityV2MarketData, getLiquitySAndYBold, getLiquityV2Staking } from '../liquityV2';
46
- import { _getAllUserEarnPositionsWithFTokens, _getUserPositionsPortfolio } from '../fluid';
46
+ import {
47
+ _getLiquityV2MarketData, _getLiquityV2TroveData, _getLiquityV2UsersTroveIds, getLiquitySAndYBold, getLiquityV2Staking,
48
+ } from '../liquityV2';
49
+ import { _getAllFluidMarketDataPortfolio, _getAllUserEarnPositionsWithFTokens, _getUserPositionsPortfolio } from '../fluid';
47
50
  import { getUmbrellaData } from '../umbrella';
48
51
  import { getMerklUnclaimedRewards, getUnclaimedRewardsForAllMarkets } from '../claiming/aaveV3';
49
52
  import { fetchSparkAirdropRewards, fetchSparkRewards } from '../claiming/spark';
50
53
  import { getKingRewards } from '../claiming/king';
51
54
  import { fetchEthenaAirdropRewards } from '../claiming/ethena';
52
- import { _getAaveV4AccountData, _getAaveV4SpokeData } from '../aaveV4';
53
55
  import { getUniswapRewards } from '../claiming/uniswap';
56
+ import {
57
+ aaveV3Lending, LENDING_PROTOCOLS, LendingKey, morphoBlueLending, requireMarket, sparkLending,
58
+ } from './lendingProtocols';
54
59
 
55
- export async function getPortfolioData(provider: EthereumProvider, network: NetworkNumber, defaultProvider: EthereumProvider, addresses: EthAddress[], isSim = false): Promise<{
56
- positions: PortfolioPositionsData;
57
- stakingPositions: any;
58
- rewardsData: any;
59
- markets: PortfolioMarketsData;
60
- }> {
61
- const isMainnet = network === NetworkNumber.Eth;
62
- const isFluidSupported = [NetworkNumber.Eth, NetworkNumber.Arb, NetworkNumber.Base, NetworkNumber.Plasma].includes(network);
63
-
64
- const morphoMarkets = Object.values(MorphoBlueMarkets(network)).filter((market) => market.chainIds.includes(network));
65
- const morphoMidnightMarkets = Object.values(MorphoMidnightMarkets(network)).filter((market) => market.chainIds.includes(network));
66
- const compoundV3Markets = Object.values(CompoundMarkets(network)).filter((market) => market.chainIds.includes(network) && market.value !== CompoundVersions.CompoundV2);
67
- const sparkMarkets = Object.values(SparkMarkets(network)).filter((market) => market.chainIds.includes(network));
68
- const aaveV3Markets = [AaveVersions.AaveV3, AaveVersions.AaveV3Lido, AaveVersions.AaveV3Etherfi].map((version) => AaveMarkets(network)[version]).filter((market) => market.chainIds.includes(network));
69
- const aaveV2Markets = [AaveVersions.AaveV2].map((version) => AaveMarkets(network)[version]).filter((market) => market.chainIds.includes(network));
70
- const compoundV2Markets = [CompoundVersions.CompoundV2].map((version) => CompoundMarkets(network)[version]).filter((market) => market.chainIds.includes(network));
71
- const crvUsdMarkets = Object.values(CrvUsdMarkets(network)).filter((market) => market.chainIds.includes(network));
72
- const llamaLendMarkets = [NetworkNumber.Eth, NetworkNumber.Arb].includes(network) ? Object.values(LlamaLendMarkets(network)).filter((market) => market.chainIds.includes(network)) : [];
73
- const liquityV2Markets = [NetworkNumber.Eth].includes(network) ? Object.values(LiquityV2Markets(network)) : [];
74
- const liquityV2MarketsStaking = [NetworkNumber.Eth].includes(network) ? Object.values(LiquityV2Markets(network)).filter(market => !market.isLegacy) : [];
75
- const aaveV4Spokes = Object.values(AaveV4Spokes(network)).filter((market) => market.chainIds.includes(network));
60
+ type PortfolioClient = ReturnType<typeof getViemProvider>;
76
61
 
62
+ /** The markets of the protocols read by their own code below; LENDING_PROTOCOLS has the others'. */
63
+ const getPortfolioMarkets = (network: NetworkNumber) => ({
64
+ liquityV2Markets: [NetworkNumber.Eth].includes(network) ? Object.values(LiquityV2Markets(network)) : [],
65
+ liquityV2MarketsStaking: [NetworkNumber.Eth].includes(network) ? Object.values(LiquityV2Markets(network)).filter(market => !market.isLegacy) : [],
66
+ });
77
67
 
68
+ const getPortfolioClients = (provider: EthereumProvider, defaultProvider: EthereumProvider, network: NetworkNumber, isSim: boolean) => {
78
69
  // batchSize is viem's cap on a batch's raw subcall calldata (bytes); the JSON-RPC body ends up
79
70
  // ~4x larger (hex + aggregate3 ABI + JSON overhead) and RPC providers reject bodies over
80
71
  // ~2.5MB with HTTP 413, so keep this small enough that no single body gets near that.
81
72
  // 250k gives the largest body of around 1.26MB - if we bump, we can save maybe 2-3 rpc calls but scaling takes a hit
82
73
  // (e.g. adding new markets may result in body size going over alchemy body size limit)
83
74
  const args: [NetworkNumber, any?] = [network, { batch: { multicall: { batchSize: isSim ? 2_000 : 250_000 } } }];
84
- const client = getViemProvider(provider, ...args);
85
- const defaultClient = getViemProvider(defaultProvider, ...args);
75
+ return {
76
+ client: getViemProvider(provider, ...args),
77
+ defaultClient: getViemProvider(defaultProvider, ...args),
78
+ };
79
+ };
86
80
 
87
- const morphoMarketsData: Record<string, MorphoBlueMarketInfo> = {};
88
- const morphoMidnightMarketsData: Record<string, MorphoMidnightMarketInfo> = {};
89
- const compoundV3MarketsData: Record<string, CompoundV3MarketsData> = {};
90
- const sparkMarketsData: Record<string, SparkMarketsData> = {};
91
- const aaveV3MarketsData: Record<string, AaveV3MarketData> = {};
92
- const makerCdps: Record<string, CdpInfo[]> = {};
93
- const aaveV2MarketsData: Record<string, AaveV2MarketData> = {};
94
- const compoundV2MarketsData: Record<string, CompoundV2MarketsData> = {};
95
- const crvUsdMarketsData: Record<string, CrvUSDGlobalMarketData> = {};
96
- const llamaLendMarketsData: Record<string, LlamaLendGlobalMarketData> = {};
97
- const liquityV2MarketsData: Record<string, LiquityV2MarketData> = {};
98
- const aaveV4SpokesData: Record<string, AaveV4SpokeData> = {};
81
+ const _getPortfolioMarketsData = async (client: PortfolioClient, defaultClient: PortfolioClient, network: NetworkNumber): Promise<PortfolioMarketsResult> => {
82
+ const { liquityV2Markets } = getPortfolioMarkets(network);
99
83
 
100
84
  const markets: PortfolioMarketsData = {
101
- morphoMarketsData,
102
- morphoMidnightMarketsData,
103
- compoundV3MarketsData,
104
- sparkMarketsData,
105
- aaveV3MarketsData,
106
- aaveV2MarketsData,
107
- compoundV2MarketsData,
108
- crvUsdMarketsData,
109
- llamaLendMarketsData,
110
- liquityV2MarketsData,
111
- aaveV4SpokesData,
85
+ morphoMarketsData: {},
86
+ morphoMidnightMarketsData: {},
87
+ compoundV3MarketsData: {},
88
+ sparkMarketsData: {},
89
+ aaveV3MarketsData: {},
90
+ aaveV2MarketsData: {},
91
+ compoundV2MarketsData: {},
92
+ crvUsdMarketsData: {},
93
+ llamaLendMarketsData: {},
94
+ liquityV2MarketsData: {},
95
+ aaveV4SpokesData: {},
112
96
  };
97
+ const liquityV2Failed: Record<string, string> = {};
98
+
99
+ const [lendingFailed] = await Promise.all([
100
+ Promise.all(LENDING_PROTOCOLS.map(async (protocol) => [protocol.marketsKey, await protocol.readMarkets({ client, defaultClient, network }, markets)] as const)),
101
+ // Liquity V2's markets: its troves are read by their own code below.
102
+ ...liquityV2Markets.map(async (market) => {
103
+ try {
104
+ markets.liquityV2MarketsData[market.value] = await _getLiquityV2MarketData(client, network, market);
105
+ } catch (error) {
106
+ console.error(`Error fetching liquityV2MarketsData for market ${market.value}:`, error);
107
+ liquityV2Failed[market.value] = `Error fetching liquityV2MarketsData for market ${market.value}`;
108
+ }
109
+ }),
110
+ ]);
111
+
112
+ // A market that failed is left out of markets and named here, under its protocol's key.
113
+ const errors: PortfolioMarketsErrors = {};
114
+ for (const [key, failed] of [...lendingFailed, ['liquityV2MarketsData', liquityV2Failed] as const]) {
115
+ if (Object.keys(failed).length) errors[key] = failed;
116
+ }
117
+ return { markets, errors };
118
+ };
119
+
120
+ const _getPortfolioUserData = async (
121
+ client: PortfolioClient,
122
+ network: NetworkNumber,
123
+ addresses: EthAddress[],
124
+ marketsData: Promise<PortfolioMarketsData>,
125
+ isSim: boolean,
126
+ options: PortfolioDataOptions,
127
+ ): Promise<PortfolioUserData> => {
128
+ const isMainnet = network === NetworkNumber.Eth;
129
+ const includePositions = options.positions !== false;
130
+ const includeStaking = options.staking !== false;
131
+ const includeRewards = options.rewards !== false;
132
+ const includeLiquityV2 = includePositions && options.liquityV2 === true;
133
+ // The addresses positions, staking, rewards and Liquity V2 troves are read for: none when left out, so their fetches
134
+ // below make no calls.
135
+ const positionsAddresses = includePositions ? addresses : [];
136
+ const stakingAddresses = includeStaking ? addresses : [];
137
+ const rewardsAddresses = includeRewards ? addresses : [];
138
+ const liquityV2Addresses = includeLiquityV2 ? addresses : [];
139
+ const isFluidSupported = [NetworkNumber.Eth, NetworkNumber.Arb, NetworkNumber.Base, NetworkNumber.Plasma].includes(network);
140
+
141
+ const { liquityV2Markets, liquityV2MarketsStaking } = getPortfolioMarkets(network);
113
142
 
114
143
  const positions: PortfolioPositionsData = {};
115
- const stakingPositions: any = {};
116
- const rewardsData: any = {};
117
- const allAddresses = [...addresses];
144
+ const stakingPositions: PortfolioStakingPositionsData = {};
145
+ const rewardsData: PortfolioRewardsData = {};
146
+ const makerCdpsErrors: Record<EthAddress, string> = {};
118
147
 
119
- for (const address of allAddresses) {
148
+ for (const address of positionsAddresses) {
120
149
  positions[address.toLowerCase() as EthAddress] = {
121
- aaveV3: {},
122
- aaveV4: {},
123
- morphoBlue: {},
124
- morphoMidnight: {},
125
- compoundV3: {},
126
- spark: {},
150
+ // Every registry protocol, keyed by market as its reads fill it in.
151
+ ...Object.fromEntries(LENDING_PROTOCOLS.map((protocol) => [protocol.key, {}])) as Pick<PortfolioPositionsDataForAddress, LendingKey>,
127
152
  maker: {},
128
- aaveV2: {},
129
- compoundV2: {},
130
153
  liquity: {},
131
- crvUsd: {},
132
- llamaLend: {},
133
154
  fluid: {
134
155
  error: '',
135
156
  data: {},
136
157
  },
158
+ ...(includeLiquityV2 ? { liquityV2: {} } : {}),
137
159
  };
138
160
  }
139
161
 
140
162
  // TODO: check default values, probably needed when fetching portfolio on unsupported networks
141
- for (const address of addresses) {
163
+ for (const address of stakingAddresses) {
142
164
  stakingPositions[address.toLowerCase() as EthAddress] = {
143
165
  aaveV3: {},
144
166
  morphoBlue: {},
@@ -152,72 +174,52 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
152
174
  error: '',
153
175
  data: {},
154
176
  },
177
+ // Set by the sBOLD/yBOLD fetch below, whether it succeeds or fails.
178
+ liquityV2SBoldYBold: { error: '', data: null },
155
179
  };
180
+ }
156
181
 
182
+ for (const address of rewardsAddresses) {
183
+ // merkl, spk, king and uniswap are set by their fetches below, whether they succeed or fail.
157
184
  rewardsData[address.toLowerCase() as EthAddress] = {
158
- merkl: {},
185
+ merkl: { error: '', data: null },
159
186
  aaveV3: {},
160
187
  spark: {},
161
- spk: {},
162
- king: {},
188
+ spk: { error: '', data: null },
189
+ king: { error: '', data: null },
163
190
  ethena: {},
191
+ uniswap: { error: '', data: null },
164
192
  };
165
193
  }
166
194
 
167
- await Promise.allSettled([
168
- // === MARKET DATA (needs to be fetched first) ===
169
- ...morphoMarkets.map(async (market) => {
170
- const marketData = await _getMorphoBluePortfolioMarketData(client, network, market);
171
- morphoMarketsData[market.value] = marketData;
172
- }),
173
- ...morphoMidnightMarkets.map(async (market) => {
174
- const marketData = await _getMorphoMidnightMarketData(client, network, market);
175
- morphoMidnightMarketsData[market.value] = marketData;
176
- }),
177
- ...compoundV3Markets.map(async (market) => {
178
- const marketData = await _getCompoundV3MarketsData(client, network, market, defaultClient);
179
- compoundV3MarketsData[market.value] = marketData;
180
- }),
181
- ...sparkMarkets.map(async (market) => {
182
- const marketData = await _getSparkMarketsData(client, network, market);
183
- sparkMarketsData[market.value] = marketData;
184
- }),
185
- ...aaveV3Markets.map(async (market) => {
186
- const marketData = await _getAaveV3MarketData(client, network, market);
187
- aaveV3MarketsData[market.value] = marketData;
188
- }),
189
- ...aaveV4Spokes.map(async (spoke) => {
190
- const spokeData = await _getAaveV4SpokeData(client, network, spoke);
191
- aaveV4SpokesData[spoke.value] = spokeData;
192
- }),
193
- ...aaveV2Markets.map(async (market) => {
194
- const marketData = await _getAaveV2MarketsData(client, network, market);
195
- aaveV2MarketsData[market.value] = marketData;
196
- }),
197
- ...compoundV2Markets.map(async (market) => {
198
- const marketData = await _getCompoundV2MarketsData(client, network);
199
- compoundV2MarketsData[market.value] = marketData;
200
- }),
201
- ...crvUsdMarkets.map(async (market) => {
202
- const marketData = await _getCurveUsdGlobalData(client, network, market);
203
- crvUsdMarketsData[market.value] = marketData;
204
- }),
205
- ...llamaLendMarkets.map(async (market) => {
206
- const marketData = await _getLlamaLendGlobalData(client, network, market);
207
- llamaLendMarketsData[market.value] = marketData;
208
- }),
209
- ...liquityV2Markets.map(async (market) => {
210
- const marketData = await _getLiquityV2MarketData(client, network, market);
211
- liquityV2MarketsData[market.value] = marketData;
212
- }),
213
-
214
- // === INDEPENDENT USER DATA (doesn't depend on market data) ===
215
- ...addresses.map(async (address) => {
195
+ // Everything starts at once; a read that needs a market's data waits for marketsData.
196
+ await Promise.all([
197
+ // === POSITIONS THAT DO NOT NEED MARKET DATA ===
198
+ // Maker: the address's vault list, then each vault. A failed list is reported in errors.makerCdps: maker stays {},
199
+ // which would otherwise read as "no vaults".
200
+ ...positionsAddresses.map(async (address) => {
216
201
  if (!isMainnet) return; // Maker CDPs are only available on mainnet
217
- const makerCdp = await _getUserCdps(client, network, address);
218
- makerCdps[address.toLowerCase() as EthAddress] = makerCdp;
202
+ let cdps: CdpInfo[];
203
+ try {
204
+ cdps = await _getUserCdps(client, network, address);
205
+ } catch (error) {
206
+ console.error(`Error fetching Maker CDPs for address ${address}:`, error);
207
+ makerCdpsErrors[address.toLowerCase() as EthAddress] = `Error fetching Maker CDPs for address ${address}`;
208
+ return;
209
+ }
210
+ await Promise.all(cdps.map(async (cdpInfo) => {
211
+ try {
212
+ const cdpData = await _getMakerCdpData(client, network, cdpInfo);
213
+ if (cdpData) {
214
+ positions[address.toLowerCase() as EthAddress].maker[cdpInfo.id] = { error: '', data: cdpData };
215
+ }
216
+ } catch (error) {
217
+ console.error(`Error fetching Maker CDP data for address ${address} with ID ${cdpInfo.id}:`, error);
218
+ positions[address.toLowerCase() as EthAddress].maker[cdpInfo.id] = { error: `Error fetching Maker CDP data for address ${address} with ID ${cdpInfo.id}`, data: null };
219
+ }
220
+ }));
219
221
  }),
220
- ...addresses.map(async (address) => {
222
+ ...positionsAddresses.map(async (address) => {
221
223
  try {
222
224
  if (!isFluidSupported) return; // Fluid is not available on Optimism
223
225
  const userPositions = (await _getUserPositionsPortfolio(client, network, address));
@@ -234,71 +236,94 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
234
236
  };
235
237
  }
236
238
  }),
239
+ ...positionsAddresses.map(async (address) => {
240
+ try {
241
+ if (!isMainnet) return; // Liquity trove info is only available on mainnet
242
+ const troveInfo = await _getLiquityTroveInfo(client, network, address);
243
+ if (new Dec(troveInfo.collateral).gt(0)) positions[address.toLowerCase() as EthAddress].liquity = { error: '', data: troveInfo };
244
+ } catch (error) {
245
+ console.error(`Error fetching Liquity trove info for address ${address}:`, error);
246
+ positions[address.toLowerCase() as EthAddress].liquity = { error: `Error fetching Liquity trove info for address ${address}`, data: null };
247
+ }
248
+ }),
237
249
 
238
- // === STAKING DATA (independent of market data) ===
239
- ...addresses.map(async (address) => {
250
+ // === STAKING DATA (independent of market data; Morpho Earn is with the positions below) ===
251
+ ...stakingAddresses.map(async (address) => {
240
252
  try {
241
253
  if (!isFluidSupported) return;
242
- stakingPositions[address.toLowerCase()].fluid = await _getAllUserEarnPositionsWithFTokens(client, network, address);
254
+ stakingPositions[address.toLowerCase() as EthAddress].fluid = await _getAllUserEarnPositionsWithFTokens(client, network, address);
243
255
  } catch (error) {
244
256
  console.error(`Error fetching Fluid lend data for address ${address}:`, error);
245
- stakingPositions[address.toLowerCase()].fluid = { error: `Error fetching Fluid lend data for address ${address}`, data: null };
257
+ stakingPositions[address.toLowerCase() as EthAddress].fluid = { error: `Error fetching Fluid lend data for address ${address}`, data: null };
246
258
  }
247
259
  }),
248
- ...addresses.map(async (address) => {
260
+ ...stakingAddresses.map(async (address) => {
249
261
  try {
250
262
  if (!isMainnet) return;
251
- stakingPositions[address.toLowerCase()].liquity = await getLiquityStakingData(client, network, address);
263
+ stakingPositions[address.toLowerCase() as EthAddress].liquity = await getLiquityStakingData(client, network, address);
252
264
  } catch (error) {
253
265
  console.error(`Error fetching Liquity staking data for address ${address}:`, error);
254
- stakingPositions[address.toLowerCase()].liquity = { error: `Error fetching Liquity staking data for address ${address}`, data: null };
266
+ stakingPositions[address.toLowerCase() as EthAddress].liquity = { error: `Error fetching Liquity staking data for address ${address}`, data: null };
255
267
  }
256
268
  }),
257
- ...addresses.map(async (address) => {
269
+ ...stakingAddresses.map(async (address) => {
258
270
  try {
259
271
  if (!isMainnet) return;
260
- stakingPositions[address.toLowerCase()].aaveV3 = await getStakeAaveData(client, network, address);
272
+ stakingPositions[address.toLowerCase() as EthAddress].aaveV3 = await getStakeAaveData(client, network, address);
261
273
  } catch (error) {
262
274
  console.error(`Error fetching Aave V3 staking data for address ${address}:`, error);
263
- stakingPositions[address.toLowerCase()].aaveV3 = { error: `Error fetching Aave V3 staking data for address ${address}`, data: null };
275
+ stakingPositions[address.toLowerCase() as EthAddress].aaveV3 = { error: `Error fetching Aave V3 staking data for address ${address}`, data: null };
264
276
  }
265
277
  }),
266
- ...addresses.map(async (address) => {
278
+ ...stakingAddresses.map(async (address) => {
267
279
  try {
268
280
  if (!isMainnet) return;
269
- stakingPositions[address.toLowerCase()].umbrella = await getUmbrellaData(client, network, address);
281
+ stakingPositions[address.toLowerCase() as EthAddress].umbrella = await getUmbrellaData(client, network, address);
270
282
  } catch (error) {
271
283
  console.error(`Error fetching Umbrella staking data for address ${address}:`, error);
272
- stakingPositions[address.toLowerCase()].umbrella = { error: `Error fetching Umbrella staking data for address ${address}`, data: null };
284
+ stakingPositions[address.toLowerCase() as EthAddress].umbrella = { error: `Error fetching Umbrella staking data for address ${address}`, data: null };
273
285
  }
274
286
  }),
275
- // Liquity V2 staking
276
- ...liquityV2MarketsStaking.map(market => addresses.map(async (address) => {
287
+ // Liquity V2 stability pools, then sBOLD/yBOLD, which reads them.
288
+ ...stakingAddresses.map(async (address) => {
289
+ await Promise.all(liquityV2MarketsStaking.map(async (market) => {
290
+ try {
291
+ if (!isMainnet) {
292
+ stakingPositions[address.toLowerCase() as EthAddress].liquityV2[market.value] = { error: '', data: null };
293
+ return;
294
+ }
295
+ const liquityV2StakingData = await getLiquityV2Staking(client, network, market.value, address);
296
+ stakingPositions[address.toLowerCase() as EthAddress].liquityV2[market.value] = { error: '', data: liquityV2StakingData };
297
+ } catch (error) {
298
+ console.error(`Error fetching Liquity V2 staking data for address ${address}, market ${market.value}:`, error);
299
+ stakingPositions[address.toLowerCase() as EthAddress].liquityV2[market.value] = { error: `Error fetching Liquity V2 staking data for address ${address}`, data: null };
300
+ }
301
+ }));
277
302
  try {
278
303
  if (!isMainnet) {
279
- stakingPositions[address.toLowerCase()].liquityV2[market.value] = { error: '', data: null };
304
+ stakingPositions[address.toLowerCase() as EthAddress].liquityV2SBoldYBold = { error: '', data: null };
280
305
  return;
281
306
  }
282
- const liquityV2StakingData = await getLiquityV2Staking(client, network, market.value, address);
283
- stakingPositions[address.toLowerCase()].liquityV2[market.value] = { error: '', data: liquityV2StakingData };
307
+ const data = await getLiquitySAndYBold(client, network, stakingPositions[address.toLowerCase() as EthAddress].liquityV2, address);
308
+ stakingPositions[address.toLowerCase() as EthAddress].liquityV2SBoldYBold = { error: '', data };
284
309
  } catch (error) {
285
- console.error(`Error fetching Liquity V2 staking data for address ${address}, market ${market.value}:`, error);
286
- stakingPositions[address.toLowerCase()].liquityV2[market.value] = { error: `Error fetching Liquity V2 staking data for address ${address}`, data: null };
310
+ console.error(`Error fetching SBold/YBold data for address ${address}:`, error);
311
+ stakingPositions[address.toLowerCase() as EthAddress].liquityV2SBoldYBold = { error: `Error fetching sBold/yBold data for address ${address}`, data: null };
287
312
  }
288
- })).flat(),
313
+ }),
289
314
 
290
315
  // === REWARDS DATA (independent of market data) ===
291
316
  // Batch King rewards
292
317
  (async () => {
293
318
  try {
294
319
  if (!isMainnet) {
295
- for (const address of addresses) {
296
- rewardsData[address.toLowerCase()].king = { error: '', data: [] };
320
+ for (const address of rewardsAddresses) {
321
+ rewardsData[address.toLowerCase() as EthAddress].king = { error: '', data: [] };
297
322
  }
298
323
  return;
299
324
  }
300
- const kingRewards = await getKingRewards(client, network, addresses);
301
- for (const address of addresses) {
325
+ const kingRewards = await getKingRewards(client, network, rewardsAddresses);
326
+ for (const address of rewardsAddresses) {
302
327
  const lowerAddress = address.toLowerCase() as EthAddress;
303
328
  rewardsData[lowerAddress].king = {
304
329
  error: '',
@@ -307,7 +332,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
307
332
  }
308
333
  } catch (error) {
309
334
  console.error('Error fetching King rewards data in batch:', error);
310
- for (const address of addresses) {
335
+ for (const address of rewardsAddresses) {
311
336
  rewardsData[address.toLowerCase() as EthAddress].king = {
312
337
  error: 'Error fetching King rewards data in batch',
313
338
  data: null,
@@ -319,13 +344,13 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
319
344
  (async () => {
320
345
  try {
321
346
  if (!isMainnet) {
322
- for (const address of addresses) {
323
- rewardsData[address.toLowerCase()].uniswap = { error: '', data: [] };
347
+ for (const address of rewardsAddresses) {
348
+ rewardsData[address.toLowerCase() as EthAddress].uniswap = { error: '', data: [] };
324
349
  }
325
350
  return;
326
351
  }
327
- const uniswapRewards = await getUniswapRewards(client, network, addresses);
328
- for (const address of addresses) {
352
+ const uniswapRewards = await getUniswapRewards(client, network, rewardsAddresses);
353
+ for (const address of rewardsAddresses) {
329
354
  const lowerAddress = address.toLowerCase() as EthAddress;
330
355
  rewardsData[lowerAddress].uniswap = {
331
356
  error: '',
@@ -334,7 +359,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
334
359
  }
335
360
  } catch (error) {
336
361
  console.error('Error fetching Uniswap rewards data in batch:', error);
337
- for (const address of addresses) {
362
+ for (const address of rewardsAddresses) {
338
363
  rewardsData[address.toLowerCase() as EthAddress].uniswap = {
339
364
  error: 'Error fetching Uniswap rewards data in batch',
340
365
  data: null,
@@ -342,10 +367,10 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
342
367
  }
343
368
  }
344
369
  })(),
345
- ...sparkMarkets.map((market) => addresses.map(async address => {
370
+ ...sparkLending.markets(network).map((market) => rewardsAddresses.map(async address => {
346
371
  try {
347
372
  if (!isMainnet) {
348
- rewardsData[address.toLowerCase()].spark[market.value] = { error: '', data: [] };
373
+ rewardsData[address.toLowerCase() as EthAddress].spark[market.value] = { error: '', data: [] };
349
374
  return;
350
375
  }
351
376
  const sparkData = await fetchSparkRewards(client, network, address, market.providerAddress);
@@ -355,7 +380,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
355
380
  rewardsData[address.toLowerCase() as EthAddress].spark[market.value] = { error: `Error fetching Spark rewards data for address ${address}`, data: null };
356
381
  }
357
382
  })).flat(),
358
- ...addresses.map(async (address) => {
383
+ ...rewardsAddresses.map(async (address) => {
359
384
  try {
360
385
  const merklData = await getMerklUnclaimedRewards(address, network);
361
386
  rewardsData[address.toLowerCase() as EthAddress].merkl = { error: '', data: merklData };
@@ -364,27 +389,28 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
364
389
  rewardsData[address.toLowerCase() as EthAddress].merkl = { error: `Error fetching Merkl rewards data for address ${address}`, data: null };
365
390
  }
366
391
  }),
367
- ...aaveV3Markets.map(market => addresses.map(async (address) => {
392
+ ...aaveV3Lending.markets(network).map(market => rewardsAddresses.map(async (address) => {
368
393
  try {
369
394
  const aaveData = await getUnclaimedRewardsForAllMarkets(client, network, address, market.providerAddress);
370
395
  rewardsData[address.toLowerCase() as EthAddress].aaveV3[market.value] = { error: '', data: aaveData };
371
396
  } catch (error) {
372
397
  console.error(`Error fetching Aave V3 Merit rewards data for address ${address}:`, error);
373
- rewardsData[address.toLowerCase() as EthAddress].aaveV3 = { error: `Error fetching Aave V3 rewards data for address ${address}`, data: null };
398
+ // This market only: the other markets keep their rewards.
399
+ rewardsData[address.toLowerCase() as EthAddress].aaveV3[market.value] = { error: `Error fetching Aave V3 rewards data for address ${address} on market ${market.value}`, data: null };
374
400
  }
375
401
  })).flat(),
376
402
  // Batch Spark Airdrop rewards
377
403
  (async () => {
378
404
  try {
379
405
  if (!isMainnet) {
380
- for (const address of addresses) {
381
- rewardsData[address.toLowerCase()].spk = { error: '', data: [] };
406
+ for (const address of rewardsAddresses) {
407
+ rewardsData[address.toLowerCase() as EthAddress].spk = { error: '', data: [] };
382
408
  }
383
409
  return;
384
410
  }
385
411
 
386
- const sparkAirdropRewards = await fetchSparkAirdropRewards(client, network, addresses);
387
- for (const address of addresses) {
412
+ const sparkAirdropRewards = await fetchSparkAirdropRewards(client, network, rewardsAddresses);
413
+ for (const address of rewardsAddresses) {
388
414
  const lowerAddress = address.toLowerCase() as EthAddress;
389
415
  rewardsData[lowerAddress].spk = {
390
416
  error: '',
@@ -393,7 +419,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
393
419
  }
394
420
  } catch (error) {
395
421
  console.error('Error fetching Spark Airdrop rewards data in batch:', error);
396
- for (const address of addresses) {
422
+ for (const address of rewardsAddresses) {
397
423
  rewardsData[address.toLowerCase() as EthAddress].spk = {
398
424
  error: 'Error fetching Spark Airdrop rewards data in batch',
399
425
  data: null,
@@ -407,8 +433,8 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
407
433
  return;
408
434
  }
409
435
 
410
- const ethenaAirdropRewards = await fetchEthenaAirdropRewards(addresses);
411
- for (const address of addresses) {
436
+ const ethenaAirdropRewards = await fetchEthenaAirdropRewards(rewardsAddresses);
437
+ for (const address of rewardsAddresses) {
412
438
  const lowerAddress = address.toLowerCase() as EthAddress;
413
439
  rewardsData[lowerAddress].ethena = {
414
440
  error: '',
@@ -417,7 +443,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
417
443
  }
418
444
  } catch (error) {
419
445
  console.error('Error fetching Ethena Airdrop rewards data:', error);
420
- for (const address of addresses) {
446
+ for (const address of rewardsAddresses) {
421
447
  rewardsData[address.toLowerCase() as EthAddress].ethena = {
422
448
  error: 'Error fetching Ethena Airdrop rewards data in batch',
423
449
  data: null,
@@ -425,21 +451,274 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
425
451
  }
426
452
  }
427
453
  })(),
454
+
455
+ // === POSITIONS ON MARKETS (each waits for its market's data) ===
456
+ ...LENDING_PROTOCOLS.flatMap((protocol) => protocol.readPositions({ client, network }, positionsAddresses, marketsData, positions)),
457
+ // Morpho Blue Earn: a supply without a borrow, kept as staking, read on the same markets as Morpho Blue's positions.
458
+ ...morphoBlueLending.markets(network).flatMap((market) => stakingAddresses.map(async (address) => {
459
+ try {
460
+ const marketData = requireMarket((await marketsData).morphoMarketsData, market.value);
461
+ const earnData = await getMorphoEarn(client, network, address, market, marketData);
462
+ if (earnData && new Dec(earnData.amount).gt(0)) {
463
+ stakingPositions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: '', data: earnData };
464
+ }
465
+ } catch (error) {
466
+ console.error(`Error fetching MorphoBlue earn data for address ${address} on market ${market.value}:`, error);
467
+ stakingPositions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: `Error fetching MorphoBlue earn data for address ${address} on market ${market.value}`, data: null };
468
+ }
469
+ })),
470
+ // Liquity V2 troves: one market's trove ids for every address at once, then each trove's data. A failure is the
471
+ // market's error; the troves that did load stay in its data.
472
+ ...(liquityV2Addresses.length ? liquityV2Markets : []).map(async (market) => {
473
+ const setEntry = (address: EthAddress, entry: PortfolioLiquityV2Troves) => {
474
+ positions[address.toLowerCase() as EthAddress].liquityV2![market.value] = entry;
475
+ };
476
+ const failAll = (error: string) => liquityV2Addresses.forEach((address) => setEntry(address, { error, data: {} }));
477
+
478
+ const allMarketsData = (await marketsData).liquityV2MarketsData;
479
+ const marketData = allMarketsData[market.value];
480
+ if (!marketData) {
481
+ failAll(`Error fetching Liquity V2 market data for market ${market.value}`);
482
+ return;
483
+ }
484
+ let troveIds: Record<EthAddress, string[]>;
485
+ try {
486
+ troveIds = await _getLiquityV2UsersTroveIds(client, network, market, marketData.marketData.troveNFTAddress, isSim, liquityV2Addresses);
487
+ } catch (error) {
488
+ console.error(`Error fetching Liquity V2 trove ids on market ${market.value}:`, error);
489
+ failAll(`Error fetching Liquity V2 troves on market ${market.value}`);
490
+ return;
491
+ }
492
+ await Promise.all(liquityV2Addresses.map(async (address) => {
493
+ const entry: PortfolioLiquityV2Troves = { error: '', data: {} };
494
+ const failed: string[] = [];
495
+ await Promise.all((troveIds[address] ?? []).map(async (troveId) => {
496
+ try {
497
+ entry.data[troveId] = await _getLiquityV2TroveData(client, network, {
498
+ selectedMarket: market,
499
+ assetsData: marketData.assetsData,
500
+ troveId,
501
+ allMarketsData: allMarketsData as Record<LiquityV2Versions, LiquityV2MarketData>,
502
+ });
503
+ } catch (error) {
504
+ console.error(`Error fetching Liquity V2 trove ${troveId} for address ${address} on market ${market.value}:`, error);
505
+ failed.push(troveId);
506
+ }
507
+ }));
508
+ if (failed.length) entry.error = `Error fetching Liquity V2 troves ${failed.join(', ')} for address ${address} on market ${market.value}`;
509
+ setEntry(address, entry);
510
+ }));
511
+ }),
512
+ ]);
513
+
514
+ return {
515
+ positions,
516
+ stakingPositions,
517
+ rewardsData,
518
+ errors: Object.keys(makerCdpsErrors).length ? { makerCdps: makerCdpsErrors } : {},
519
+ };
520
+ };
521
+
522
+ /**
523
+ * Every market the portfolio reads on a network — the markets half of getPortfolioData, for a caller that keeps
524
+ * markets apart from what it reads per address (a cache shared by many addresses). A market that fails is left out
525
+ * of `markets` and named in `errors`.
526
+ */
527
+ export async function getPortfolioMarketsData(provider: EthereumProvider, network: NetworkNumber, defaultProvider: EthereumProvider, isSim = false): Promise<PortfolioMarketsResult> {
528
+ const { client, defaultClient } = getPortfolioClients(provider, defaultProvider, network, isSim);
529
+ return _getPortfolioMarketsData(client, defaultClient, network);
530
+ }
531
+
532
+ /**
533
+ * What the portfolio reads per address — the other half of getPortfolioData — against `markets` from
534
+ * getPortfolioMarketsData. `markets` can be a promise: reads that do not need a market (Maker, Fluid, Liquity, staking,
535
+ * rewards) start at once, and each read on a market waits for it. A position on a market missing from `markets` is an
536
+ * entry with an error.
537
+ */
538
+ export async function getPortfolioUserData(
539
+ provider: EthereumProvider,
540
+ network: NetworkNumber,
541
+ addresses: EthAddress[],
542
+ markets: PortfolioMarketsData | Promise<PortfolioMarketsData>,
543
+ isSim = false,
544
+ options: PortfolioDataOptions = {},
545
+ ): Promise<PortfolioUserData> {
546
+ const { client } = getPortfolioClients(provider, provider, network, isSim);
547
+ return _getPortfolioUserData(client, network, addresses, Promise.resolve(markets), isSim, options);
548
+ }
549
+
550
+ export async function getPortfolioData(provider: EthereumProvider, network: NetworkNumber, defaultProvider: EthereumProvider, addresses: EthAddress[], isSim = false, options: PortfolioDataOptions = {}): Promise<PortfolioData> {
551
+ // One pair of clients for both halves, so their calls batch together.
552
+ const { client, defaultClient } = getPortfolioClients(provider, defaultProvider, network, isSim);
553
+ const marketsResult = _getPortfolioMarketsData(client, defaultClient, network);
554
+ const [{ markets, errors: marketsErrors }, userData] = await Promise.all([
555
+ marketsResult,
556
+ _getPortfolioUserData(client, network, addresses, marketsResult.then((result) => result.markets), isSim, options),
557
+ ]);
558
+
559
+ return {
560
+ positions: userData.positions,
561
+ stakingPositions: userData.stakingPositions,
562
+ rewardsData: userData.rewardsData,
563
+ markets,
564
+ errors: {
565
+ ...(Object.keys(marketsErrors).length ? { markets: marketsErrors } : {}),
566
+ ...userData.errors,
567
+ },
568
+ };
569
+ }
570
+
571
+
572
+ export async function getShifterPortfolioData(provider: EthereumProvider, network: NetworkNumber, defaultProvider: EthereumProvider, addresses: EthAddress[], isSim = false): Promise<{
573
+ positions: PortfolioPositionsData;
574
+ markets: ShifterPortfolioMarketsData;
575
+ }> {
576
+ const isMainnet = network === NetworkNumber.Eth;
577
+ const isFluidSupported = [NetworkNumber.Eth, NetworkNumber.Arb, NetworkNumber.Base, NetworkNumber.Plasma].includes(network);
578
+
579
+ const morphoMarkets = Object.values(MorphoBlueMarkets(network)).filter((market) => market.chainIds.includes(network));
580
+ const compoundV3Markets = Object.values(CompoundMarkets(network)).filter((market) => market.chainIds.includes(network) && market.value !== CompoundVersions.CompoundV2);
581
+ const sparkMarkets = Object.values(SparkMarkets(network)).filter((market) => market.chainIds.includes(network));
582
+ const aaveV3Markets = [AaveVersions.AaveV3, AaveVersions.AaveV3Lido, AaveVersions.AaveV3Etherfi].map((version) => AaveMarkets(network)[version]).filter((market) => market.chainIds.includes(network));
583
+ const aaveV2Markets = [AaveVersions.AaveV2].map((version) => AaveMarkets(network)[version]).filter((market) => market.chainIds.includes(network));
584
+ const compoundV2Markets = [CompoundVersions.CompoundV2].map((version) => CompoundMarkets(network)[version]).filter((market) => market.chainIds.includes(network));
585
+ const crvUsdMarkets = Object.values(CrvUsdMarkets(network)).filter((market) => market.chainIds.includes(network));
586
+ const llamaLendMarkets = [NetworkNumber.Eth, NetworkNumber.Arb].includes(network) ? Object.values(LlamaLendMarkets(network)).filter((market) => market.chainIds.includes(network)) : [];
587
+ const liquityV2Markets = [NetworkNumber.Eth].includes(network) ? Object.values(LiquityV2Markets(network)) : [];
588
+ const aaveV4Spokes = Object.values(AaveV4Spokes(network)).filter((market) => market.chainIds.includes(network));
589
+
590
+ const args: [NetworkNumber, any?] = [network, { batch: { multicall: { batchSize: isSim ? 2_000 : 2_500_000 } } }];
591
+ const client = getViemProvider(provider, ...args);
592
+ const defaultClient = getViemProvider(defaultProvider, ...args);
593
+
594
+ const markets: ShifterPortfolioMarketsData = {
595
+ morphoMarketsData: {},
596
+ morphoMidnightMarketsData: {}, // Morpho Midnight is not fetched for the shifter; key kept so the payload shape is stable
597
+ compoundV3MarketsData: {},
598
+ sparkMarketsData: {},
599
+ aaveV3MarketsData: {},
600
+ aaveV2MarketsData: {},
601
+ compoundV2MarketsData: {},
602
+ crvUsdMarketsData: {},
603
+ llamaLendMarketsData: {},
604
+ liquityV2MarketsData: {},
605
+ aaveV4SpokesData: {},
606
+ fluidMarketsData: {},
607
+ makerMarketsData: {},
608
+ };
609
+ const makerCdps: Record<string, CdpInfo[]> = {};
610
+
611
+ const positions: PortfolioPositionsData = {};
612
+
613
+ for (const address of addresses) {
614
+ positions[address.toLowerCase() as EthAddress] = {
615
+ aaveV3: {},
616
+ aaveV4: {},
617
+ morphoBlue: {},
618
+ morphoMidnight: {}, // not fetched for the shifter (see markets above)
619
+ compoundV3: {},
620
+ spark: {},
621
+ maker: {},
622
+ aaveV2: {},
623
+ compoundV2: {},
624
+ liquity: {},
625
+ crvUsd: {},
626
+ llamaLend: {},
627
+ fluid: {
628
+ error: '',
629
+ data: {},
630
+ },
631
+ };
632
+ }
633
+
634
+ await Promise.allSettled([
635
+ // === MARKET DATA (needs to be fetched first) ===
636
+ ...morphoMarkets.map(async (market) => {
637
+ markets.morphoMarketsData[market.value] = await _getMorphoBluePortfolioMarketData(client, network, market);
638
+ }),
639
+ ...compoundV3Markets.map(async (market) => {
640
+ markets.compoundV3MarketsData[market.value] = await _getCompoundV3MarketsData(client, network, market, defaultClient);
641
+ }),
642
+ ...sparkMarkets.map(async (market) => {
643
+ markets.sparkMarketsData[market.value] = await _getSparkMarketsData(client, network, market);
644
+ }),
645
+ ...aaveV3Markets.map(async (market) => {
646
+ markets.aaveV3MarketsData[market.value] = await _getAaveV3MarketData(client, network, market);
647
+ }),
648
+ ...aaveV4Spokes.map(async (spoke) => {
649
+ markets.aaveV4SpokesData[spoke.value] = await _getAaveV4SpokeData(client, network, spoke);
650
+ }),
651
+ ...aaveV2Markets.map(async (market) => {
652
+ markets.aaveV2MarketsData[market.value] = await _getAaveV2MarketsData(client, network, market);
653
+ }),
654
+ ...compoundV2Markets.map(async (market) => {
655
+ markets.compoundV2MarketsData[market.value] = await _getCompoundV2MarketsData(client, network);
656
+ }),
657
+ ...crvUsdMarkets.map(async (market) => {
658
+ markets.crvUsdMarketsData[market.value] = await _getCurveUsdGlobalData(client, network, market);
659
+ }),
660
+ ...llamaLendMarkets.map(async (market) => {
661
+ markets.llamaLendMarketsData[market.value] = await _getLlamaLendGlobalData(client, network, market);
662
+ }),
663
+ ...liquityV2Markets.map(async (market) => {
664
+ markets.liquityV2MarketsData[market.value] = await _getLiquityV2MarketData(client, network, market);
665
+ }),
666
+ (async () => {
667
+ if (!isFluidSupported) return;
668
+ try {
669
+ markets.fluidMarketsData = await _getAllFluidMarketDataPortfolio(client, network);
670
+ } catch (error) {
671
+ console.error('Error fetching Fluid markets data:', error);
672
+ }
673
+ })(),
674
+ (async () => {
675
+ if (!isMainnet) return; // Maker CDPs are only available on mainnet
676
+ try {
677
+ markets.makerMarketsData = await _getMakerIlksData(client, network, MakerActiveIlks);
678
+ } catch (error) {
679
+ console.error('Error fetching Maker ilks data:', error);
680
+ }
681
+ })(),
682
+
683
+ // === INDEPENDENT USER DATA (doesn't depend on market data) ===
684
+ ...addresses.map(async (address) => {
685
+ if (!isMainnet) return; // Maker CDPs are only available on mainnet
686
+ const makerCdp = await _getUserCdps(client, network, address);
687
+ makerCdps[address.toLowerCase() as EthAddress] = makerCdp;
688
+ }),
689
+ ...addresses.map(async (address) => {
690
+ try {
691
+ if (!isFluidSupported) return; // Fluid is not available on Optimism
692
+ const userPositions = (await _getUserPositionsPortfolio(client, network, address));
693
+ for (const position of userPositions) {
694
+ if (position.userData && new Dec(position.userData.suppliedUsd).gt(0)) {
695
+ positions[address.toLowerCase() as EthAddress].fluid.data[position.userData.nftId] = position.userData;
696
+ }
697
+ }
698
+ } catch (error) {
699
+ console.error(`Error fetching Fluid positions for address ${address}:`, error);
700
+ positions[address.toLowerCase() as EthAddress].fluid = {
701
+ error: `Error fetching Fluid positions for address ${address}`,
702
+ data: {},
703
+ };
704
+ }
705
+ }),
428
706
  ]);
429
707
 
430
708
  await Promise.all([
431
- ...aaveV3Markets.map((market) => allAddresses.map(async (address) => {
709
+ ...aaveV3Markets.map((market) => addresses.map(async (address) => {
432
710
  try {
433
- const accData = await _getAaveV3AccountData(client, network, address, { selectedMarket: market, ...aaveV3MarketsData[market.value] });
434
- if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].aaveV3[market.value] = { error: '', data: accData };
711
+ const accData = await _getAaveV3AccountData(client, network, address, { selectedMarket: market, ...markets.aaveV3MarketsData[market.value] });
712
+ // Always store the position (even with no supply) so the eMode category is available to the shifter
713
+ positions[address.toLowerCase() as EthAddress].aaveV3[market.value] = { error: '', data: accData };
435
714
  } catch (error) {
436
715
  console.error(`Error fetching AaveV3 account data for address ${address} on market ${market.value}:`, error);
437
716
  positions[address.toLowerCase() as EthAddress].aaveV3[market.value] = { error: `Error fetching AaveV3 account data for address ${address} on market ${market.value}`, data: null };
438
717
  }
439
718
  })).flat(),
440
- ...aaveV4Spokes.map((spoke) => allAddresses.map(async (address) => {
719
+ ...aaveV4Spokes.map((spoke) => addresses.map(async (address) => {
441
720
  try {
442
- const accData = await _getAaveV4AccountData(client, network, aaveV4SpokesData[spoke.value], address);
721
+ const accData = await _getAaveV4AccountData(client, network, markets.aaveV4SpokesData[spoke.value], address);
443
722
  if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].aaveV4[spoke.value] = { error: '', data: accData };
444
723
  } catch (error) {
445
724
  console.error(`Error fetching AaveV4 account data for address ${address} on spoke ${spoke.value}:`, error);
@@ -448,70 +727,36 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
448
727
  })).flat(),
449
728
  ...morphoMarkets.map((market) => addresses.map(async (address) => {
450
729
  try {
451
- const [accDataPromise, earnDataPromise] = await Promise.allSettled([
452
- _getMorphoBlueAccountData(client, network, address, market, morphoMarketsData[market.value]),
453
- getMorphoEarn(client, network, address, market, morphoMarketsData[market.value]),
454
- ]);
455
- if (accDataPromise.status === 'rejected') {
456
- console.error(`Error fetching MorphoBlue account data for address ${address} on market ${market.value}:`, accDataPromise.reason);
457
- positions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: `Error fetching MorphoBlue account data for address ${address} on market ${market.value}`, data: null };
458
- }
459
- if (earnDataPromise.status === 'rejected') {
460
- console.error(`Error fetching MorphoBlue account data for address ${address} on market ${market.value}:`, earnDataPromise.reason);
461
- positions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: `Error fetching MorphoBlue account data for address ${address} on market ${market.value}`, data: null };
462
- }
463
- if (accDataPromise.status !== 'rejected') {
464
- const accData = accDataPromise.value;
465
- if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: '', data: accData };
466
- }
467
- if (earnDataPromise.status !== 'rejected') {
468
- const earnData = earnDataPromise.value;
469
- if (earnData && new Dec(earnData.amount).gt(0)) {
470
- stakingPositions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = {
471
- error: '',
472
- data: earnData,
473
- };
474
- }
475
- }
730
+ const accData = await _getMorphoBlueAccountData(client, network, address, market, markets.morphoMarketsData[market.value]);
731
+ if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: '', data: accData };
476
732
  } catch (error) {
477
733
  console.error(`Error fetching MorphoBlue account data for address ${address} on market ${market.value}:`, error);
478
734
  positions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: `Error fetching MorphoBlue account data for address ${address} on market ${market.value}`, data: null };
479
735
  }
480
736
  })).flat(),
481
- ...morphoMidnightMarkets.map((market) => addresses.map(async (address) => {
482
- try {
483
- // Markets are created lazily on the first position, so an uncreated one can hold no position.
484
- if (!morphoMidnightMarketsData[market.value]?.isCreated) return;
485
- const accData = await _getMorphoMidnightAccountData(client, network, address, market, morphoMidnightMarketsData[market.value]);
486
- if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].morphoMidnight[market.value] = { error: '', data: accData };
487
- } catch (error) {
488
- console.error(`Error fetching MorphoMidnight account data for address ${address} on market ${market.value}:`, error);
489
- positions[address.toLowerCase() as EthAddress].morphoMidnight[market.value] = { error: `Error fetching MorphoMidnight account data for address ${address} on market ${market.value}`, data: null };
490
- }
491
- })).flat(),
492
737
  ...compoundV3Markets.map((market) => addresses.map(async (address) => {
493
738
  try {
494
- const accData = await _getCompoundV3AccountData(client, network, address, ZERO_ADDRESS, { selectedMarket: market, assetsData: compoundV3MarketsData[market.value].assetsData });
739
+ const accData = await _getCompoundV3AccountData(client, network, address, ZERO_ADDRESS, { selectedMarket: market, assetsData: markets.compoundV3MarketsData[market.value].assetsData });
495
740
  if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].compoundV3[market.value] = { error: '', data: accData };
496
741
  } catch (error) {
497
742
  console.error(`Error fetching CompoundV3 account data for address ${address} on market ${market.value}:`, error);
498
743
  positions[address.toLowerCase() as EthAddress].compoundV3[market.value] = { error: `Error fetching CompoundV3 account data for address ${address} on market ${market.value}`, data: null };
499
744
  }
500
745
  })).flat(),
501
- ...sparkMarkets.map((market) => allAddresses.map(async (address) => {
746
+ ...sparkMarkets.map((market) => addresses.map(async (address) => {
502
747
  try {
503
- const accData = await _getSparkAccountData(client, network, address, { selectedMarket: market, assetsData: sparkMarketsData[market.value].assetsData, eModeCategoriesData: sparkMarketsData[market.value].eModeCategoriesData });
504
- if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].spark[market.value] = { error: '', data: accData };
748
+ const accData = await _getSparkAccountData(client, network, address, { selectedMarket: market, assetsData: markets.sparkMarketsData[market.value].assetsData, eModeCategoriesData: markets.sparkMarketsData[market.value].eModeCategoriesData });
749
+ // Always store the position (even with no supply) so the eMode category is available to the shifter
750
+ positions[address.toLowerCase() as EthAddress].spark[market.value] = { error: '', data: accData };
505
751
  } catch (error) {
506
752
  console.error(`Error fetching Spark account data for address ${address} on market ${market.value}:`, error);
507
753
  positions[address.toLowerCase() as EthAddress].spark[market.value] = { error: `Error fetching Spark account data for address ${address} on market ${market.value}`, data: null };
508
754
  }
509
755
  })).flat(),
510
- // flatMap, not map: each CDP's fetch has to be one of the awaited promises, or getPortfolioData can return
511
- // before a vault's data lands and the vault is missing from the result.
512
- ...addresses.flatMap((address) => (makerCdps[address.toLowerCase() as EthAddress] ?? []).map(async (cdpInfo) => {
756
+ ...addresses.map((address) => (makerCdps[address.toLowerCase() as EthAddress] || []).map(async (cdpInfo) => {
513
757
  try {
514
- const cdpData = await _getMakerCdpData(client, network, cdpInfo);
758
+ // reuse ilk data fetched for the markets payload; ilks outside the active set are fetched on demand
759
+ const cdpData = await _getMakerCdpData(client, network, cdpInfo, markets.makerMarketsData[cdpInfo.ilkLabel]);
515
760
  if (cdpData) {
516
761
  positions[address.toLowerCase() as EthAddress].maker[cdpInfo.id] = { error: '', data: cdpData };
517
762
  }
@@ -519,10 +764,10 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
519
764
  console.error(`Error fetching Maker CDP data for address ${address} with ID ${cdpInfo.id}:`, error);
520
765
  positions[address.toLowerCase() as EthAddress].maker[cdpInfo.id] = { error: `Error fetching Maker CDP data for address ${address} with ID ${cdpInfo.id}`, data: null };
521
766
  }
522
- })),
767
+ })).flat(),
523
768
  ...aaveV2Markets.map((market) => addresses.map(async (address) => {
524
769
  try {
525
- const accData = await _getAaveV2AccountData(client, network, address, aaveV2MarketsData[market.value].assetsData, market);
770
+ const accData = await _getAaveV2AccountData(client, network, address, markets.aaveV2MarketsData[market.value].assetsData, market);
526
771
  if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].aaveV2[market.value] = { error: '', data: accData };
527
772
  } catch (error) {
528
773
  console.error(`Error fetching AaveV2 account data for address ${address}:`, error);
@@ -531,7 +776,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
531
776
  })).flat(),
532
777
  ...compoundV2Markets.map((market) => addresses.map(async (address) => {
533
778
  try {
534
- const accData = await _getCompoundV2AccountData(client, network, address, compoundV2MarketsData[market.value].assetsData);
779
+ const accData = await _getCompoundV2AccountData(client, network, address, markets.compoundV2MarketsData[market.value].assetsData);
535
780
  if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].compoundV2[market.value] = { error: '', data: accData };
536
781
  } catch (error) {
537
782
  console.error(`Error fetching CompoundV2 account data for address ${address}:`, error);
@@ -550,9 +795,9 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
550
795
  }),
551
796
  ...crvUsdMarkets.map((market) => addresses.map(async (address) => {
552
797
  try {
553
- const accData = await _getCurveUsdUserData(client, network, address, market, crvUsdMarketsData[market.value].activeBand);
798
+ const accData = await _getCurveUsdUserData(client, network, address, market, markets.crvUsdMarketsData[market.value].activeBand, markets.crvUsdMarketsData[market.value].borrowRate);
554
799
  if (new Dec(accData.suppliedUsd).gt(0) || new Dec(accData.borrowedUsd).gt(0)) {
555
- positions[address.toLowerCase() as EthAddress].crvUsd[market.value] = { error: '', data: { ...accData, borrowRate: crvUsdMarketsData[market.value].borrowRate } };
800
+ positions[address.toLowerCase() as EthAddress].crvUsd[market.value] = { error: '', data: accData };
556
801
  }
557
802
  } catch (error) {
558
803
  console.error(`Error fetching Curve USD account data for address ${address} on market ${market.value}:`, error);
@@ -561,35 +806,19 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
561
806
  })).flat(),
562
807
  ...llamaLendMarkets.map((market) => addresses.map(async (address) => {
563
808
  try {
564
- const accData = await _getLlamaLendUserData(client, network, address, market, llamaLendMarketsData[market.value]);
809
+ const accData = await _getLlamaLendUserData(client, network, address, market, markets.llamaLendMarketsData[market.value]);
565
810
  if (new Dec(accData.suppliedUsd).gt(0) || new Dec(accData.borrowedUsd).gt(0)) {
566
- positions[address.toLowerCase() as EthAddress].llamaLend[market.value] = { error: '', data: { ...accData, borrowRate: llamaLendMarketsData[market.value].borrowRate } };
811
+ positions[address.toLowerCase() as EthAddress].llamaLend[market.value] = { error: '', data: { ...accData, borrowRate: markets.llamaLendMarketsData[market.value].borrowRate } };
567
812
  }
568
813
  } catch (error) {
569
814
  console.error(`Error fetching LlamaLend account data for address ${address} on market ${market.value}:`, error);
570
815
  positions[address.toLowerCase() as EthAddress].llamaLend[market.value] = { error: `Error fetching LlamaLend account data for address ${address} on market ${market.value}`, data: null };
571
816
  }
572
817
  })).flat(),
573
- // liquity sBold/yBold and staking options
574
- ...addresses.map(async (address) => {
575
- try {
576
- if (!isMainnet) {
577
- stakingPositions[address.toLowerCase() as EthAddress].liquityV2SBoldYBold = { error: '', data: null };
578
- return;
579
- }
580
- const data = await getLiquitySAndYBold(client, network, stakingPositions[address.toLowerCase()].liquityV2, address);
581
- stakingPositions[address.toLowerCase() as EthAddress].liquityV2SBoldYBold = { error: '', data };
582
- } catch (error) {
583
- console.error(`Error fetching SBold/YBold data for address ${address}:`, error);
584
- stakingPositions[address.toLowerCase() as EthAddress].liquityV2SBoldYBold = { error: `Error fetching sBold/yBold data for address ${address}`, data: null };
585
- }
586
- }),
587
818
  ]);
588
819
 
589
820
  return {
590
821
  positions,
591
- stakingPositions,
592
- rewardsData,
593
822
  markets,
594
823
  };
595
824
  }