@moonwell-fi/moonwell-sdk 0.10.7 → 0.12.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 (166) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/_cjs/actions/core/markets/common.js +26 -12
  3. package/_cjs/actions/core/markets/common.js.map +1 -1
  4. package/_cjs/actions/core/markets/getMarketSnapshots.js +173 -321
  5. package/_cjs/actions/core/markets/getMarketSnapshots.js.map +1 -1
  6. package/_cjs/actions/core/user-positions/getUserPositionSnapshots.js +31 -26
  7. package/_cjs/actions/core/user-positions/getUserPositionSnapshots.js.map +1 -1
  8. package/_cjs/actions/governance/getCirculatingSupplySnapshots.js +35 -29
  9. package/_cjs/actions/governance/getCirculatingSupplySnapshots.js.map +1 -1
  10. package/_cjs/actions/governance/getStakingSnapshots.js +16 -12
  11. package/_cjs/actions/governance/getStakingSnapshots.js.map +1 -1
  12. package/_cjs/actions/governance/proposals/common.js +247 -211
  13. package/_cjs/actions/governance/proposals/common.js.map +1 -1
  14. package/_cjs/actions/lunar-indexer-client.js +32 -0
  15. package/_cjs/actions/lunar-indexer-client.js.map +1 -1
  16. package/_cjs/actions/morpho/markets/common.js +26 -29
  17. package/_cjs/actions/morpho/markets/common.js.map +1 -1
  18. package/_cjs/actions/morpho/user-positions/getMorphoVaultUserPositionSnapshots.js +41 -56
  19. package/_cjs/actions/morpho/user-positions/getMorphoVaultUserPositionSnapshots.js.map +1 -1
  20. package/_cjs/actions/morpho/vaults/common.js +7 -4
  21. package/_cjs/actions/morpho/vaults/common.js.map +1 -1
  22. package/_cjs/actions/morpho/vaults/getMorphoVaultSnapshots.js +21 -78
  23. package/_cjs/actions/morpho/vaults/getMorphoVaultSnapshots.js.map +1 -1
  24. package/_cjs/actions/morpho/vaults/getMorphoVaultStakingSnapshots.js +13 -66
  25. package/_cjs/actions/morpho/vaults/getMorphoVaultStakingSnapshots.js.map +1 -1
  26. package/_cjs/client/createMoonwellClient.js +9 -3
  27. package/_cjs/client/createMoonwellClient.js.map +1 -1
  28. package/_cjs/environments/definitions/arbitrum/environment.js +1 -2
  29. package/_cjs/environments/definitions/arbitrum/environment.js.map +1 -1
  30. package/_cjs/environments/definitions/avalanche/environment.js +1 -2
  31. package/_cjs/environments/definitions/avalanche/environment.js.map +1 -1
  32. package/_cjs/environments/definitions/base/custom.js +0 -1
  33. package/_cjs/environments/definitions/base/custom.js.map +1 -1
  34. package/_cjs/environments/definitions/base/environment.js +1 -2
  35. package/_cjs/environments/definitions/base/environment.js.map +1 -1
  36. package/_cjs/environments/definitions/ethereum/environment.js +1 -2
  37. package/_cjs/environments/definitions/ethereum/environment.js.map +1 -1
  38. package/_cjs/environments/definitions/moonbeam/environment.js +1 -2
  39. package/_cjs/environments/definitions/moonbeam/environment.js.map +1 -1
  40. package/_cjs/environments/definitions/optimism/custom.js +0 -1
  41. package/_cjs/environments/definitions/optimism/custom.js.map +1 -1
  42. package/_cjs/environments/definitions/optimism/environment.js +1 -2
  43. package/_cjs/environments/definitions/optimism/environment.js.map +1 -1
  44. package/_cjs/environments/definitions/polygon/environment.js +1 -2
  45. package/_cjs/environments/definitions/polygon/environment.js.map +1 -1
  46. package/_cjs/environments/index.js +8 -8
  47. package/_cjs/environments/index.js.map +1 -1
  48. package/_cjs/environments/types/config.js +1 -0
  49. package/_cjs/environments/types/config.js.map +1 -1
  50. package/_cjs/errors/version.js +1 -1
  51. package/_esm/actions/core/markets/common.js +26 -13
  52. package/_esm/actions/core/markets/common.js.map +1 -1
  53. package/_esm/actions/core/markets/getMarketSnapshots.js +194 -346
  54. package/_esm/actions/core/markets/getMarketSnapshots.js.map +1 -1
  55. package/_esm/actions/core/user-positions/getUserPositionSnapshots.js +32 -29
  56. package/_esm/actions/core/user-positions/getUserPositionSnapshots.js.map +1 -1
  57. package/_esm/actions/governance/getCirculatingSupplySnapshots.js +35 -30
  58. package/_esm/actions/governance/getCirculatingSupplySnapshots.js.map +1 -1
  59. package/_esm/actions/governance/getStakingSnapshots.js +17 -13
  60. package/_esm/actions/governance/getStakingSnapshots.js.map +1 -1
  61. package/_esm/actions/governance/proposals/common.js +246 -216
  62. package/_esm/actions/governance/proposals/common.js.map +1 -1
  63. package/_esm/actions/lunar-indexer-client.js +35 -0
  64. package/_esm/actions/lunar-indexer-client.js.map +1 -1
  65. package/_esm/actions/morpho/markets/common.js +27 -34
  66. package/_esm/actions/morpho/markets/common.js.map +1 -1
  67. package/_esm/actions/morpho/user-positions/getMorphoVaultUserPositionSnapshots.js +42 -54
  68. package/_esm/actions/morpho/user-positions/getMorphoVaultUserPositionSnapshots.js.map +1 -1
  69. package/_esm/actions/morpho/vaults/common.js +7 -4
  70. package/_esm/actions/morpho/vaults/common.js.map +1 -1
  71. package/_esm/actions/morpho/vaults/getMorphoVaultSnapshots.js +24 -78
  72. package/_esm/actions/morpho/vaults/getMorphoVaultSnapshots.js.map +1 -1
  73. package/_esm/actions/morpho/vaults/getMorphoVaultStakingSnapshots.js +15 -65
  74. package/_esm/actions/morpho/vaults/getMorphoVaultStakingSnapshots.js.map +1 -1
  75. package/_esm/client/createMoonwellClient.js +9 -3
  76. package/_esm/client/createMoonwellClient.js.map +1 -1
  77. package/_esm/environments/definitions/arbitrum/environment.js +1 -2
  78. package/_esm/environments/definitions/arbitrum/environment.js.map +1 -1
  79. package/_esm/environments/definitions/avalanche/environment.js +1 -2
  80. package/_esm/environments/definitions/avalanche/environment.js.map +1 -1
  81. package/_esm/environments/definitions/base/custom.js +0 -1
  82. package/_esm/environments/definitions/base/custom.js.map +1 -1
  83. package/_esm/environments/definitions/base/environment.js +1 -2
  84. package/_esm/environments/definitions/base/environment.js.map +1 -1
  85. package/_esm/environments/definitions/ethereum/environment.js +1 -2
  86. package/_esm/environments/definitions/ethereum/environment.js.map +1 -1
  87. package/_esm/environments/definitions/moonbeam/environment.js +1 -2
  88. package/_esm/environments/definitions/moonbeam/environment.js.map +1 -1
  89. package/_esm/environments/definitions/optimism/custom.js +0 -1
  90. package/_esm/environments/definitions/optimism/custom.js.map +1 -1
  91. package/_esm/environments/definitions/optimism/environment.js +1 -2
  92. package/_esm/environments/definitions/optimism/environment.js.map +1 -1
  93. package/_esm/environments/definitions/polygon/environment.js +1 -2
  94. package/_esm/environments/definitions/polygon/environment.js.map +1 -1
  95. package/_esm/environments/index.js +8 -8
  96. package/_esm/environments/index.js.map +1 -1
  97. package/_esm/environments/types/config.js +1 -0
  98. package/_esm/environments/types/config.js.map +1 -1
  99. package/_esm/errors/version.js +1 -1
  100. package/_types/actions/core/markets/common.d.ts.map +1 -1
  101. package/_types/actions/core/markets/getMarketSnapshots.d.ts.map +1 -1
  102. package/_types/actions/core/user-positions/getUserPositionSnapshots.d.ts +0 -1
  103. package/_types/actions/core/user-positions/getUserPositionSnapshots.d.ts.map +1 -1
  104. package/_types/actions/governance/getCirculatingSupplySnapshots.d.ts.map +1 -1
  105. package/_types/actions/governance/getStakingSnapshots.d.ts.map +1 -1
  106. package/_types/actions/governance/proposals/common.d.ts +2 -8
  107. package/_types/actions/governance/proposals/common.d.ts.map +1 -1
  108. package/_types/actions/lunar-indexer-client.d.ts +26 -0
  109. package/_types/actions/lunar-indexer-client.d.ts.map +1 -1
  110. package/_types/actions/morpho/markets/common.d.ts.map +1 -1
  111. package/_types/actions/morpho/user-positions/getMorphoVaultUserPositionSnapshots.d.ts +9 -3
  112. package/_types/actions/morpho/user-positions/getMorphoVaultUserPositionSnapshots.d.ts.map +1 -1
  113. package/_types/actions/morpho/vaults/common.d.ts.map +1 -1
  114. package/_types/actions/morpho/vaults/getMorphoVaultSnapshots.d.ts.map +1 -1
  115. package/_types/actions/morpho/vaults/getMorphoVaultStakingSnapshots.d.ts.map +1 -1
  116. package/_types/client/createMoonwellClient.d.ts +4 -4
  117. package/_types/client/createMoonwellClient.d.ts.map +1 -1
  118. package/_types/environments/definitions/arbitrum/environment.d.ts +1 -1
  119. package/_types/environments/definitions/arbitrum/environment.d.ts.map +1 -1
  120. package/_types/environments/definitions/avalanche/environment.d.ts +1 -1
  121. package/_types/environments/definitions/avalanche/environment.d.ts.map +1 -1
  122. package/_types/environments/definitions/base/custom.d.ts +0 -1
  123. package/_types/environments/definitions/base/custom.d.ts.map +1 -1
  124. package/_types/environments/definitions/base/environment.d.ts +1 -1
  125. package/_types/environments/definitions/base/environment.d.ts.map +1 -1
  126. package/_types/environments/definitions/ethereum/environment.d.ts +1 -1
  127. package/_types/environments/definitions/ethereum/environment.d.ts.map +1 -1
  128. package/_types/environments/definitions/moonbeam/environment.d.ts +1 -1
  129. package/_types/environments/definitions/moonbeam/environment.d.ts.map +1 -1
  130. package/_types/environments/definitions/optimism/custom.d.ts +0 -1
  131. package/_types/environments/definitions/optimism/custom.d.ts.map +1 -1
  132. package/_types/environments/definitions/optimism/environment.d.ts +1 -2
  133. package/_types/environments/definitions/optimism/environment.d.ts.map +1 -1
  134. package/_types/environments/definitions/polygon/environment.d.ts +1 -1
  135. package/_types/environments/definitions/polygon/environment.d.ts.map +1 -1
  136. package/_types/environments/index.d.ts +0 -3
  137. package/_types/environments/index.d.ts.map +1 -1
  138. package/_types/environments/types/config.d.ts +10 -3
  139. package/_types/environments/types/config.d.ts.map +1 -1
  140. package/_types/errors/version.d.ts +1 -1
  141. package/actions/core/markets/common.ts +33 -18
  142. package/actions/core/markets/getMarketSnapshots.ts +261 -511
  143. package/actions/core/user-positions/getUserPositionSnapshots.ts +44 -46
  144. package/actions/governance/getCirculatingSupplySnapshots.ts +43 -35
  145. package/actions/governance/getStakingSnapshots.ts +32 -29
  146. package/actions/governance/proposals/common.ts +369 -330
  147. package/actions/lunar-indexer-client.ts +64 -0
  148. package/actions/morpho/markets/common.ts +32 -43
  149. package/actions/morpho/user-positions/getMorphoVaultUserPositionSnapshots.ts +76 -87
  150. package/actions/morpho/vaults/common.ts +7 -6
  151. package/actions/morpho/vaults/getMorphoVaultSnapshots.ts +36 -120
  152. package/actions/morpho/vaults/getMorphoVaultStakingSnapshots.ts +24 -111
  153. package/client/createMoonwellClient.ts +17 -5
  154. package/environments/definitions/arbitrum/environment.ts +0 -2
  155. package/environments/definitions/avalanche/environment.ts +0 -2
  156. package/environments/definitions/base/custom.ts +0 -1
  157. package/environments/definitions/base/environment.ts +0 -2
  158. package/environments/definitions/ethereum/environment.ts +0 -2
  159. package/environments/definitions/moonbeam/environment.ts +0 -2
  160. package/environments/definitions/optimism/custom.ts +0 -1
  161. package/environments/definitions/optimism/environment.ts +0 -2
  162. package/environments/definitions/polygon/environment.ts +0 -2
  163. package/environments/index.ts +6 -27
  164. package/environments/types/config.ts +11 -3
  165. package/errors/version.ts +1 -1
  166. package/package.json +1 -1
