@moonwell-fi/moonwell-sdk 0.23.0 → 0.25.0

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 (199) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/_cjs/actions/axiosWithRetry.js +10 -11
  3. package/_cjs/actions/axiosWithRetry.js.map +1 -1
  4. package/_cjs/actions/core/getUserBalances.js +63 -32
  5. package/_cjs/actions/core/getUserBalances.js.map +1 -1
  6. package/_cjs/actions/core/markets/common.js +31 -42
  7. package/_cjs/actions/core/markets/common.js.map +1 -1
  8. package/_cjs/actions/core/markets/getMarkets.js +7 -3
  9. package/_cjs/actions/core/markets/getMarkets.js.map +1 -1
  10. package/_cjs/actions/core/user-positions/common.js +56 -60
  11. package/_cjs/actions/core/user-positions/common.js.map +1 -1
  12. package/_cjs/actions/core/user-positions/getUserPositions.js +9 -6
  13. package/_cjs/actions/core/user-positions/getUserPositions.js.map +1 -1
  14. package/_cjs/actions/governance/getStakingInfo.js +1 -7
  15. package/_cjs/actions/governance/getStakingInfo.js.map +1 -1
  16. package/_cjs/actions/governance/getUserStakingInfo.js +1 -7
  17. package/_cjs/actions/governance/getUserStakingInfo.js.map +1 -1
  18. package/_cjs/actions/governance/getWellPrice.js +51 -1
  19. package/_cjs/actions/governance/getWellPrice.js.map +1 -1
  20. package/_cjs/actions/governance/proposals/common.js +34 -15
  21. package/_cjs/actions/governance/proposals/common.js.map +1 -1
  22. package/_cjs/actions/governance/proposals/getProposals.js +5 -4
  23. package/_cjs/actions/governance/proposals/getProposals.js.map +1 -1
  24. package/_cjs/actions/index.js +3 -1
  25. package/_cjs/actions/index.js.map +1 -1
  26. package/_cjs/actions/morpho/markets/common.js +155 -26
  27. package/_cjs/actions/morpho/markets/common.js.map +1 -1
  28. package/_cjs/actions/morpho/markets/getMorphoMarket.js +1 -0
  29. package/_cjs/actions/morpho/markets/getMorphoMarket.js.map +1 -1
  30. package/_cjs/actions/morpho/markets/getMorphoMarkets.js +1 -0
  31. package/_cjs/actions/morpho/markets/getMorphoMarkets.js.map +1 -1
  32. package/_cjs/actions/morpho/markets/getMorphoMarketsSharedLiquidity.js +46 -0
  33. package/_cjs/actions/morpho/markets/getMorphoMarketsSharedLiquidity.js.map +1 -0
  34. package/_cjs/actions/morpho/user-positions/common.js +26 -24
  35. package/_cjs/actions/morpho/user-positions/common.js.map +1 -1
  36. package/_cjs/actions/morpho/user-positions/getMorphoMarketUserPositions.js +7 -8
  37. package/_cjs/actions/morpho/user-positions/getMorphoMarketUserPositions.js.map +1 -1
  38. package/_cjs/actions/morpho/user-positions/getMorphoVaultUserPositions.js +7 -8
  39. package/_cjs/actions/morpho/user-positions/getMorphoVaultUserPositions.js.map +1 -1
  40. package/_cjs/actions/morpho/user-rewards/common.js +1 -7
  41. package/_cjs/actions/morpho/user-rewards/common.js.map +1 -1
  42. package/_cjs/actions/morpho/vaults/common.js +104 -87
  43. package/_cjs/actions/morpho/vaults/common.js.map +1 -1
  44. package/_cjs/actions/morpho/vaults/lunarIndexerTransform.js +31 -3
  45. package/_cjs/actions/morpho/vaults/lunarIndexerTransform.js.map +1 -1
  46. package/_cjs/actions/retry.js +43 -13
  47. package/_cjs/actions/retry.js.map +1 -1
  48. package/_cjs/client/createActions.js +2 -0
  49. package/_cjs/client/createActions.js.map +1 -1
  50. package/_cjs/common/error.js +37 -1
  51. package/_cjs/common/error.js.map +1 -1
  52. package/_cjs/common/index.js +2 -1
  53. package/_cjs/common/index.js.map +1 -1
  54. package/_cjs/common/readAcrossEnvironments.js +17 -0
  55. package/_cjs/common/readAcrossEnvironments.js.map +1 -0
  56. package/_cjs/environments/types/config.js.map +1 -1
  57. package/_cjs/errors/version.js +1 -1
  58. package/_cjs/index.js +3 -1
  59. package/_cjs/index.js.map +1 -1
  60. package/_esm/actions/axiosWithRetry.js +11 -13
  61. package/_esm/actions/axiosWithRetry.js.map +1 -1
  62. package/_esm/actions/core/getUserBalances.js +72 -35
  63. package/_esm/actions/core/getUserBalances.js.map +1 -1
  64. package/_esm/actions/core/markets/common.js +36 -43
  65. package/_esm/actions/core/markets/common.js.map +1 -1
  66. package/_esm/actions/core/markets/getMarkets.js +7 -3
  67. package/_esm/actions/core/markets/getMarkets.js.map +1 -1
  68. package/_esm/actions/core/user-positions/common.js +65 -63
  69. package/_esm/actions/core/user-positions/common.js.map +1 -1
  70. package/_esm/actions/core/user-positions/getUserPositions.js +14 -6
  71. package/_esm/actions/core/user-positions/getUserPositions.js.map +1 -1
  72. package/_esm/actions/governance/getStakingInfo.js +2 -8
  73. package/_esm/actions/governance/getStakingInfo.js.map +1 -1
  74. package/_esm/actions/governance/getUserStakingInfo.js +2 -8
  75. package/_esm/actions/governance/getUserStakingInfo.js.map +1 -1
  76. package/_esm/actions/governance/getWellPrice.js +67 -7
  77. package/_esm/actions/governance/getWellPrice.js.map +1 -1
  78. package/_esm/actions/governance/proposals/common.js +37 -12
  79. package/_esm/actions/governance/proposals/common.js.map +1 -1
  80. package/_esm/actions/governance/proposals/getProposals.js +7 -5
  81. package/_esm/actions/governance/proposals/getProposals.js.map +1 -1
  82. package/_esm/actions/index.js +1 -0
  83. package/_esm/actions/index.js.map +1 -1
  84. package/_esm/actions/morpho/markets/common.js +168 -27
  85. package/_esm/actions/morpho/markets/common.js.map +1 -1
  86. package/_esm/actions/morpho/markets/getMorphoMarket.js +1 -0
  87. package/_esm/actions/morpho/markets/getMorphoMarket.js.map +1 -1
  88. package/_esm/actions/morpho/markets/getMorphoMarkets.js +1 -0
  89. package/_esm/actions/morpho/markets/getMorphoMarkets.js.map +1 -1
  90. package/_esm/actions/morpho/markets/getMorphoMarketsSharedLiquidity.js +48 -0
  91. package/_esm/actions/morpho/markets/getMorphoMarketsSharedLiquidity.js.map +1 -0
  92. package/_esm/actions/morpho/user-positions/common.js +31 -24
  93. package/_esm/actions/morpho/user-positions/common.js.map +1 -1
  94. package/_esm/actions/morpho/user-positions/getMorphoMarketUserPositions.js +12 -8
  95. package/_esm/actions/morpho/user-positions/getMorphoMarketUserPositions.js.map +1 -1
  96. package/_esm/actions/morpho/user-positions/getMorphoVaultUserPositions.js +12 -8
  97. package/_esm/actions/morpho/user-positions/getMorphoVaultUserPositions.js.map +1 -1
  98. package/_esm/actions/morpho/user-rewards/common.js +2 -8
  99. package/_esm/actions/morpho/user-rewards/common.js.map +1 -1
  100. package/_esm/actions/morpho/vaults/common.js +110 -91
  101. package/_esm/actions/morpho/vaults/common.js.map +1 -1
  102. package/_esm/actions/morpho/vaults/lunarIndexerTransform.js +36 -4
  103. package/_esm/actions/morpho/vaults/lunarIndexerTransform.js.map +1 -1
  104. package/_esm/actions/retry.js +45 -15
  105. package/_esm/actions/retry.js.map +1 -1
  106. package/_esm/client/createActions.js +2 -0
  107. package/_esm/client/createActions.js.map +1 -1
  108. package/_esm/common/error.js +49 -0
  109. package/_esm/common/error.js.map +1 -1
  110. package/_esm/common/index.js +1 -1
  111. package/_esm/common/index.js.map +1 -1
  112. package/_esm/common/readAcrossEnvironments.js +30 -0
  113. package/_esm/common/readAcrossEnvironments.js.map +1 -0
  114. package/_esm/environments/types/config.js.map +1 -1
  115. package/_esm/errors/version.js +1 -1
  116. package/_esm/index.js +1 -0
  117. package/_esm/index.js.map +1 -1
  118. package/_types/actions/axiosWithRetry.d.ts +1 -0
  119. package/_types/actions/axiosWithRetry.d.ts.map +1 -1
  120. package/_types/actions/core/getUserBalances.d.ts.map +1 -1
  121. package/_types/actions/core/markets/common.d.ts.map +1 -1
  122. package/_types/actions/core/markets/getMarkets.d.ts.map +1 -1
  123. package/_types/actions/core/user-positions/common.d.ts.map +1 -1
  124. package/_types/actions/core/user-positions/getUserPositions.d.ts +5 -0
  125. package/_types/actions/core/user-positions/getUserPositions.d.ts.map +1 -1
  126. package/_types/actions/governance/getStakingInfo.d.ts.map +1 -1
  127. package/_types/actions/governance/getUserStakingInfo.d.ts.map +1 -1
  128. package/_types/actions/governance/getWellPrice.d.ts +9 -1
  129. package/_types/actions/governance/getWellPrice.d.ts.map +1 -1
  130. package/_types/actions/governance/proposals/common.d.ts.map +1 -1
  131. package/_types/actions/governance/proposals/getProposals.d.ts +10 -1
  132. package/_types/actions/governance/proposals/getProposals.d.ts.map +1 -1
  133. package/_types/actions/index.d.ts +1 -0
  134. package/_types/actions/index.d.ts.map +1 -1
  135. package/_types/actions/morpho/markets/common.d.ts +7 -10
  136. package/_types/actions/morpho/markets/common.d.ts.map +1 -1
  137. package/_types/actions/morpho/markets/getMorphoMarket.d.ts +2 -0
  138. package/_types/actions/morpho/markets/getMorphoMarket.d.ts.map +1 -1
  139. package/_types/actions/morpho/markets/getMorphoMarkets.d.ts +2 -0
  140. package/_types/actions/morpho/markets/getMorphoMarkets.d.ts.map +1 -1
  141. package/_types/actions/morpho/markets/getMorphoMarketsSharedLiquidity.d.ts +17 -0
  142. package/_types/actions/morpho/markets/getMorphoMarketsSharedLiquidity.d.ts.map +1 -0
  143. package/_types/actions/morpho/user-positions/common.d.ts.map +1 -1
  144. package/_types/actions/morpho/user-positions/getMorphoMarketUserPositions.d.ts +5 -0
  145. package/_types/actions/morpho/user-positions/getMorphoMarketUserPositions.d.ts.map +1 -1
  146. package/_types/actions/morpho/user-positions/getMorphoVaultUserPositions.d.ts +5 -0
  147. package/_types/actions/morpho/user-positions/getMorphoVaultUserPositions.d.ts.map +1 -1
  148. package/_types/actions/morpho/user-rewards/common.d.ts.map +1 -1
  149. package/_types/actions/morpho/vaults/common.d.ts.map +1 -1
  150. package/_types/actions/morpho/vaults/lunarIndexerTransform.d.ts.map +1 -1
  151. package/_types/actions/retry.d.ts +5 -4
  152. package/_types/actions/retry.d.ts.map +1 -1
  153. package/_types/client/createActions.d.ts +2 -0
  154. package/_types/client/createActions.d.ts.map +1 -1
  155. package/_types/common/error.d.ts +31 -0
  156. package/_types/common/error.d.ts.map +1 -1
  157. package/_types/common/index.d.ts +2 -2
  158. package/_types/common/index.d.ts.map +1 -1
  159. package/_types/common/readAcrossEnvironments.d.ts +23 -0
  160. package/_types/common/readAcrossEnvironments.d.ts.map +1 -0
  161. package/_types/environments/types/config.d.ts +11 -1
  162. package/_types/environments/types/config.d.ts.map +1 -1
  163. package/_types/errors/version.d.ts +1 -1
  164. package/_types/index.d.ts +3 -1
  165. package/_types/index.d.ts.map +1 -1
  166. package/_types/types/morphoMarket.d.ts +9 -0
  167. package/_types/types/morphoMarket.d.ts.map +1 -1
  168. package/actions/axiosWithRetry.ts +19 -13
  169. package/actions/core/getUserBalances.ts +98 -51
  170. package/actions/core/markets/common.ts +49 -51
  171. package/actions/core/markets/getMarkets.ts +9 -5
  172. package/actions/core/user-positions/common.ts +107 -111
  173. package/actions/core/user-positions/getUserPositions.ts +11 -10
  174. package/actions/governance/getStakingInfo.ts +2 -9
  175. package/actions/governance/getUserStakingInfo.ts +2 -8
  176. package/actions/governance/getWellPrice.ts +99 -7
  177. package/actions/governance/proposals/common.ts +44 -19
  178. package/actions/governance/proposals/getProposals.ts +22 -5
  179. package/actions/index.ts +5 -0
  180. package/actions/morpho/markets/common.ts +324 -125
  181. package/actions/morpho/markets/getMorphoMarket.ts +3 -0
  182. package/actions/morpho/markets/getMorphoMarkets.ts +3 -0
  183. package/actions/morpho/markets/getMorphoMarketsSharedLiquidity.ts +93 -0
  184. package/actions/morpho/user-positions/common.ts +48 -42
  185. package/actions/morpho/user-positions/getMorphoMarketUserPositions.ts +14 -15
  186. package/actions/morpho/user-positions/getMorphoVaultUserPositions.ts +14 -15
  187. package/actions/morpho/user-rewards/common.ts +2 -8
  188. package/actions/morpho/vaults/common.ts +161 -127
  189. package/actions/morpho/vaults/lunarIndexerTransform.ts +41 -6
  190. package/actions/retry.ts +53 -18
  191. package/client/createActions.ts +13 -0
  192. package/common/error.ts +45 -0
  193. package/common/index.ts +2 -2
  194. package/common/readAcrossEnvironments.ts +45 -0
  195. package/environments/types/config.ts +11 -1
  196. package/errors/version.ts +1 -1
  197. package/index.ts +3 -0
  198. package/package.json +1 -1
  199. package/types/morphoMarket.ts +10 -0
