@exodus/market-history 10.7.0 → 10.8.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,16 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [10.8.0](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/market-history@10.7.1...@exodus/market-history@10.8.0) (2026-06-08)
7
+
8
+ ### Features
9
+
10
+ - **market-history:** add fetchAssets for non-enabled assets ([#17075](https://github.com/ExodusMovement/exodus-hydra/issues/17075)) ([faf4427](https://github.com/ExodusMovement/exodus-hydra/commit/faf44275bcb96ec92ce58b97f0a2f5fec0b11d3e))
11
+
12
+ ## [10.7.1](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/market-history@10.7.0...@exodus/market-history@10.7.1) (2026-05-04)
13
+
14
+ **Note:** Version bump only for package @exodus/market-history
15
+
6
16
  ## [10.7.0](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/market-history@10.6.3...@exodus/market-history@10.7.0) (2026-05-01)
7
17
 
8
18
  ### Features
@@ -8,6 +8,7 @@ declare const marketHistoryApiDefinition: {
8
8
  marketHistory: {
9
9
  update: (granularity: import("../types.js").Granularity) => Promise<void>;
10
10
  updateAll: () => Promise<void>;
11
+ fetchAssets: (assetNames: string[], granularities?: import("../types.js").Granularity[]) => Promise<void>;
11
12
  fetchAssetPricesFromDate: (params: {
12
13
  assetName: string;
13
14
  granularity: import("../types.js").Granularity;
package/lib/api/index.js CHANGED
@@ -3,6 +3,7 @@ const createMarketHistoryApi = ({ marketHistoryMonitor, }) => {
3
3
  marketHistory: {
4
4
  update: marketHistoryMonitor.update,
5
5
  updateAll: marketHistoryMonitor.updateAll,
6
+ fetchAssets: marketHistoryMonitor.fetchAssets,
6
7
  fetchAssetPricesFromDate: marketHistoryMonitor.fetchAssetPricesFromDate,
7
8
  },
8
9
  };
@@ -3,7 +3,7 @@ import type { MarketHistoryState } from '../types.js';
3
3
  export declare const marketHistoryAtomDefinition: {
4
4
  readonly id: "marketHistoryAtom";
5
5
  readonly type: "atom";
6
- readonly factory: () => import("@exodus/atoms").Atom<MarketHistoryState>;
6
+ readonly factory: () => import("@exodus/atoms").Atom<MarketHistoryState | undefined>;
7
7
  readonly dependencies: readonly [];
8
8
  readonly public: true;
9
9
  };
@@ -0,0 +1,3 @@
1
+ export declare const DEFAULT_DAILY_REQUEST_LIMIT = 366;
2
+ export declare const DEFAULT_HOURLY_REQUEST_LIMIT: number;
3
+ export declare const DEFAULT_MINUTELY_REQUEST_LIMIT: number;
@@ -0,0 +1,3 @@
1
+ export const DEFAULT_DAILY_REQUEST_LIMIT = 366;
2
+ export const DEFAULT_HOURLY_REQUEST_LIMIT = 7 * 24;
3
+ export const DEFAULT_MINUTELY_REQUEST_LIMIT = 2 * 60;
package/lib/index.d.ts CHANGED
@@ -105,7 +105,7 @@ declare const marketHistory: ({ clearHistoryCacheDefaultValue, remoteConfigClear
105
105
  stop: () => void;
106
106
  hydrate: () => Promise<void>;
107
107
  };
108
- marketHistoryAtom: import("@exodus/atoms").Atom<import("./types.js").MarketHistoryState>;
108
+ marketHistoryAtom: import("@exodus/atoms").Atom<import("./types.js").MarketHistoryState | undefined>;
109
109
  appProcessAtom?: import("@exodus/atoms").ReadonlyAtom<{
110
110
  mode: string;
111
111
  }>;
@@ -126,7 +126,7 @@ declare const marketHistory: ({ clearHistoryCacheDefaultValue, remoteConfigClear
126
126
  definition: {
127
127
  readonly id: "marketHistoryAtom";
128
128
  readonly type: "atom";
129
- readonly factory: () => import("@exodus/atoms").Atom<import("./types.js").MarketHistoryState>;
129
+ readonly factory: () => import("@exodus/atoms").Atom<import("./types.js").MarketHistoryState | undefined>;
130
130
  readonly dependencies: readonly [];
131
131
  readonly public: true;
132
132
  };
@@ -143,6 +143,7 @@ declare const marketHistory: ({ clearHistoryCacheDefaultValue, remoteConfigClear
143
143
  marketHistory: {
144
144
  update: (granularity: import("./types.js").Granularity) => Promise<void>;
145
145
  updateAll: () => Promise<void>;
146
+ fetchAssets: (assetNames: string[], granularities?: import("./types.js").Granularity[]) => Promise<void>;
146
147
  fetchAssetPricesFromDate: (params: {
147
148
  assetName: string;
148
149
  granularity: import("./types.js").Granularity;
package/lib/index.js CHANGED
@@ -3,11 +3,9 @@ import { marketHistoryAtomDefinition, marketHistoryClearCacheAtomDefinition, mar
3
3
  import marketHistoryMonitorDefinition from './module/index.js';
4
4
  import marketHistoryPluginDefinition from './plugin/index.js';
5
5
  import marketHistoryApiDefinition from './api/index.js';
6
+ import { DEFAULT_DAILY_REQUEST_LIMIT, DEFAULT_HOURLY_REQUEST_LIMIT, DEFAULT_MINUTELY_REQUEST_LIMIT, } from './constants.js';
6
7
  const CLEAR_MARKET_HISTORY_VERSION = 'infrastructure.marketHistory.clearVersion';
7
8
  const REFRESH_INTERVAL_ATOM_PATH = 'infrastructure.marketHistory.refreshInterval';
8
- const DEFAULT_DAILY_REQUEST_LIMIT = 366;
9
- const DEFAULT_HOURLY_REQUEST_LIMIT = 7 * 24;
10
- const DEFAULT_MINUTELY_REQUEST_LIMIT = 2 * 60;
11
9
  const marketHistory = ({ clearHistoryCacheDefaultValue = null, remoteConfigClearHistoryCachePath = CLEAR_MARKET_HISTORY_VERSION, remoteConfigClearHistoryCacheDefaultValue = null, marketHistoryRefreshIntervalPath = REFRESH_INTERVAL_ATOM_PATH, marketHistoryRefreshIntervalDefaultValue = null, dailyRequestLimit = DEFAULT_DAILY_REQUEST_LIMIT, hourlyRequestLimit = DEFAULT_HOURLY_REQUEST_LIMIT, minutelyRequestLimit = DEFAULT_MINUTELY_REQUEST_LIMIT, } = Object.create(null)) => {
12
10
  return {
13
11
  id: safeString `marketHistory`,
@@ -14,6 +14,6 @@ export default function fetchAndProcessTickers({ assetTickersToFetch, api, fiatT
14
14
  historicalPricesMap: Map<string, Map<number, PriceData>>;
15
15
  fetchedPricesMap: Map<string, Map<number, PriceData>>;
16
16
  specificTimestamp?: number | null;
17
- requestLimit?: number;
17
+ requestLimit: number;
18
18
  }): Promise<void>;
19
19
  export {};
@@ -5,7 +5,7 @@ export default function fetchHistoricalPrices({ api, assetTickers, fiatTicker, g
5
5
  fiatTicker: string;
6
6
  granularity?: Granularity;
7
7
  getCacheFromStorage: (ticker: string) => Promise<CacheEntry[] | string | undefined>;
8
- requestLimit?: number;
8
+ requestLimit: number;
9
9
  timestamp?: number;
10
10
  getCurrentTime?: () => number;
11
11
  ignoreInvalidSymbols?: boolean;
@@ -3,7 +3,7 @@ declare const getLimit: ({ granularity, history, requestLimit, requestTimestamp,
3
3
  granularity: Granularity;
4
4
  history: Map<number, PriceData>;
5
5
  requestTimestamp: number;
6
- requestLimit?: number;
6
+ requestLimit: number;
7
7
  specificTimestamp?: number | null;
8
8
  }) => number;
9
9
  export default getLimit;
@@ -1,21 +1,20 @@
1
- import { LIMIT, START_TIME } from './constants.js';
2
1
  import lastTimestampFromPricesMap from './last-timestamp-from-prices-map.js';
3
2
  import { timeToDays, timeToHours, timeToMinutes } from './utils.js';
3
+ const START_TIME = new Date('2015-12-09').getTime();
4
4
  const getLimit = ({ granularity, history, requestLimit, requestTimestamp, specificTimestamp, }) => {
5
5
  if (specificTimestamp)
6
6
  return 1;
7
7
  const lastCachedTime = lastTimestampFromPricesMap(history);
8
- const limitFromOptions = requestLimit || LIMIT[granularity];
9
8
  if (granularity === 'minute') {
10
9
  if (!lastCachedTime)
11
- return limitFromOptions;
12
- return Math.ceil(Math.min(limitFromOptions, timeToMinutes(requestTimestamp - lastCachedTime)));
10
+ return requestLimit;
11
+ return Math.ceil(Math.min(requestLimit, timeToMinutes(requestTimestamp - lastCachedTime)));
13
12
  }
14
13
  if (granularity === 'hour') {
15
14
  if (!lastCachedTime)
16
- return limitFromOptions;
17
- return Math.min(limitFromOptions, timeToHours(requestTimestamp - lastCachedTime));
15
+ return requestLimit;
16
+ return Math.min(requestLimit, timeToHours(requestTimestamp - lastCachedTime));
18
17
  }
19
- return Math.min(limitFromOptions, timeToDays(requestTimestamp - (lastCachedTime || START_TIME)));
18
+ return Math.min(requestLimit, timeToDays(requestTimestamp - (lastCachedTime || START_TIME)));
20
19
  };
21
20
  export default getLimit;
@@ -109,7 +109,7 @@ class MarketHistoryMonitorImpl {
109
109
  #runtimeCache = new Map();
110
110
  #marketHistoryAtom;
111
111
  #enabledAssetsAtom;
112
- #granularityRequestLimits = null;
112
+ #granularityRequestLimits;
113
113
  #logger;
114
114
  #abortController = new AbortController();
115
115
  #synchronizedTime;
@@ -138,6 +138,9 @@ class MarketHistoryMonitorImpl {
138
138
  this.#synchronizedTime = synchronizedTime;
139
139
  }
140
140
  #isActive = false;
141
+ #stillActive() {
142
+ return this.#isActive;
143
+ }
141
144
  #setCache = async ({ currency, granularity, pricesByAssetName, }) => {
142
145
  const assetNamesWithChanges = [];
143
146
  const changes = Object.create(null);
@@ -186,7 +189,7 @@ class MarketHistoryMonitorImpl {
186
189
  granularity,
187
190
  assetName: getAssetFromTicker(this.assetsModule.getAssets(), assetTicker).name,
188
191
  });
189
- #fetch = async ({ currency, granularity, assetNames, requestLimit, }) => {
192
+ #fetch = async ({ currency, granularity, assetNames, }) => {
190
193
  const assets = this.assetsModule.getAssets();
191
194
  const assetTickers = assetNames
192
195
  .filter((assetName) => !assets[assetName].isCombined)
@@ -219,7 +222,7 @@ class MarketHistoryMonitorImpl {
219
222
  ignoreCache,
220
223
  runtimeCache: this.#runtimeCache,
221
224
  getRuntimeCacheKey: ({ fiatTicker, granularity, assetTicker, }) => this.#getRuntimeCacheKey({ fiatTicker, granularity, assetTicker }),
222
- requestLimit: requestLimit || this.#granularityRequestLimits[granularity],
225
+ requestLimit: this.#granularityRequestLimits[granularity],
223
226
  });
224
227
  if (fetchedPricesMap.size > 0) {
225
228
  const pricesByAssetNameForCache = mapPricesForCache(fetchedPricesMap, assetNames, assets);
@@ -287,6 +290,34 @@ class MarketHistoryMonitorImpl {
287
290
  this.#logger.error('Market history update all failed', error);
288
291
  }
289
292
  };
293
+ fetchAssets = async (assetNames, granularities = ['day', 'hour', 'minute']) => {
294
+ if (!this.#isActive) {
295
+ const e = new Error('market-history fetchAssets cannot be called before module started');
296
+ this.#errorTracking.track({
297
+ error: e,
298
+ context: { method: 'fetchAssets' },
299
+ });
300
+ throw e;
301
+ }
302
+ if (!this.#currency) {
303
+ const e = new Error('market-history fetchAssets cannot be called before currency is loaded');
304
+ this.#errorTracking.track({
305
+ error: e,
306
+ context: { method: 'fetchAssets' },
307
+ });
308
+ throw e;
309
+ }
310
+ try {
311
+ await this.#updateAssets(assetNames, granularities);
312
+ }
313
+ catch (error) {
314
+ this.#errorTracking.track({
315
+ error,
316
+ context: { method: 'fetchAssets' },
317
+ });
318
+ this.#logger.error('market-history fetchAssets failed', error);
319
+ }
320
+ };
290
321
  #fetchPricesByGranularity = async ({ granularity, assetNames, currency, }) => {
291
322
  const parsedGranularity = parseGranularity(granularity);
292
323
  try {
@@ -307,7 +338,7 @@ class MarketHistoryMonitorImpl {
307
338
  const currency = this.#currency;
308
339
  const promises = granularities.map((granularity) => this.#fetchPricesByGranularity({ granularity, assetNames, currency }));
309
340
  const results = await Promise.all(promises);
310
- if (!this.#isActive) {
341
+ if (!this.#stillActive()) {
311
342
  return;
312
343
  }
313
344
  await this.#marketHistoryAtom.set((current) => {
@@ -330,7 +361,7 @@ class MarketHistoryMonitorImpl {
330
361
  daily: Object.create(null),
331
362
  hourly: Object.create(null),
332
363
  minutely: Object.create(null),
333
- ...current?.data?.[currency],
364
+ ...current?.data[currency],
334
365
  },
335
366
  };
336
367
  granularities.forEach((granularity, index) => {
@@ -502,7 +533,7 @@ class MarketHistoryMonitorImpl {
502
533
  if (hydratedCount === 0)
503
534
  return;
504
535
  await this.#marketHistoryAtom.set((current) => {
505
- if (current?.data?.[currency])
536
+ if (current?.data[currency])
506
537
  return current;
507
538
  return {
508
539
  data: {
@@ -522,28 +553,28 @@ class MarketHistoryMonitorImpl {
522
553
  await this.hydrate().catch((error) => {
523
554
  this.#logger.error('market history hydrate failed', error);
524
555
  });
525
- if (!this.#isActive) {
556
+ if (!this.#stillActive()) {
526
557
  return;
527
558
  }
528
559
  const remoteConfigClearCacheVersion = await this.#remoteConfigClearCacheAtom.get();
529
560
  await this.#invalidateStorage({ remoteConfigClearCacheVersion });
530
- if (!this.#isActive) {
561
+ if (!this.#stillActive()) {
531
562
  return;
532
563
  }
533
564
  this.#currency = await this.#currencyAtom.get();
534
565
  this.#previouslyEnabledAssets = await this.#enabledAssetsAtom.get();
535
- if (!this.#isActive) {
566
+ if (!this.#stillActive()) {
536
567
  return;
537
568
  }
538
569
  await this.#initMarketHistoryAtom();
539
- if (!this.#isActive) {
570
+ if (!this.#stillActive()) {
540
571
  return;
541
572
  }
542
573
  this.#setupTimers();
543
574
  this.#listenRemoteConfigClearCacheVersionChanges();
544
575
  this.#listenCurrencyChanges();
545
576
  this.#listenEnabledAssetsChanges();
546
- if (!this.#isActive) {
577
+ if (!this.#stillActive()) {
547
578
  return;
548
579
  }
549
580
  await this.updateAll();
@@ -616,9 +647,9 @@ class MarketHistoryMonitorImpl {
616
647
  return {
617
648
  data: {
618
649
  [currency]: {
619
- ...current?.data?.[currency],
650
+ ...current?.data[currency],
620
651
  [parsedGranularity]: {
621
- ...current?.data?.[currency]?.[parsedGranularity],
652
+ ...current?.data[currency]?.[parsedGranularity],
622
653
  [assetName]: transformedPrices,
623
654
  },
624
655
  },
@@ -4,7 +4,7 @@ export default function prepareFetchInstructions({ tickerSymbol, fiatTicker, gra
4
4
  fiatTicker: string;
5
5
  granularity: Granularity;
6
6
  getCacheFromStorage: (ticker: string) => Promise<CacheEntry[] | string | undefined>;
7
- requestLimit?: number;
7
+ requestLimit: number;
8
8
  specificTimestamp?: number | null;
9
9
  ignoreCache: boolean;
10
10
  runtimeCache: Map<string, CacheEntry[]>;
@@ -8,5 +8,5 @@ export default function processApiResponse({ assetTicker, fiatTicker, fetchedPri
8
8
  historicalPricesMap: Map<string, Map<number, PriceData>>;
9
9
  granularity: Granularity;
10
10
  specificTimestamp?: number | null;
11
- requestLimit?: number;
11
+ requestLimit: number;
12
12
  }): Map<number, PriceData> | null;
@@ -7,7 +7,7 @@ type MarketHistoryMonitor = {
7
7
  };
8
8
  type Dependencies = {
9
9
  marketHistoryMonitor: MarketHistoryMonitor;
10
- marketHistoryAtom: Atom<MarketHistoryState>;
10
+ marketHistoryAtom: Atom<MarketHistoryState | undefined>;
11
11
  appProcessAtom?: ReadonlyAtom<{
12
12
  mode: string;
13
13
  }>;
package/lib/types.d.ts CHANGED
@@ -77,7 +77,7 @@ export type MarketHistoryMonitorDependencies = {
77
77
  clearCacheAtom?: Atom<string | null>;
78
78
  remoteConfigClearCacheAtom?: Atom<string | null>;
79
79
  enabledAssetsAtom: ReadonlyAtom<Record<string, Asset>>;
80
- marketHistoryAtom: Atom<MarketHistoryState>;
80
+ marketHistoryAtom: Atom<MarketHistoryState | undefined>;
81
81
  logger: Logger;
82
82
  config?: {
83
83
  granularityRequestLimits?: Record<Granularity, number>;
@@ -93,6 +93,7 @@ export type MarketHistoryMonitor = {
93
93
  hydrate: () => Promise<void>;
94
94
  update: (granularity: Granularity) => Promise<void>;
95
95
  updateAll: () => Promise<void>;
96
+ fetchAssets: (assetNames: string[], granularities?: Granularity[]) => Promise<void>;
96
97
  fetchAssetPricesFromDate: (params: {
97
98
  assetName: string;
98
99
  granularity: Granularity;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/market-history",
3
- "version": "10.7.0",
3
+ "version": "10.8.0",
4
4
  "description": "Fetches historical prices for assets",
5
5
  "author": "Exodus Movement, Inc.",
6
6
  "license": "MIT",
@@ -17,8 +17,13 @@
17
17
  "main": "lib/index.js",
18
18
  "types": "lib/index.d.ts",
19
19
  "typings": "lib/index.d.ts",
20
+ "exports": {
21
+ ".": "./lib/index.js",
22
+ "./redux": "./lib/redux/index.js"
23
+ },
20
24
  "files": [
21
25
  "lib",
26
+ "redux.js",
22
27
  "CHANGELOG.md",
23
28
  "README.md"
24
29
  ],
@@ -37,14 +42,14 @@
37
42
  "devDependencies": {
38
43
  "@exodus/assets": "^11.0.0",
39
44
  "@exodus/assets-base": "^12.0.0",
40
- "@exodus/assets-feature": "^9.2.2",
45
+ "@exodus/assets-feature": "^9.2.3",
41
46
  "@exodus/bitcoin-meta": "^2.0.0",
42
47
  "@exodus/dependency-types": "^2.1.1",
43
48
  "@exodus/ethereum-meta": "^2.4.1",
44
49
  "@exodus/locale": "^2.7.0",
45
50
  "@exodus/logger": "^1.2.3",
46
- "@exodus/rates-monitor": "^4.14.8",
47
- "@exodus/redux-dependency-injection": "^4.4.0",
51
+ "@exodus/rates-monitor": "^4.15.0",
52
+ "@exodus/redux-dependency-injection": "^5.0.0",
48
53
  "@exodus/storage-memory": "^2.4.0",
49
54
  "events": "^3.3.0",
50
55
  "redux": "^4.0.0"
@@ -61,5 +66,5 @@
61
66
  "access": "public",
62
67
  "provenance": false
63
68
  },
64
- "gitHead": "15750a8f551c1d4d869c6a133f1c5199c6dcfa93"
69
+ "gitHead": "c75365960676b56be5a919b421c0ea801f694908"
65
70
  }
package/redux.js ADDED
@@ -0,0 +1,2 @@
1
+ // eslint-disable-next-line import/no-unresolved -- shim re-exports built artifact
2
+ export { default } from './lib/redux/index.js'
@@ -1,2 +0,0 @@
1
- export declare const LIMIT: Record<string, number>;
2
- export declare const START_TIME: number;
@@ -1,9 +0,0 @@
1
- const HOURLY_LIMIT = 24 * 7;
2
- const DAILY_LIMIT = 3000;
3
- const MINUTE_LIMIT = 60 * 2;
4
- export const LIMIT = {
5
- minute: MINUTE_LIMIT,
6
- hour: HOURLY_LIMIT,
7
- day: DAILY_LIMIT,
8
- };
9
- export const START_TIME = new Date('2015-12-09').getTime();