@@ -74,6 +74,30 @@ export interface LunarPortfolioOptions {
74
74
  market?: string;
75
75
  }
76
76
 
77
+ export interface LunarVaultPortfolioOptions {
78
+ startTime: number;
79
+ endTime: number;
80
+ granularity?: "1h" | "6h" | "1d";
81
+ chainId?: number;
82
+ vault?: string;
83
+ }
84
+
85
+ export interface LunarVaultPosition {
86
+ chainId: number;
87
+ vaultAddress: string;
88
+ shareBalance: string;
89
+ shareBalanceUsd: number;
90
+ assetsValue: number;
91
+ }
92
+
93
+ export interface LunarVaultPortfolio {
94
+ account: string;
95
+ positions: Array<{
96
+ timestamp: number;
97
+ vaults: LunarVaultPosition[];
98
+ }>;
99
+ }
100
+
77
101
  export interface LunarComptroller {
78
102
  id: string;
79
103
  chainId: number;
@@ -251,6 +275,7 @@ export const DEFAULT_LUNAR_TIMEOUT_MS = 10_000;
251
275
  export class LunarIndexerClient {
252
276
  private client: AxiosInstance;
253
277
  private stakingClient: AxiosInstance;
278
+ private vaultsClient: AxiosInstance;
254
279
 
255
280
  constructor(config: LunarIndexerConfig) {
256
281
  this.client = axios.create({
@@ -268,6 +293,14 @@ export class LunarIndexerClient {
268
293
  "Content-Type": "application/json",
269
294
  },
270
295
  });
296
+
297
+ this.vaultsClient = axios.create({
298
+ baseURL: `${config.baseUrl}/api/v1/vaults`,
299
+ timeout: config.timeout || DEFAULT_LUNAR_TIMEOUT_MS,
300
+ headers: {
301
+ "Content-Type": "application/json",
302
+ },
303
+ });
271
304
  }
272
305
 
273
306
  /**
@@ -469,6 +502,37 @@ export class LunarIndexerClient {
469
502
  }
470
503
  }
471
504
 
505
+ /**
506
+ * Get vault account portfolio with historical positions
507
+ */
508
+ async getVaultAccountPortfolio(
509
+ accountAddress: string,
510
+ options: LunarVaultPortfolioOptions,
511
+ ): Promise<LunarVaultPortfolio> {
512
+ try {
513
+ const params: Record<string, string> = {
514
+ startTime: options.startTime.toString(),
515
+ endTime: options.endTime.toString(),
516
+ };
517
+ if (options.granularity) params.granularity = options.granularity;
518
+ if (options.chainId) params.chainId = options.chainId.toString();
519
+ if (options.vault) params.vault = options.vault;
520
+
521
+ const response = await this.vaultsClient.get<LunarVaultPortfolio>(
522
+ `/account/${accountAddress.toLowerCase()}/portfolio`,
523
+ { params },
524
+ );
525
+ return response.data;
526
+ } catch (error) {
527
+ throw new LunarIndexerError(
528
+ `Failed to fetch vault portfolio for account ${accountAddress}`,
529
+ axios.isAxiosError(error) ? error.response?.status : undefined,
530
+ `/account/${accountAddress}/portfolio`,
531
+ error as Error,
532
+ );
533
+ }
534
+ }
535
+
472
536
  /**
473
537
  * Get vault staking snapshots for a specific chain (MetaMorpho vault staking)
474
538
  */
@@ -8,7 +8,6 @@ import type {
8
8
  PublicAllocatorSharedLiquidityType,
9
9
  } from "../../../types/morphoMarket.js";
10
10
  import type { MorphoReward } from "../../../types/morphoReward.js";
11
- import { shouldFallback } from "../../lunar-indexer-client.js";
12
11
  import { getGraphQL } from "../utils/graphql.js";
13
12
  import {
14
13
  type GetMorphoMarketsRewardsReturnType as LunarIndexerRewardsType,
@@ -829,7 +828,10 @@ async function getMorphoMarketsDataFromIndexer(params: {
829
828
  `Failed to fetch markets from Lunar Indexer for chain ${environment.chainId}, falling back to on-chain:`,
830
829
  error,
831
830
  );
832
- // Return rejection so we can fall back to on-chain for this environment
831
+ environment.onError?.(error, {
832
+ source: "morpho-markets",
833
+ chainId: environment.chainId,
834
+ });
833
835
  return Promise.reject({ environment, error });
834
836
  }
835
837
  }),
@@ -841,9 +843,9 @@ async function getMorphoMarketsDataFromIndexer(params: {
841
843
 
842
844
  // Collect environments that failed to fetch from indexer for fallback
843
845
  const failedEnvironments = marketsSettlements
844
- .filter((s) => s.status === "rejected")
845
- .map((s: any) => s.reason?.environment)
846
- .filter((env) => env !== undefined);
846
+ .filter((s): s is PromiseRejectedResult => s.status === "rejected")
847
+ .map((s) => (s.reason as { environment?: Environment }).environment)
848
+ .filter((env): env is Environment => env !== undefined);
847
849
 
848
850
  // Fall back to on-chain for environments where indexer failed
849
851
  let fallbackMarkets: MorphoMarket[] = [];
@@ -851,16 +853,27 @@ async function getMorphoMarketsDataFromIndexer(params: {
851
853
  console.warn(
852
854
  `Falling back to on-chain for ${failedEnvironments.length} environment(s)`,
853
855
  );
854
- fallbackMarkets = await getMorphoMarketsDataFromOnChain({
855
- environments: failedEnvironments,
856
- markets: params.markets,
857
- includeRewards: params.includeRewards,
858
- });
856
+ try {
857
+ fallbackMarkets = await getMorphoMarketsDataFromOnChain({
858
+ environments: failedEnvironments,
859
+ markets: params.markets,
860
+ includeRewards: params.includeRewards,
861
+ });
862
+ } catch (rpcError) {
863
+ console.warn(
864
+ `RPC fallback also failed for ${failedEnvironments.length} environment(s):`,
865
+ rpcError,
866
+ );
867
+ for (const env of failedEnvironments) {
868
+ env.onError?.(rpcError, {
869
+ source: "morpho-markets-rpc-fallback",
870
+ chainId: env.chainId,
871
+ });
872
+ }
873
+ }
859
874
  }
860
875
 
861
- // Fetch shared liquidity from lunar-indexer, tracking which environments
862
- // fell back so we can fill them from api.morpho.org after.
863
- const fallbackChainIds = new Set<number>();
876
+ // Fetch shared liquidity from lunar-indexer.
864
877
  const sharedLiquiditySettlements = await Promise.allSettled(
865
878
  fulfilledMarkets.map(async ({ environment, markets }) => {
866
879
  const lunarIndexerUrl = environment.lunarIndexerUrl;
@@ -903,12 +916,14 @@ async function getMorphoMarketsDataFromIndexer(params: {
903
916
  );
904
917
  return { environment, data };
905
918
  } catch (error) {
906
- if (!shouldFallback(error)) throw error;
907
- console.debug(
908
- "[Lunar fallback] Falling back to Morpho API for shared liquidity:",
919
+ console.warn(
920
+ `[getMorphoMarketsData] Lunar shared liquidity failed for chain ${environment.chainId}:`,
909
921
  error,
910
922
  );
911
- fallbackChainIds.add(environment.chainId);
923
+ environment.onError?.(error, {
924
+ source: "morpho-shared-liquidity",
925
+ chainId: environment.chainId,
926
+ });
912
927
  return {
913
928
  environment,
914
929
  data: [] as GetMorphoMarketsPublicAllocatorSharedLiquidityReturnType[],
@@ -963,32 +978,6 @@ async function getMorphoMarketsDataFromIndexer(params: {
963
978
  });
964
979
  });
965
980
 
966
- // Fallback: if the lunar shared-liquidity endpoint failed for some environments,
967
- // fetch publicAllocatorSharedLiquidity from the Morpho API for those environments only.
968
- if (fallbackChainIds.size > 0) {
969
- const fallbackMarketIds = fulfilledMarkets
970
- .filter(({ environment }) => fallbackChainIds.has(environment.chainId))
971
- .flatMap(({ environment, markets }) =>
972
- markets.map((m) => ({
973
- marketId: m.marketId,
974
- chainId: environment.chainId,
975
- })),
976
- );
977
- const rewardEnvironment =
978
- params.environments.find((env) => env.custom?.morpho?.apiUrl) ??
979
- params.environments[0];
980
- const morphoApiData = await getMorphoMarketRewards(
981
- rewardEnvironment,
982
- fallbackMarketIds,
983
- );
984
- morphoApiData.forEach((item) => {
985
- const key = `${item.chainId}-${item.marketId.toLowerCase()}`;
986
- if (!sharedLiquidityMap.has(key)) {
987
- sharedLiquidityMap.set(key, item.publicAllocatorSharedLiquidity);
988
- }
989
- });
990
- }
991
-
992
981
  // Overlay rewards from the indexer when requested
993
982
  if (params.includeRewards) {
994
983
  fulfilledMarkets.forEach(({ environment, markets }) => {
@@ -1,23 +1,32 @@
1
- import axios from "axios";
2
- import dayjs from "dayjs";
3
- import utc from "dayjs/plugin/utc.js";
4
1
  import type { Address } from "viem";
5
2
  import type { MoonwellClient } from "../../../client/createMoonwellClient.js";
6
- import { getEnvironmentFromArgs, isStartOfDay } from "../../../common/index.js";
3
+ import { getEnvironmentFromArgs } from "../../../common/index.js";
4
+ import type { NetworkParameterType } from "../../../common/types.js";
7
5
  import type {
8
- MorphoVaultParameterType,
9
- NetworkParameterType,
10
- } from "../../../common/types.js";
11
- import type { Chain, Environment } from "../../../environments/index.js";
6
+ Chain,
7
+ Environment,
8
+ GetEnvironment,
9
+ VaultsType,
10
+ } from "../../../environments/index.js";
12
11
  import type { MorphoVaultUserPositionSnapshot } from "../../../types/morphoUserPosition.js";
13
-
14
- dayjs.extend(utc);
12
+ import {
13
+ DEFAULT_LUNAR_TIMEOUT_MS,
14
+ createLunarIndexerClient,
15
+ } from "../../lunar-indexer-client.js";
15
16
 
16
17
  export type GetMorphoVaultUserPositionSnapshotsParameters<
17
18
  environments,
18
19
  network extends Chain | undefined,
19
20
  > = NetworkParameterType<environments, network> &
20
- MorphoVaultParameterType<network> & {
21
+ (undefined extends network
22
+ ? {
23
+ /** Address of the vault token (omit to get all vaults for the account) */
24
+ vaultAddress?: Address;
25
+ }
26
+ : {
27
+ /** Vault key */
28
+ vault: keyof VaultsType<GetEnvironment<network>>;
29
+ }) & {
21
30
  userAddress: Address;
22
31
  };
23
32
 
@@ -38,95 +47,75 @@ export async function getMorphoVaultUserPositionSnapshots<
38
47
  return [];
39
48
  }
40
49
 
41
- let { vaultAddress, vault } = args as unknown as {
42
- vaultAddress: Address;
43
- vault: string;
50
+ const { vaultAddress: rawVaultAddress, vault } = args as unknown as {
51
+ vaultAddress: Address | undefined;
52
+ vault: string | undefined;
44
53
  };
45
54
 
46
- if (!vaultAddress) {
47
- vaultAddress = environment.vaults[vault].address;
55
+ const vaultAddress: Address | undefined =
56
+ rawVaultAddress ?? (vault ? environment.vaults[vault].address : undefined);
57
+
58
+ const lunarIndexerUrl = environment.lunarIndexerUrl;
59
+
60
+ if (!lunarIndexerUrl) {
61
+ return [];
48
62
  }
49
63
 
50
- return fetchUserPositionSnapshots(
51
- args.userAddress,
52
- vaultAddress,
53
- environment,
54
- );
64
+ try {
65
+ return await fetchUserPositionSnapshotsFromLunar(
66
+ args.userAddress,
67
+ vaultAddress,
68
+ lunarIndexerUrl,
69
+ environment,
70
+ );
71
+ } catch (error) {
72
+ console.warn(
73
+ `[getMorphoVaultUserPositionSnapshots] Lunar Indexer failed for chain ${environment.chainId}:`,
74
+ error,
75
+ );
76
+ environment.onError?.(error, {
77
+ source: "morpho-vault-user-position-snapshots",
78
+ chainId: environment.chainId,
79
+ });
80
+ return [];
81
+ }
55
82
  }
56
83
 
57
- async function fetchUserPositionSnapshots(
84
+ async function fetchUserPositionSnapshotsFromLunar(
58
85
  userAddress: Address,
59
- vaultAddress: Address,
86
+ vaultAddress: Address | undefined,
87
+ lunarIndexerUrl: string,
60
88
  environment: Environment,
61
89
  ): Promise<MorphoVaultUserPositionSnapshot[]> {
62
- const dailyData: MorphoVaultUserDailyData[] = [];
63
- let hasNextPage = true;
64
- let endCursor: string | undefined;
90
+ const lunarClient = createLunarIndexerClient({
91
+ baseUrl: lunarIndexerUrl,
92
+ timeout: DEFAULT_LUNAR_TIMEOUT_MS,
93
+ });
65
94
 
66
- interface MorphoVaultUserDailyData {
67
- totalSuppliesUSD: string;
68
- timestamp: number;
69
- }
70
-
71
- while (hasNextPage) {
72
- const result = await axios.post<{
73
- data: {
74
- accountVaultDailySnapshots: {
75
- items: MorphoVaultUserDailyData[];
76
- pageInfo: {
77
- hasNextPage: boolean;
78
- endCursor: string;
79
- };
80
- };
81
- };
82
- }>(environment.indexerUrl, {
83
- query: `
84
- query {
85
- accountVaultDailySnapshots(
86
- limit: 365,
87
- orderDirection: "desc",
88
- orderBy: "timestamp",
89
- where: { vaultAddress: "${vaultAddress.toLowerCase()}", accountAddress: "${userAddress.toLowerCase()}", chainId: ${environment.chainId} }
90
- ${endCursor ? `after: "${endCursor}"` : ""}
91
- ) {
92
- items {
93
- totalSuppliesUSD
94
- timestamp
95
- }
96
- pageInfo {
97
- hasNextPage
98
- endCursor
99
- }
100
- }
101
- }
102
- `,
103
- });
95
+ const endTime = Math.floor(Date.now() / 1000);
96
+ const startTime = endTime - 3 * 365 * 24 * 60 * 60;
104
97
 
105
- dailyData.push(
106
- ...result.data.data.accountVaultDailySnapshots.items.filter(
107
- (f: { timestamp: number }) => isStartOfDay(f.timestamp),
108
- ),
109
- );
110
- hasNextPage =
111
- result.data.data.accountVaultDailySnapshots.pageInfo.hasNextPage;
112
- endCursor = result.data.data.accountVaultDailySnapshots.pageInfo.endCursor;
113
- }
98
+ const portfolio = await lunarClient.getVaultAccountPortfolio(userAddress, {
99
+ startTime,
100
+ endTime,
101
+ granularity: "1d",
102
+ chainId: environment.chainId,
103
+ ...(vaultAddress ? { vault: vaultAddress } : {}),
104
+ });
114
105
 
115
- if (dailyData.length > 0) {
116
- return dailyData.map((point: MorphoVaultUserDailyData) => {
117
- const suppliedUsd = Number(point.totalSuppliesUSD);
106
+ const snapshots: MorphoVaultUserPositionSnapshot[] = [];
118
107
 
119
- const result: MorphoVaultUserPositionSnapshot = {
120
- chainId: environment.chainId,
121
- timestamp: point.timestamp * 1000,
122
- suppliedUsd: suppliedUsd,
108
+ for (const position of portfolio.positions) {
109
+ for (const v of position.vaults) {
110
+ snapshots.push({
111
+ chainId: v.chainId,
123
112
  account: userAddress,
124
- vaultAddress: vaultAddress,
125
- };
126
-
127
- return result;
128
- });
129
- } else {
130
- return [];
113
+ vaultAddress: v.vaultAddress as Address,
114
+ suppliedUsd: v.shareBalanceUsd,
115
+ timestamp: position.timestamp * 1000,
116
+ });
117
+ }
131
118
  }
119
+
120
+ return snapshots;
132
121
  }
@@ -153,14 +153,13 @@ async function getMorphoVaultsDataFromIndexer(params: {
153
153
  // Filter environments that have vaults and Lunar Indexer URL configured
154
154
  const environmentsWithVaults = environments.filter(
155
155
  (environment) =>
156
- Object.keys(environment.vaults).length > 0 &&
157
- environment.custom?.morpho?.lunarIndexerUrl,
156
+ Object.keys(environment.vaults).length > 0 && environment.lunarIndexerUrl,
158
157
  );
159
158
 
160
159
  // Fetch vaults from Lunar Indexer for each environment
161
160
  const environmentsVaultsSettlements = await Promise.allSettled(
162
161
  environmentsWithVaults.map(async (environment) => {
163
- const lunarIndexerUrl = environment.custom.morpho!.lunarIndexerUrl!;
162
+ const lunarIndexerUrl = environment.lunarIndexerUrl!;
164
163
 
165
164
  try {
166
165
  // Fetch tokens and vaults in parallel
@@ -228,6 +227,10 @@ async function getMorphoVaultsDataFromIndexer(params: {
228
227
  `Failed to fetch vaults from Lunar Indexer for chain ${environment.chainId}, falling back to on-chain:`,
229
228
  error,
230
229
  );
230
+ environment.onError?.(error, {
231
+ source: "vaults",
232
+ chainId: environment.chainId,
233
+ });
231
234
  throw { environment, error };
232
235
  }
233
236
  }),
@@ -442,9 +445,7 @@ export async function getMorphoVaultsData(params: {
442
445
  const { environments } = params;
443
446
 
444
447
  // Check if any environment has Lunar Indexer URL configured
445
- const hasLunarIndexer = environments.some(
446
- (env) => env.custom?.morpho?.lunarIndexerUrl,
447
- );
448
+ const hasLunarIndexer = environments.some((env) => env.lunarIndexerUrl);
448
449
 
449
450
  // Use Lunar Indexer implementation if available
450
451
  if (hasLunarIndexer) {
@@ -1,17 +1,15 @@
1
- import axios from "axios";
2
1
  import type { MoonwellClient } from "../../../client/createMoonwellClient.js";
3
2
  import {
4
3
  applyGranularity,
5
4
  calculateTimeRange,
6
5
  getEnvironmentFromArgs,
7
- isStartOfDay,
8
6
  toApiGranularity,
9
7
  } from "../../../common/index.js";
10
8
  import type {
11
9
  MorphoVaultParameterType,
12
10
  NetworkParameterType,
13
11
  } from "../../../common/types.js";
14
- import type { Chain, Environment } from "../../../environments/index.js";
12
+ import type { Chain } from "../../../environments/index.js";
15
13
  import type { MorphoVaultSnapshot } from "../../../types/morphoVault.js";
16
14
  import {
17
15
  fetchVaultSnapshotsFromIndexer,
@@ -76,29 +74,43 @@ export async function getMorphoVaultSnapshots<
76
74
  }
77
75
  }
78
76
 
79
- const lunarIndexerUrl = environment.custom?.morpho?.lunarIndexerUrl;
80
-
81
- const snapshots = lunarIndexerUrl
82
- ? await fetchVaultSnapshotsFromLunarIndexer(
83
- fetchAddress,
84
- environment.chainId,
85
- lunarIndexerUrl,
86
- period,
87
- customStartTime,
88
- customEndTime,
89
- )
90
- : await fetchVaultSnapshotsFromPonder(fetchAddress, environment);
91
-
92
- // Restore the originally-requested vault address on every snapshot so
93
- // callers keying by address see the V2 address they asked for.
94
- if (fetchAddress.toLowerCase() !== requestedVaultAddress.toLowerCase()) {
95
- return snapshots.map((s) => ({
96
- ...s,
97
- vaultAddress: requestedVaultAddress.toLowerCase(),
98
- }));
77
+ const lunarIndexerUrl = environment.lunarIndexerUrl;
78
+
79
+ if (!lunarIndexerUrl) {
80
+ return [];
99
81
  }
100
82
 
101
- return snapshots;
83
+ try {
84
+ const snapshots = await fetchVaultSnapshotsFromLunarIndexer(
85
+ fetchAddress,
86
+ environment.chainId,
87
+ lunarIndexerUrl,
88
+ period,
89
+ customStartTime,
90
+ customEndTime,
91
+ );
92
+
93
+ // Restore the originally-requested vault address on every snapshot so
94
+ // callers keying by address see the V2 address they asked for.
95
+ if (fetchAddress.toLowerCase() !== requestedVaultAddress.toLowerCase()) {
96
+ return snapshots.map((s) => ({
97
+ ...s,
98
+ vaultAddress: requestedVaultAddress.toLowerCase(),
99
+ }));
100
+ }
101
+
102
+ return snapshots;
103
+ } catch (error) {
104
+ console.warn(
105
+ `[getMorphoVaultSnapshots] Lunar Indexer failed for chain ${environment.chainId}:`,
106
+ error,
107
+ );
108
+ environment.onError?.(error, {
109
+ source: "morpho-vault-snapshots",
110
+ chainId: environment.chainId,
111
+ });
112
+ return [];
113
+ }
102
114
  }
103
115
 
104
116
  async function fetchVaultSnapshotsFromLunarIndexer(
@@ -147,99 +159,3 @@ async function fetchVaultSnapshotsFromLunarIndexer(
147
159
  allSnapshots.sort((a, b) => a.timestamp - b.timestamp);
148
160
  return applyGranularity(allSnapshots, granularity);
149
161
  }
150
-
151
- async function fetchVaultSnapshotsFromPonder(
152
- vaultAddress: string,
153
- environment: Environment,
154
- ): Promise<MorphoVaultSnapshot[]> {
155
- const dailyData: VaultDailyData[] = [];
156
- let hasNextPage = true;
157
- let endCursor: string | undefined;
158
-
159
- interface VaultDailyData {
160
- totalBorrows: number;
161
- totalBorrowsUSD: number;
162
- totalSupplies: number;
163
- totalSuppliesUSD: number;
164
- totalLiquidity: number;
165
- totalLiquidityUSD: number;
166
- timestamp: number;
167
- }
168
-
169
- while (hasNextPage) {
170
- const result = await axios.post<{
171
- data: {
172
- vaultDailySnapshots: {
173
- items: VaultDailyData[];
174
- pageInfo: {
175
- hasNextPage: boolean;
176
- endCursor: string;
177
- };
178
- };
179
- };
180
- }>(environment.indexerUrl, {
181
- query: `
182
- query {
183
- vaultDailySnapshots (
184
- limit: 365,
185
- orderBy: "timestamp"
186
- orderDirection: "desc"
187
- where: {vaultAddress: "${vaultAddress.toLowerCase()}", chainId: ${environment.chainId}}
188
- ${endCursor ? `after: "${endCursor}"` : ""}
189
- ) {
190
- items {
191
- totalBorrows
192
- totalBorrowsUSD
193
- totalSupplies
194
- totalSuppliesUSD
195
- totalLiquidity
196
- totalLiquidityUSD
197
- timestamp
198
- }
199
- pageInfo {
200
- hasNextPage
201
- endCursor
202
- }
203
- }
204
- }
205
- `,
206
- });
207
-
208
- if (result.data.data.vaultDailySnapshots) {
209
- dailyData.push(
210
- ...result.data.data.vaultDailySnapshots.items.filter(
211
- (f: { timestamp: number }) => isStartOfDay(f.timestamp),
212
- ),
213
- );
214
- hasNextPage = result.data.data.vaultDailySnapshots.pageInfo.hasNextPage;
215
- endCursor = result.data.data.vaultDailySnapshots.pageInfo.endCursor;
216
- }
217
- }
218
-
219
- if (dailyData.length > 0) {
220
- return dailyData.map((point: VaultDailyData) => {
221
- const supplied = Number(point.totalSupplies);
222
- const borrow = Number(point.totalBorrows);
223
- const borrowUsd = Number(point.totalBorrowsUSD);
224
- const suppliedUsd = Number(point.totalSuppliesUSD);
225
- const liquidity = Number(point.totalLiquidity);
226
- const liquidityUsd = Number(point.totalLiquidityUSD);
227
-
228
- const result: MorphoVaultSnapshot = {
229
- vaultAddress: vaultAddress.toLowerCase(),
230
- chainId: environment.chainId,
231
- timestamp: point.timestamp * 1000,
232
- totalSupply: supplied,
233
- totalSupplyUsd: suppliedUsd,
234
- totalBorrows: borrow,
235
- totalBorrowsUsd: borrowUsd,
236
- totalLiquidity: liquidity,
237
- totalLiquidityUsd: liquidityUsd,
238
- };
239
-
240
- return result;
241
- });
242
- } else {
243
- return [];
244
- }
245
- }