@@ -248,13 +248,14 @@ export function transformVaultFromIndexer(
248
248
  // where totalLiquidity briefly exceeds totalAssets.
249
249
  const vaultSupplyValue = Math.max(0, totalAssetsValue - totalLiquidityValue);
250
250
 
251
- // Transform markets
252
- const markets = indexerVault.markets.map((market) =>
251
+ // Transform markets. The indexer has served vaults without `markets` /
252
+ // `rewards` (Sentry MOONWELL-FRONTEND-GX); keep the vault with empty lists.
253
+ const markets = (indexerVault.markets ?? []).map((market) =>
253
254
  transformMarket(market, underlyingDecimals, totalAssetsValue, tokenMap),
254
255
  );
255
256
 
256
257
  // Transform rewards
257
- const rewards = transformRewards(indexerVault.rewards, tokenMap);
258
+ const rewards = transformRewards(indexerVault.rewards ?? [], tokenMap);
258
259
 
259
260
  // Build vault object
260
261
  const vault: MorphoVault = {
@@ -313,9 +314,43 @@ export function transformVaultsFromIndexer(
313
314
  environment: Environment,
314
315
  tokenMap: Map<string, LunarIndexerToken>,
315
316
  ): MorphoVault[] {
316
- return indexerVaults.map((vault) =>
317
- transformVaultFromIndexer(vault, environment, tokenMap),
318
- );
317
+ // A malformed record costs only that record, never the chain's whole list.
318
+ // Missing `markets` / `rewards` keep the vault (as empty lists); a vault
319
+ // that cannot be built at all is dropped. Both are reported once per call.
320
+ const malformed: string[] = [];
321
+ let firstError: unknown;
322
+ const vaults = indexerVaults.flatMap((vault) => {
323
+ if (!Array.isArray(vault.markets) || !Array.isArray(vault.rewards)) {
324
+ malformed.push(vault.address);
325
+ firstError ??= new Error(
326
+ `Lunar Indexer vault ${vault.address} is missing ${!Array.isArray(vault.markets) ? "markets" : "rewards"}`,
327
+ );
328
+ }
329
+ try {
330
+ return [transformVaultFromIndexer(vault, environment, tokenMap)];
331
+ } catch (error) {
332
+ if (!malformed.includes(vault.address)) malformed.push(vault.address);
333
+ firstError ??= error;
334
+ return [];
335
+ }
336
+ });
337
+
338
+ if (malformed.length > 0) {
339
+ console.warn(
340
+ `Malformed Lunar Indexer vault record(s) on chain ${environment.chainId}: ${malformed.join(", ")}`,
341
+ firstError,
342
+ );
343
+ environment.onError?.(firstError, {
344
+ source: "vaults",
345
+ chainId: environment.chainId,
346
+ operation: "malformed-vault-records",
347
+ failedCount: malformed.length,
348
+ totalCount: indexerVaults.length,
349
+ items: malformed,
350
+ });
351
+ }
352
+
353
+ return vaults;
319
354
  }
320
355
 
321
356
  /**
package/actions/retry.ts CHANGED
@@ -16,8 +16,8 @@
16
16
  * where the existing `environment.onError(...)` wiring picks it up
17
17
  */
18
18
  import axios, {
19
- type AxiosError,
20
19
  type AxiosInstance,
20
+ type GenericAbortSignal,
21
21
  type InternalAxiosRequestConfig,
22
22
  } from "axios";
23
23
 
@@ -25,14 +25,30 @@ export interface RetryOptions {
25
25
  maxAttempts?: number;
26
26
  initialDelay?: number;
27
27
  maxDelay?: number;
28
+ signal?: GenericAbortSignal | undefined;
28
29
  }
29
30
 
30
31
  const DEFAULT_MAX_ATTEMPTS = 3;
31
32
  const DEFAULT_INITIAL_DELAY_MS = 250;
32
33
  const DEFAULT_MAX_DELAY_MS = 5_000;
33
34
 
34
- async function sleep(ms: number): Promise<void> {
35
- return new Promise((resolve) => setTimeout(resolve, ms));
35
+ async function sleep(ms: number, signal?: GenericAbortSignal): Promise<void> {
36
+ return new Promise((resolve, reject) => {
37
+ if (signal?.aborted) {
38
+ reject(new axios.CanceledError());
39
+ return;
40
+ }
41
+ const onAbort = (): void => {
42
+ clearTimeout(timer);
43
+ signal?.removeEventListener?.("abort", onAbort);
44
+ reject(new axios.CanceledError());
45
+ };
46
+ const timer = setTimeout(() => {
47
+ signal?.removeEventListener?.("abort", onAbort);
48
+ resolve();
49
+ }, ms);
50
+ signal?.addEventListener?.("abort", onAbort, { once: true });
51
+ });
36
52
  }
37
53
 
38
54
  function backoffDelay(
@@ -51,6 +67,7 @@ function backoffDelay(
51
67
  * - Non-axios errors (parse errors, code bugs) → do NOT retry
52
68
  */
53
69
  export function isRetriableError(error: unknown): boolean {
70
+ if (axios.isCancel(error)) return false;
54
71
  if (!axios.isAxiosError(error)) return false;
55
72
  if (!error.response) return true;
56
73
  return error.response.status >= 500;
@@ -59,33 +76,49 @@ export function isRetriableError(error: unknown): boolean {
59
76
  /**
60
77
  * Attach a retry-on-failure interceptor to an axios instance. Every request
61
78
  * made through this instance is retried up to `maxAttempts` times when the
62
- * failure is retriable. Per-config state is tracked on a WeakMap so we don't
63
- * pollute the public axios config shape.
79
+ * failure is retriable. Axios merges into a new config on every request, so
80
+ * the attempt counter must travel with that config rather than its identity.
64
81
  */
65
82
  export function attachRetryInterceptor(
66
83
  instance: AxiosInstance,
67
- options: RetryOptions = {},
84
+ options: Omit<RetryOptions, "signal"> = {},
68
85
  ): void {
69
86
  const {
70
87
  maxAttempts = DEFAULT_MAX_ATTEMPTS,
71
88
  initialDelay = DEFAULT_INITIAL_DELAY_MS,
72
89
  maxDelay = DEFAULT_MAX_DELAY_MS,
73
90
  } = options;
74
- const attemptsByConfig = new WeakMap<InternalAxiosRequestConfig, number>();
91
+ interface RetryConfig extends InternalAxiosRequestConfig {
92
+ moonwellRetryAttempts?: number | undefined;
93
+ }
75
94
 
76
95
  instance.interceptors.response.use(
77
- (response) => response,
78
- async (error: AxiosError) => {
79
- const config = error.config;
96
+ (response) => {
97
+ const config: RetryConfig = response.config;
98
+ config.moonwellRetryAttempts = undefined;
99
+ return response;
100
+ },
101
+ async (error: unknown) => {
102
+ if (!axios.isAxiosError(error)) throw error;
103
+ const config: RetryConfig | undefined = error.config;
80
104
  if (!config) throw error;
81
- if (!isRetriableError(error)) throw error;
82
-
83
- const previousAttempts = attemptsByConfig.get(config) ?? 0;
84
- if (previousAttempts >= maxAttempts - 1) throw error;
105
+ const previousAttempts = config.moonwellRetryAttempts ?? 0;
106
+ if (!isRetriableError(error) || previousAttempts >= maxAttempts - 1) {
107
+ config.moonwellRetryAttempts = undefined;
108
+ throw error;
109
+ }
85
110
 
86
- attemptsByConfig.set(config, previousAttempts + 1);
87
- await sleep(backoffDelay(previousAttempts, initialDelay, maxDelay));
88
- return instance.request(config);
111
+ config.moonwellRetryAttempts = previousAttempts + 1;
112
+ try {
113
+ await sleep(
114
+ backoffDelay(previousAttempts, initialDelay, maxDelay),
115
+ config.signal,
116
+ );
117
+ return await instance.request(config);
118
+ } catch (retryError) {
119
+ config.moonwellRetryAttempts = undefined;
120
+ throw retryError;
121
+ }
89
122
  },
90
123
  );
91
124
  }
@@ -103,10 +136,12 @@ export async function retry<T>(
103
136
  maxAttempts = DEFAULT_MAX_ATTEMPTS,
104
137
  initialDelay = DEFAULT_INITIAL_DELAY_MS,
105
138
  maxDelay = DEFAULT_MAX_DELAY_MS,
139
+ signal,
106
140
  } = options;
107
141
  let attempt = 0;
108
142
  let lastError: unknown;
109
143
  while (attempt < maxAttempts) {
144
+ if (signal?.aborted) throw new axios.CanceledError();
110
145
  try {
111
146
  return await fn();
112
147
  } catch (error) {
@@ -114,7 +149,7 @@ export async function retry<T>(
114
149
  attempt++;
115
150
  if (!isRetriableError(error)) throw error;
116
151
  if (attempt >= maxAttempts) break;
117
- await sleep(backoffDelay(attempt - 1, initialDelay, maxDelay));
152
+ await sleep(backoffDelay(attempt - 1, initialDelay, maxDelay), signal);
118
153
  }
119
154
  }
120
155
  throw lastError;
@@ -143,6 +143,11 @@ import {
143
143
  type GetMorphoMarketsReturnType,
144
144
  getMorphoMarkets,
145
145
  } from "../actions/morpho/markets/getMorphoMarkets.js";
146
+ import {
147
+ type GetMorphoMarketsSharedLiquidityParameters,
148
+ type GetMorphoMarketsSharedLiquidityReturnType,
149
+ getMorphoMarketsSharedLiquidity,
150
+ } from "../actions/morpho/markets/getMorphoMarketsSharedLiquidity.js";
146
151
  import {
147
152
  type GetMorphoMarketUserPositionParameters,
148
153
  type GetMorphoMarketUserPositionReturnType,
@@ -288,6 +293,12 @@ export type Actions<
288
293
  args?: GetMorphoMarketsParameters<environments, chain>,
289
294
  ) => GetMorphoMarketsReturnType;
290
295
 
296
+ getMorphoMarketsSharedLiquidity: <
297
+ chain extends Chain | undefined = Chain | undefined,
298
+ >(
299
+ args: GetMorphoMarketsSharedLiquidityParameters<environments, chain>,
300
+ ) => GetMorphoMarketsSharedLiquidityReturnType;
301
+
291
302
  getMorphoMarketUserPosition: <
292
303
  chain extends Chain | undefined = Chain | undefined,
293
304
  >(
@@ -399,6 +410,8 @@ export const actions = <
399
410
  getUserVotingPowers: (args) => getUserVotingPowers(client, args),
400
411
  getMorphoMarket: (args) => getMorphoMarket(client, args),
401
412
  getMorphoMarkets: (args) => getMorphoMarkets(client, args),
413
+ getMorphoMarketsSharedLiquidity: (args) =>
414
+ getMorphoMarketsSharedLiquidity(client, args),
402
415
  getMorphoMarketUserPosition: (args) =>
403
416
  getMorphoMarketUserPosition(client, args),
404
417
  getMorphoMarketUserPositions: (args) =>
package/common/error.ts CHANGED
@@ -28,3 +28,48 @@ export class HttpRequestError extends BaseError {
28
28
  Object.setPrototypeOf(this, HttpRequestError.prototype);
29
29
  }
30
30
  }
31
+
32
+ /** One failed per-chain read inside a multi-chain action. */
33
+ export type ChainReadFailure = {
34
+ chainId: number;
35
+ /** The original error from the RPC / contract read, unchanged. */
36
+ reason: unknown;
37
+ };
38
+
39
+ /**
40
+ * Thrown by multi-chain read actions (`getUserPositions`,
41
+ * `getMorphoMarketUserPositions`, `getMorphoVaultUserPositions`) when the read
42
+ * fails on one or more of the requested chains.
43
+ *
44
+ * `failures` lists every failed chain with its original error and `data`
45
+ * carries the results from the chains that succeeded, so a consumer can keep
46
+ * rendering the healthy chains while flagging the broken one. `errors` (from
47
+ * `AggregateError`) holds the same original errors in `failures` order.
48
+ *
49
+ * These failures are surfaced only through this rejection, not additionally
50
+ * through `onError`: a rejection is the consumer's to report, and reporting it
51
+ * on both paths doubled telemetry under query-library retries.
52
+ */
53
+ export class ChainReadError<T = unknown> extends AggregateError {
54
+ override readonly name = "ChainReadError";
55
+ readonly source: string;
56
+ readonly failures: ChainReadFailure[];
57
+ readonly data: T[];
58
+
59
+ constructor(params: {
60
+ source: string;
61
+ failures: ChainReadFailure[];
62
+ data: T[];
63
+ }) {
64
+ const chainIds = params.failures
65
+ .map((failure) => failure.chainId)
66
+ .join(", ");
67
+ super(
68
+ params.failures.map((failure) => failure.reason),
69
+ `[moonwell-sdk] ${params.source}: read failed on chainIds ${chainIds}`,
70
+ );
71
+ this.source = params.source;
72
+ this.failures = params.failures;
73
+ this.data = params.data;
74
+ }
75
+ }
package/common/index.ts CHANGED
@@ -5,8 +5,8 @@ import type { Environment } from "../environments/index.js";
5
5
 
6
6
  dayjs.extend(utc);
7
7
  export { Amount } from "./amount.js";
8
- export { BaseError, HttpRequestError } from "./error.js";
9
- export type { HttpRequestErrorType } from "./error.js";
8
+ export { BaseError, ChainReadError, HttpRequestError } from "./error.js";
9
+ export type { ChainReadFailure, HttpRequestErrorType } from "./error.js";
10
10
  export { getBlockNumberAtTimestamp } from "./getBlockNumberAtTimestamp.js";
11
11
  export type { MultichainReturnType } from "./types.js";
12
12
 
@@ -0,0 +1,45 @@
1
+ import type { Environment } from "../environments/index.js";
2
+ import { ChainReadError } from "./error.js";
3
+
4
+ /**
5
+ * Run a per-environment read on every environment and flatten the results.
6
+ *
7
+ * Unlike a bare `Promise.allSettled` that keeps only fulfilled entries, a
8
+ * rejected environment is never dropped silently: if any environment fails,
9
+ * the call rejects with a `ChainReadError` that lists every failed chain with
10
+ * its original error and carries the results of the chains that succeeded.
11
+ * For user positions a silently missing chain is indistinguishable from "no
12
+ * position", which drives wrong balances and borrow limits downstream; the
13
+ * partial `data` on the error lets a consumer keep rendering the healthy
14
+ * chains while flagging the broken one.
15
+ *
16
+ * Failures are surfaced only through the rejection, not additionally through
17
+ * `environment.onError`, so a consumer that retries the call is not told about
18
+ * the same outage once per attempt per chain.
19
+ */
20
+ export async function readAcrossEnvironments<T>(params: {
21
+ environments: Environment[];
22
+ source: string;
23
+ read: (environment: Environment) => Promise<T[]>;
24
+ }): Promise<T[]> {
25
+ const { environments, source, read } = params;
26
+
27
+ const settled = await Promise.allSettled(
28
+ environments.map((environment) => read(environment)),
29
+ );
30
+
31
+ const data = settled.flatMap((result) =>
32
+ result.status === "fulfilled" ? result.value : [],
33
+ );
34
+ const failures = settled.flatMap((result, index) =>
35
+ result.status === "rejected"
36
+ ? [{ chainId: environments[index].chainId, reason: result.reason }]
37
+ : [],
38
+ );
39
+
40
+ if (failures.length > 0) {
41
+ throw new ChainReadError({ source, failures, data });
42
+ }
43
+
44
+ return data;
45
+ }
@@ -228,11 +228,21 @@ export type ContractsConfig<contracts, tokens> = {} extends contracts
228
228
  ? {}
229
229
  : { [name in keyof ContractConfig<tokens>]?: ContractConfig<tokens>[name] };
230
230
 
231
- /** Context passed to `onError` callbacks. `token` is set for per-token read failures. */
231
+ /**
232
+ * Context passed to `onError` callbacks. `token` is set for per-token read failures.
233
+ *
234
+ * Batched reads report once per operation rather than once per item: `error`
235
+ * is the first failure, `failedCount`/`totalCount` size the batch and `items`
236
+ * identifies what failed (proposalIds, token/vault addresses, chainIds...).
237
+ */
232
238
  export type OnErrorContext = {
233
239
  source: string;
234
240
  chainId: number;
235
241
  token?: Address;
242
+ operation?: string;
243
+ failedCount?: number;
244
+ totalCount?: number;
245
+ items?: string[];
236
246
  };
237
247
 
238
248
  export type CustomConfig<custom> = {} extends custom
package/errors/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '0.23.0'
1
+ export const version = '0.25.0'
package/index.ts CHANGED
@@ -5,6 +5,8 @@ export {
5
5
 
6
6
  export { MerklApiError } from "./actions/morpho/user-rewards/common.js";
7
7
  export { MorphoUserRewardsAggregateError } from "./actions/morpho/user-rewards/getMorphoUserRewards.js";
8
+ export { ChainReadError } from "./common/error.js";
9
+ export type { ChainReadFailure } from "./common/error.js";
8
10
 
9
11
  export type {
10
12
  MarketConfig,
@@ -18,6 +20,7 @@ export type { Discussion } from "./types/discussion.js";
18
20
  export type { Market, MarketReward, MarketSnapshot } from "./types/market.js";
19
21
  export type {
20
22
  MorphoMarket,
23
+ MorphoMarketSharedLiquidity,
21
24
  MorphoMarketParamsType,
22
25
  PublicAllocatorSharedLiquidityType,
23
26
  } from "./types/morphoMarket.js";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@moonwell-fi/moonwell-sdk",
3
3
  "description": "TypeScript Interface for Moonwell",
4
- "version": "0.23.0",
4
+ "version": "0.25.0",
5
5
  "main": "./_cjs/index.js",
6
6
  "module": "./_esm/index.js",
7
7
  "types": "./_types/index.d.ts",
@@ -41,6 +41,16 @@ export type MorphoMarket = {
41
41
  totalBorrowApr: number;
42
42
  rewards: Required<MorphoReward>[];
43
43
  publicAllocatorSharedLiquidity: PublicAllocatorSharedLiquidityType[];
44
+ /** Whether allocator liquidity was fetched. An empty list is not proof of zero
45
+ * reallocatable liquidity when this is "not-requested" or "unavailable". */
46
+ sharedLiquidityStatus?: "available" | "unavailable" | "not-requested";
47
+ };
48
+
49
+ export type MorphoMarketSharedLiquidity = {
50
+ chainId: number;
51
+ marketId: string;
52
+ reallocatableLiquidityAssets: Amount;
53
+ publicAllocatorSharedLiquidity: PublicAllocatorSharedLiquidityType[];
44
54
  };
45
55
 
46
56
  export type MorphoMarketParamsType = {