@drift-labs/common 1.0.14 → 1.0.17

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 (104) hide show
  1. package/lib/clients/DlobWebsocketClient.d.ts +8 -1
  2. package/lib/clients/DlobWebsocketClient.js +29 -9
  3. package/lib/clients/DlobWebsocketClient.js.map +1 -1
  4. package/lib/clients/candleClient.js +12 -0
  5. package/lib/clients/candleClient.js.map +1 -1
  6. package/lib/clients/redisClient.d.ts +1 -0
  7. package/lib/clients/swiftClient.d.ts +1 -0
  8. package/lib/clients/swiftClient.js +19 -5
  9. package/lib/clients/swiftClient.js.map +1 -1
  10. package/lib/clients/tvFeed.d.ts +4 -1
  11. package/lib/clients/tvFeed.js +29 -3
  12. package/lib/clients/tvFeed.js.map +1 -1
  13. package/lib/common-ui-utils/commonUiUtils.d.ts +22 -3
  14. package/lib/common-ui-utils/commonUiUtils.js +53 -11
  15. package/lib/common-ui-utils/commonUiUtils.js.map +1 -1
  16. package/lib/common-ui-utils/market.d.ts +5 -0
  17. package/lib/common-ui-utils/market.js +19 -11
  18. package/lib/common-ui-utils/market.js.map +1 -1
  19. package/lib/common-ui-utils/order.d.ts +7 -1
  20. package/lib/common-ui-utils/order.js +9 -9
  21. package/lib/common-ui-utils/order.js.map +1 -1
  22. package/lib/common-ui-utils/user.d.ts +5 -0
  23. package/lib/common-ui-utils/user.js +57 -0
  24. package/lib/common-ui-utils/user.js.map +1 -1
  25. package/lib/constants/markets.d.ts +4 -0
  26. package/lib/constants/markets.js +5 -1
  27. package/lib/constants/markets.js.map +1 -1
  28. package/lib/constants/superstake.js.map +1 -1
  29. package/lib/drift/Drift/clients/AuthorityDrift/DriftL2OrderbookManager.d.ts +1 -1
  30. package/lib/drift/Drift/clients/AuthorityDrift/DriftL2OrderbookManager.js.map +1 -1
  31. package/lib/drift/Drift/clients/AuthorityDrift/DriftOperations/index.js +6 -0
  32. package/lib/drift/Drift/clients/AuthorityDrift/DriftOperations/index.js.map +1 -1
  33. package/lib/drift/Drift/clients/AuthorityDrift/DriftOperations/types.d.ts +1 -0
  34. package/lib/drift/Drift/clients/AuthorityDrift/DriftOperations/types.js.map +1 -1
  35. package/lib/drift/Drift/clients/AuthorityDrift/index.d.ts +1 -1
  36. package/lib/drift/Drift/clients/AuthorityDrift/index.js.map +1 -1
  37. package/lib/drift/Drift/clients/CentralServerDrift/index.d.ts +19 -2
  38. package/lib/drift/Drift/clients/CentralServerDrift/index.js +102 -64
  39. package/lib/drift/Drift/clients/CentralServerDrift/index.js.map +1 -1
  40. package/lib/drift/Drift/data/PollingDlob.d.ts +1 -1
  41. package/lib/drift/Drift/data/PollingDlob.js.map +1 -1
  42. package/lib/drift/base/actions/perp/settlePnl.d.ts +4 -3
  43. package/lib/drift/base/actions/perp/settlePnl.js +6 -3
  44. package/lib/drift/base/actions/perp/settlePnl.js.map +1 -1
  45. package/lib/drift/base/actions/spot/deposit.d.ts +10 -3
  46. package/lib/drift/base/actions/spot/deposit.js +22 -7
  47. package/lib/drift/base/actions/spot/deposit.js.map +1 -1
  48. package/lib/drift/base/actions/trade/editOrder.d.ts +5 -0
  49. package/lib/drift/base/actions/trade/editOrder.js +1 -0
  50. package/lib/drift/base/actions/trade/editOrder.js.map +1 -1
  51. package/lib/drift/base/actions/trade/index.d.ts +1 -0
  52. package/lib/drift/base/actions/trade/index.js +1 -0
  53. package/lib/drift/base/actions/trade/index.js.map +1 -1
  54. package/lib/drift/base/actions/trade/margin.d.ts +24 -0
  55. package/lib/drift/base/actions/trade/margin.js +48 -0
  56. package/lib/drift/base/actions/trade/margin.js.map +1 -0
  57. package/lib/drift/base/actions/trade/openPerpOrder/auction.d.ts +6 -1
  58. package/lib/drift/base/actions/trade/openPerpOrder/auction.js +4 -4
  59. package/lib/drift/base/actions/trade/openPerpOrder/auction.js.map +1 -1
  60. package/lib/drift/base/actions/trade/openPerpOrder/dlobServer/index.d.ts +8 -5
  61. package/lib/drift/base/actions/trade/openPerpOrder/dlobServer/index.js +6 -5
  62. package/lib/drift/base/actions/trade/openPerpOrder/dlobServer/index.js.map +1 -1
  63. package/lib/drift/base/actions/trade/openPerpOrder/openPerpMarketOrder/index.d.ts +13 -5
  64. package/lib/drift/base/actions/trade/openPerpOrder/openPerpMarketOrder/index.js +18 -15
  65. package/lib/drift/base/actions/trade/openPerpOrder/openPerpMarketOrder/index.js.map +1 -1
  66. package/lib/drift/base/actions/trade/openPerpOrder/openPerpNonMarketOrder/index.d.ts +3 -4
  67. package/lib/drift/base/actions/trade/openPerpOrder/openPerpNonMarketOrder/index.js +11 -11
  68. package/lib/drift/base/actions/trade/openPerpOrder/openPerpNonMarketOrder/index.js.map +1 -1
  69. package/lib/drift/base/actions/trade/openPerpOrder/openSwiftOrder/index.d.ts +40 -11
  70. package/lib/drift/base/actions/trade/openPerpOrder/openSwiftOrder/index.js +49 -15
  71. package/lib/drift/base/actions/trade/openPerpOrder/openSwiftOrder/index.js.map +1 -1
  72. package/lib/drift/base/actions/trade/openPerpOrder/types.d.ts +5 -3
  73. package/lib/drift/base/actions/trade/openPerpOrder/types.js.map +1 -1
  74. package/lib/drift/base/actions/trade/swap.d.ts +18 -11
  75. package/lib/drift/base/actions/trade/swap.js +40 -17
  76. package/lib/drift/base/actions/trade/swap.js.map +1 -1
  77. package/lib/drift/base/actions/user/create.d.ts +3 -1
  78. package/lib/drift/base/actions/user/create.js +21 -7
  79. package/lib/drift/base/actions/user/create.js.map +1 -1
  80. package/lib/drift/cli.js +16 -2
  81. package/lib/drift/cli.js.map +1 -1
  82. package/lib/drift/utils/auctionParamsResponseMapper.d.ts +2 -1
  83. package/lib/drift/utils/auctionParamsResponseMapper.js +1 -1
  84. package/lib/drift/utils/auctionParamsResponseMapper.js.map +1 -1
  85. package/lib/drift/utils/orderParams.js +1 -1
  86. package/lib/drift/utils/orderParams.js.map +1 -1
  87. package/lib/utils/driftEvents.js +18 -2
  88. package/lib/utils/driftEvents.js.map +1 -1
  89. package/lib/utils/index.d.ts +1 -0
  90. package/lib/utils/logger.js +34 -3
  91. package/lib/utils/logger.js.map +1 -1
  92. package/lib/utils/orderbook/constants.d.ts +2 -0
  93. package/lib/utils/orderbook/constants.js +5 -0
  94. package/lib/utils/orderbook/constants.js.map +1 -0
  95. package/lib/utils/orderbook/index.d.ts +38 -59
  96. package/lib/utils/orderbook/index.js +285 -1
  97. package/lib/utils/orderbook/index.js.map +1 -1
  98. package/lib/utils/orderbook/types.d.ts +101 -0
  99. package/lib/utils/orderbook/types.js +9 -0
  100. package/lib/utils/orderbook/types.js.map +1 -0
  101. package/lib/utils/signedMsgs.d.ts +1 -1
  102. package/lib/utils/signedMsgs.js +2 -2
  103. package/lib/utils/signedMsgs.js.map +1 -1
  104. package/package.json +3 -3
@@ -1,6 +1,7 @@
1
- import { MarketId, RawL2Output, deserializeL2Response, OrderbookGrouping, DlobServerChannel } from '../index';
1
+ import { MarketId, deserializeL2Response, OrderbookGrouping, DlobServerChannel } from '..';
2
2
  import { Observable } from 'rxjs';
3
3
  import { ResultSlotIncrementer } from '../utils/ResultSlotIncrementer';
4
+ import { RawL2Output } from '../utils/orderbook/types';
4
5
  export type OrderbookChannelTypes = Extract<DlobServerChannel, 'orderbook' | 'orderbook_indicative'>;
5
6
  export interface DlobWebsocketClientConfig {
6
7
  websocketUrl: string;
@@ -24,6 +25,8 @@ export declare class DlobWebsocketClient {
24
25
  private subscriptions;
25
26
  private resultIncrementer;
26
27
  private destroy$;
28
+ private static readonly stringCache;
29
+ private static readonly maxCacheSize;
27
30
  private marketSubscriptions$;
28
31
  private rawMessages$;
29
32
  constructor(config: DlobWebsocketClientConfig);
@@ -50,6 +53,10 @@ export declare class DlobWebsocketClient {
50
53
  * Reset slot tracking for clean state on reconnection
51
54
  */
52
55
  resetSlotTracking(): void;
56
+ /**
57
+ * Cache frequently used strings to reduce memory allocation
58
+ */
59
+ private getCachedString;
53
60
  /**
54
61
  * Destroy the client and clean up all subscriptions
55
62
  */
@@ -2,7 +2,7 @@
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.DlobWebsocketClient = void 0;
5
- const index_1 = require("../index");
5
+ const __1 = require("..");
6
6
  const rxjs_1 = require("rxjs");
7
7
  const operators_1 = require("rxjs/operators");
8
8
  const ResultSlotIncrementer_1 = require("../utils/ResultSlotIncrementer");
@@ -61,10 +61,26 @@ class DlobWebsocketClient {
61
61
  for (const subscriptionKey of this.subscriptions.keys()) {
62
62
  // Extract marketId and channel from subscription key
63
63
  const [marketKey, channel] = subscriptionKey.split('_');
64
- const resultKey = `${channel}_${marketKey}`;
64
+ const resultKey = this.getCachedString(`${channel}_${marketKey}`, 'result');
65
65
  this.resultIncrementer.resetKey(resultKey);
66
66
  }
67
67
  }
68
+ /**
69
+ * Cache frequently used strings to reduce memory allocation
70
+ */
71
+ getCachedString(baseString, type) {
72
+ const cacheKey = `${type}:${baseString}`;
73
+ if (DlobWebsocketClient.stringCache.has(cacheKey)) {
74
+ return DlobWebsocketClient.stringCache.get(cacheKey);
75
+ }
76
+ // If cache is getting too large, clear oldest entries
77
+ if (DlobWebsocketClient.stringCache.size >= DlobWebsocketClient.maxCacheSize) {
78
+ const firstKey = DlobWebsocketClient.stringCache.keys().next().value;
79
+ DlobWebsocketClient.stringCache.delete(firstKey);
80
+ }
81
+ DlobWebsocketClient.stringCache.set(cacheKey, baseString);
82
+ return baseString;
83
+ }
68
84
  /**
69
85
  * Destroy the client and clean up all subscriptions
70
86
  */
@@ -135,13 +151,13 @@ class DlobWebsocketClient {
135
151
  const { unsubscribe } = MultiplexWebSocket_1.MultiplexWebSocket.createWebSocketSubscription({
136
152
  wsUrl: this.config.websocketUrl,
137
153
  enableHeartbeatMonitoring: true,
138
- subscriptionId: `${this.config.websocketUrl}_dlob_liquidity_${marketId.key}`,
139
- subscribeMessage: JSON.stringify(index_1.DLOB_SERVER_WEBSOCKET_UTILS.getSubscriptionProps({
154
+ subscriptionId: this.getCachedString(`${this.config.websocketUrl}_dlob_liquidity_${marketId.key}`, 'subscription'),
155
+ subscribeMessage: JSON.stringify(__1.DLOB_SERVER_WEBSOCKET_UTILS.getSubscriptionProps({
140
156
  type: channel,
141
157
  market: marketId,
142
158
  grouping,
143
159
  })),
144
- unsubscribeMessage: JSON.stringify(index_1.DLOB_SERVER_WEBSOCKET_UTILS.getUnsubscriptionProps({
160
+ unsubscribeMessage: JSON.stringify(__1.DLOB_SERVER_WEBSOCKET_UTILS.getUnsubscriptionProps({
145
161
  type: channel,
146
162
  market: marketId,
147
163
  grouping,
@@ -154,7 +170,7 @@ class DlobWebsocketClient {
154
170
  });
155
171
  },
156
172
  messageFilter: (message) => {
157
- return index_1.DLOB_SERVER_WEBSOCKET_UTILS.getMessageFilter({
173
+ return __1.DLOB_SERVER_WEBSOCKET_UTILS.getMessageFilter({
158
174
  type: channel,
159
175
  market: marketId,
160
176
  grouping,
@@ -183,13 +199,13 @@ class DlobWebsocketClient {
183
199
  var _a, _b;
184
200
  try {
185
201
  const parsed = this.tryParse(data);
186
- const resultKey = `${channel}_${marketId.key}`;
202
+ const resultKey = this.getCachedString(`${channel}_${marketId.key}`, 'result');
187
203
  const messageTimestamp = Date.now(); // Capture when we received this message
188
204
  const validResult = this.resultIncrementer.handleResult(resultKey, (_a = parsed.slot) !== null && _a !== void 0 ? _a : 0, messageTimestamp);
189
205
  if (!validResult) {
190
206
  return null; // Skip results which aren't slot-increasing or are filtered due to tab return
191
207
  }
192
- const deserializedData = (0, index_1.deserializeL2Response)(parsed);
208
+ const deserializedData = (0, __1.deserializeL2Response)(parsed);
193
209
  return {
194
210
  marketId,
195
211
  rawData: parsed,
@@ -218,9 +234,13 @@ class DlobWebsocketClient {
218
234
  }
219
235
  getSubscriptionKey(subscription) {
220
236
  const { marketId, channel, grouping } = subscription;
221
- return `${marketId.key}_${channel}${grouping ? `_${grouping}` : ''}`;
237
+ const baseKey = `${marketId.key}_${channel}${grouping ? `_${grouping}` : ''}`;
238
+ return this.getCachedString(baseKey, 'subscription_key');
222
239
  }
223
240
  }
224
241
  exports.DlobWebsocketClient = DlobWebsocketClient;
242
+ // String caches to prevent repeated string concatenation
243
+ DlobWebsocketClient.stringCache = new Map();
244
+ DlobWebsocketClient.maxCacheSize = 1000;
225
245
  exports.default = DlobWebsocketClient;
226
246
  //# sourceMappingURL=DlobWebsocketClient.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DlobWebsocketClient.js","sourceRoot":"","sources":["../../src/clients/DlobWebsocketClient.ts"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport {\n\tMarketId,\n\tRawL2Output,\n\tdeserializeL2Response,\n\tOrderbookGrouping,\n\tDLOB_SERVER_WEBSOCKET_UTILS,\n\tDlobServerChannel,\n} from '../index';\nimport { Observable, Subject, BehaviorSubject, EMPTY } from 'rxjs';\nimport {\n\tmap,\n\tfilter,\n\tcatchError,\n\ttakeUntil,\n\tshare,\n\tdistinctUntilChanged,\n\tswitchMap,\n} from 'rxjs/operators';\nimport { ResultSlotIncrementer } from '../utils/ResultSlotIncrementer';\nimport { MultiplexWebSocket } from '../utils/MultiplexWebSocket';\n\nexport type OrderbookChannelTypes = Extract<\n\tDlobServerChannel,\n\t'orderbook' | 'orderbook_indicative'\n>;\n\nexport interface DlobWebsocketClientConfig {\n\twebsocketUrl: string;\n\tenableIndicativeOrderbook?: boolean;\n\tresultSlotIncrementer?: ResultSlotIncrementer;\n\tonFallback?: (marketId: MarketId) => void;\n}\n\nexport interface MarketSubscription {\n\tmarketId: MarketId;\n\tchannel: OrderbookChannelTypes;\n\tgrouping?: OrderbookGrouping;\n}\n\nexport interface ProcessedMarketData {\n\tmarketId: MarketId;\n\trawData: RawL2Output;\n\tdeserializedData: ReturnType<typeof deserializeL2Response>;\n\tslot: number;\n}\n\nexport class DlobWebsocketClient {\n\tprivate config: DlobWebsocketClientConfig;\n\tprivate subscriptions = new Map<string, { unsubscribe: () => void }>();\n\tprivate resultIncrementer: ResultSlotIncrementer;\n\tprivate destroy$ = new Subject<void>();\n\n\t// Subjects for reactive streams\n\tprivate marketSubscriptions$ = new BehaviorSubject<MarketSubscription[]>([]);\n\tprivate rawMessages$ = new Subject<{\n\t\tmarketId: MarketId;\n\t\tchannel: string;\n\t\tdata: string;\n\t}>();\n\n\tconstructor(config: DlobWebsocketClientConfig) {\n\t\tthis.config = config;\n\t\tthis.resultIncrementer =\n\t\t\tconfig.resultSlotIncrementer || new ResultSlotIncrementer();\n\n\t\tthis.setupSubscriptionManagement();\n\t}\n\n\t/**\n\t * Get an observable stream of processed market data for specific markets\n\t */\n\tgetMarketDataStream(marketIds: MarketId[]): Observable<ProcessedMarketData> {\n\t\treturn this.rawMessages$.pipe(\n\t\t\tfilter(({ marketId }) => marketIds.some((id) => id.key === marketId.key)),\n\t\t\tmap(({ marketId, channel, data }) =>\n\t\t\t\tthis.processRawMessage(marketId, channel, data)\n\t\t\t),\n\t\t\tfilter((result): result is ProcessedMarketData => result !== null),\n\t\t\tcatchError((error) => {\n\t\t\t\tconsole.error('Caught error in getMarketDataStream', error);\n\t\t\t\treturn EMPTY;\n\t\t\t}),\n\t\t\ttakeUntil(this.destroy$),\n\t\t\tshare()\n\t\t);\n\t}\n\n\t/**\n\t * Subscribe to market data for given markets\n\t */\n\tsubscribeToMarkets(\n\t\tmarkets: {\n\t\t\tmarketId: MarketId;\n\t\t\tgrouping?: OrderbookGrouping;\n\t\t}[]\n\t): void {\n\t\tconst subscriptions: MarketSubscription[] = markets.map(\n\t\t\t({ marketId, grouping }) => ({\n\t\t\t\tmarketId,\n\t\t\t\tchannel: this.config.enableIndicativeOrderbook\n\t\t\t\t\t? 'orderbook_indicative'\n\t\t\t\t\t: 'orderbook',\n\t\t\t\tgrouping,\n\t\t\t})\n\t\t);\n\n\t\tthis.marketSubscriptions$.next(subscriptions);\n\t}\n\n\t/**\n\t * Unsubscribe from all markets\n\t */\n\tunsubscribeAll(): void {\n\t\tthis.marketSubscriptions$.next([]);\n\t}\n\n\t/**\n\t * Handle tab return to prevent \"speed run\" through queued messages\n\t */\n\thandleTabReturn(): void {\n\t\tthis.resultIncrementer.handleTabReturn();\n\t}\n\n\t/**\n\t * Reset slot tracking for clean state on reconnection\n\t */\n\tresetSlotTracking(): void {\n\t\t// Get all current subscription keys and reset their slot tracking\n\t\tfor (const subscriptionKey of this.subscriptions.keys()) {\n\t\t\t// Extract marketId and channel from subscription key\n\t\t\tconst [marketKey, channel] = subscriptionKey.split('_');\n\t\t\tconst resultKey = `${channel}_${marketKey}`;\n\t\t\tthis.resultIncrementer.resetKey(resultKey);\n\t\t}\n\t}\n\n\t/**\n\t * Destroy the client and clean up all subscriptions\n\t */\n\tdestroy(): void {\n\t\tthis.destroy$.next();\n\t\tthis.destroy$.complete();\n\t\tthis.subscriptions.forEach(({ unsubscribe }) => unsubscribe());\n\t\tthis.subscriptions.clear();\n\t}\n\n\t/**\n\t * Sets up the subscription management pipeline that handles market data subscriptions.\n\t * This pipeline:\n\t * 1. Watches for changes in market subscriptions\n\t * 2. Only processes changes when the subscription list actually changes\n\t * 3. Manages the lifecycle of websocket subscriptions\n\t * 4. Cleans up when the client is destroyed\n\t */\n\tprivate setupSubscriptionManagement(): void {\n\t\tthis.marketSubscriptions$\n\t\t\t.pipe(\n\t\t\t\t// Only emit when the subscription list actually changes\n\t\t\t\t// Uses JSON.stringify for deep comparison of subscription arrays\n\t\t\t\tdistinctUntilChanged(\n\t\t\t\t\t(prev, curr) => JSON.stringify(prev) === JSON.stringify(curr)\n\t\t\t\t),\n\t\t\t\t// Switch to managing the new set of subscriptions\n\t\t\t\t// This will cancel any previous subscription management\n\t\t\t\tswitchMap((subscriptions) =>\n\t\t\t\t\tthis.manageNewSubscriptions(subscriptions)\n\t\t\t\t),\n\t\t\t\t// Clean up when the client is destroyed\n\t\t\t\ttakeUntil(this.destroy$)\n\t\t\t)\n\t\t\t.subscribe();\n\t}\n\n\t/**\n\t * Manages the lifecycle of websocket subscriptions by:\n\t * 1. Comparing current subscriptions with new subscriptions\n\t * 2. Unsubscribing from any subscriptions that are no longer needed\n\t * 3. Creating new subscriptions for markets that weren't previously subscribed\n\t *\n\t * @param newSubscriptions - The new set of market subscriptions to maintain\n\t * @returns An empty observable since this is a side-effect operation\n\t */\n\tprivate manageNewSubscriptions(\n\t\tnewSubscriptions: MarketSubscription[]\n\t): Observable<never> {\n\t\t// Get sets of subscription keys for efficient comparison\n\t\tconst currentKeys = new Set(this.subscriptions.keys());\n\t\tconst newKeys = new Set(\n\t\t\tnewSubscriptions.map((sub) => this.getSubscriptionKey(sub))\n\t\t);\n\n\t\t// Unsubscribe from removed subscriptions\n\t\tfor (const key of currentKeys) {\n\t\t\tif (!newKeys.has(key)) {\n\t\t\t\tconst subscription = this.subscriptions.get(key);\n\t\t\t\tif (subscription) {\n\t\t\t\t\tsubscription.unsubscribe();\n\t\t\t\t\tthis.subscriptions.delete(key);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Subscribe to new subscriptions\n\t\tfor (const subscription of newSubscriptions) {\n\t\t\tconst key = this.getSubscriptionKey(subscription);\n\t\t\tif (!currentKeys.has(key)) {\n\t\t\t\tthis.createSubscription(subscription);\n\t\t\t}\n\t\t}\n\n\t\treturn EMPTY;\n\t}\n\n\tprivate createSubscription(subscription: MarketSubscription): void {\n\t\tconst { marketId, channel, grouping } = subscription;\n\t\tconst subscriptionKey = this.getSubscriptionKey(subscription);\n\n\t\ttry {\n\t\t\tconst { unsubscribe } = MultiplexWebSocket.createWebSocketSubscription<{\n\t\t\t\tchannel: string;\n\t\t\t\tdata: string;\n\t\t\t}>({\n\t\t\t\twsUrl: this.config.websocketUrl,\n\t\t\t\tenableHeartbeatMonitoring: true,\n\t\t\t\tsubscriptionId: `${this.config.websocketUrl}_dlob_liquidity_${marketId.key}`,\n\t\t\t\tsubscribeMessage: JSON.stringify(\n\t\t\t\t\tDLOB_SERVER_WEBSOCKET_UTILS.getSubscriptionProps({\n\t\t\t\t\t\ttype: channel,\n\t\t\t\t\t\tmarket: marketId,\n\t\t\t\t\t\tgrouping,\n\t\t\t\t\t})\n\t\t\t\t),\n\t\t\t\tunsubscribeMessage: JSON.stringify(\n\t\t\t\t\tDLOB_SERVER_WEBSOCKET_UTILS.getUnsubscriptionProps({\n\t\t\t\t\t\ttype: channel,\n\t\t\t\t\t\tmarket: marketId,\n\t\t\t\t\t\tgrouping,\n\t\t\t\t\t})\n\t\t\t\t),\n\t\t\t\tonMessage: (message: { channel: string; data: string }) => {\n\t\t\t\t\tthis.rawMessages$.next({\n\t\t\t\t\t\tmarketId,\n\t\t\t\t\t\tchannel: message.channel,\n\t\t\t\t\t\tdata: message.data,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\tmessageFilter: (message: { channel: string; data: string }) => {\n\t\t\t\t\treturn DLOB_SERVER_WEBSOCKET_UTILS.getMessageFilter({\n\t\t\t\t\t\ttype: channel,\n\t\t\t\t\t\tmarket: marketId,\n\t\t\t\t\t\tgrouping,\n\t\t\t\t\t})(message);\n\t\t\t\t},\n\t\t\t\tonError: (error?: any) => {\n\t\t\t\t\tconsole.error('Caught error in createSubscription', error);\n\t\t\t\t\tthis.config.onFallback?.(marketId);\n\t\t\t\t},\n\t\t\t\terrorMessageFilter: (message?: any) => {\n\t\t\t\t\tif (message?.error) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tthis.subscriptions.set(subscriptionKey, { unsubscribe });\n\t\t} catch (error) {\n\t\t\tconsole.error('Failed to create subscription:', error);\n\t\t\tthis.config.onFallback?.(marketId);\n\t\t}\n\t}\n\n\tprivate processRawMessage(\n\t\tmarketId: MarketId,\n\t\tchannel: string,\n\t\tdata: string\n\t): ProcessedMarketData | null {\n\t\ttry {\n\t\t\tconst parsed = this.tryParse(data) as RawL2Output;\n\t\t\tconst resultKey = `${channel}_${marketId.key}`;\n\t\t\tconst messageTimestamp = Date.now(); // Capture when we received this message\n\n\t\t\tconst validResult = this.resultIncrementer.handleResult(\n\t\t\t\tresultKey,\n\t\t\t\tparsed.slot ?? 0,\n\t\t\t\tmessageTimestamp\n\t\t\t);\n\n\t\t\tif (!validResult) {\n\t\t\t\treturn null; // Skip results which aren't slot-increasing or are filtered due to tab return\n\t\t\t}\n\n\t\t\tconst deserializedData = deserializeL2Response(parsed);\n\n\t\t\treturn {\n\t\t\t\tmarketId,\n\t\t\t\trawData: parsed,\n\t\t\t\tdeserializedData,\n\t\t\t\tslot: parsed.slot ?? 0,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate tryParse(data: unknown): unknown {\n\t\ttry {\n\t\t\treturn JSON.parse(data as string, (key, value) => {\n\t\t\t\t// If the value is a number and it's too large to be safely represented as a JavaScript number,\n\t\t\t\t// convert it to a string to prevent precision loss\n\t\t\t\tif (typeof value === 'number' && !Number.isSafeInteger(value)) {\n\t\t\t\t\treturn value.toString();\n\t\t\t\t}\n\t\t\t\treturn value;\n\t\t\t});\n\t\t} catch (e) {\n\t\t\treturn data;\n\t\t}\n\t}\n\n\tprivate getSubscriptionKey(subscription: MarketSubscription): string {\n\t\tconst { marketId, channel, grouping } = subscription;\n\t\treturn `${marketId.key}_${channel}${grouping ? `_${grouping}` : ''}`;\n\t}\n}\n\nexport default DlobWebsocketClient;\n"]}
1
+ {"version":3,"file":"DlobWebsocketClient.js","sourceRoot":"","sources":["../../src/clients/DlobWebsocketClient.ts"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport {\n\tMarketId,\n\tdeserializeL2Response,\n\tOrderbookGrouping,\n\tDLOB_SERVER_WEBSOCKET_UTILS,\n\tDlobServerChannel,\n} from '..';\nimport { Observable, Subject, BehaviorSubject, EMPTY } from 'rxjs';\nimport {\n\tmap,\n\tfilter,\n\tcatchError,\n\ttakeUntil,\n\tshare,\n\tdistinctUntilChanged,\n\tswitchMap,\n} from 'rxjs/operators';\nimport { ResultSlotIncrementer } from '../utils/ResultSlotIncrementer';\nimport { MultiplexWebSocket } from '../utils/MultiplexWebSocket';\nimport { RawL2Output } from '../utils/orderbook/types';\n\nexport type OrderbookChannelTypes = Extract<\n\tDlobServerChannel,\n\t'orderbook' | 'orderbook_indicative'\n>;\n\nexport interface DlobWebsocketClientConfig {\n\twebsocketUrl: string;\n\tenableIndicativeOrderbook?: boolean;\n\tresultSlotIncrementer?: ResultSlotIncrementer;\n\tonFallback?: (marketId: MarketId) => void;\n}\n\nexport interface MarketSubscription {\n\tmarketId: MarketId;\n\tchannel: OrderbookChannelTypes;\n\tgrouping?: OrderbookGrouping;\n}\n\nexport interface ProcessedMarketData {\n\tmarketId: MarketId;\n\trawData: RawL2Output;\n\tdeserializedData: ReturnType<typeof deserializeL2Response>;\n\tslot: number;\n}\n\nexport class DlobWebsocketClient {\n\tprivate config: DlobWebsocketClientConfig;\n\tprivate subscriptions = new Map<string, { unsubscribe: () => void }>();\n\tprivate resultIncrementer: ResultSlotIncrementer;\n\tprivate destroy$ = new Subject<void>();\n\n\t// String caches to prevent repeated string concatenation\n\tprivate static readonly stringCache = new Map<string, string>();\n\tprivate static readonly maxCacheSize = 1000;\n\n\t// Subjects for reactive streams\n\tprivate marketSubscriptions$ = new BehaviorSubject<MarketSubscription[]>([]);\n\tprivate rawMessages$ = new Subject<{\n\t\tmarketId: MarketId;\n\t\tchannel: string;\n\t\tdata: string;\n\t}>();\n\n\tconstructor(config: DlobWebsocketClientConfig) {\n\t\tthis.config = config;\n\t\tthis.resultIncrementer =\n\t\t\tconfig.resultSlotIncrementer || new ResultSlotIncrementer();\n\n\t\tthis.setupSubscriptionManagement();\n\t}\n\n\t/**\n\t * Get an observable stream of processed market data for specific markets\n\t */\n\tgetMarketDataStream(marketIds: MarketId[]): Observable<ProcessedMarketData> {\n\t\treturn this.rawMessages$.pipe(\n\t\t\tfilter(({ marketId }) => marketIds.some((id) => id.key === marketId.key)),\n\t\t\tmap(({ marketId, channel, data }) =>\n\t\t\t\tthis.processRawMessage(marketId, channel, data)\n\t\t\t),\n\t\t\tfilter((result): result is ProcessedMarketData => result !== null),\n\t\t\tcatchError((error) => {\n\t\t\t\tconsole.error('Caught error in getMarketDataStream', error);\n\t\t\t\treturn EMPTY;\n\t\t\t}),\n\t\t\ttakeUntil(this.destroy$),\n\t\t\tshare()\n\t\t);\n\t}\n\n\t/**\n\t * Subscribe to market data for given markets\n\t */\n\tsubscribeToMarkets(\n\t\tmarkets: {\n\t\t\tmarketId: MarketId;\n\t\t\tgrouping?: OrderbookGrouping;\n\t\t}[]\n\t): void {\n\t\tconst subscriptions: MarketSubscription[] = markets.map(\n\t\t\t({ marketId, grouping }) => ({\n\t\t\t\tmarketId,\n\t\t\t\tchannel: this.config.enableIndicativeOrderbook\n\t\t\t\t\t? 'orderbook_indicative'\n\t\t\t\t\t: 'orderbook',\n\t\t\t\tgrouping,\n\t\t\t})\n\t\t);\n\n\t\tthis.marketSubscriptions$.next(subscriptions);\n\t}\n\n\t/**\n\t * Unsubscribe from all markets\n\t */\n\tunsubscribeAll(): void {\n\t\tthis.marketSubscriptions$.next([]);\n\t}\n\n\t/**\n\t * Handle tab return to prevent \"speed run\" through queued messages\n\t */\n\thandleTabReturn(): void {\n\t\tthis.resultIncrementer.handleTabReturn();\n\t}\n\n\t/**\n\t * Reset slot tracking for clean state on reconnection\n\t */\n\tresetSlotTracking(): void {\n\t\t// Get all current subscription keys and reset their slot tracking\n\t\tfor (const subscriptionKey of this.subscriptions.keys()) {\n\t\t\t// Extract marketId and channel from subscription key\n\t\t\tconst [marketKey, channel] = subscriptionKey.split('_');\n\t\t\tconst resultKey = this.getCachedString(\n\t\t\t\t`${channel}_${marketKey}`,\n\t\t\t\t'result'\n\t\t\t);\n\t\t\tthis.resultIncrementer.resetKey(resultKey);\n\t\t}\n\t}\n\n\t/**\n\t * Cache frequently used strings to reduce memory allocation\n\t */\n\tprivate getCachedString(baseString: string, type: string): string {\n\t\tconst cacheKey = `${type}:${baseString}`;\n\n\t\tif (DlobWebsocketClient.stringCache.has(cacheKey)) {\n\t\t\treturn DlobWebsocketClient.stringCache.get(cacheKey)!;\n\t\t}\n\n\t\t// If cache is getting too large, clear oldest entries\n\t\tif (\n\t\t\tDlobWebsocketClient.stringCache.size >= DlobWebsocketClient.maxCacheSize\n\t\t) {\n\t\t\tconst firstKey = DlobWebsocketClient.stringCache.keys().next().value;\n\t\t\tDlobWebsocketClient.stringCache.delete(firstKey);\n\t\t}\n\n\t\tDlobWebsocketClient.stringCache.set(cacheKey, baseString);\n\t\treturn baseString;\n\t}\n\n\t/**\n\t * Destroy the client and clean up all subscriptions\n\t */\n\tdestroy(): void {\n\t\tthis.destroy$.next();\n\t\tthis.destroy$.complete();\n\t\tthis.subscriptions.forEach(({ unsubscribe }) => unsubscribe());\n\t\tthis.subscriptions.clear();\n\t}\n\n\t/**\n\t * Sets up the subscription management pipeline that handles market data subscriptions.\n\t * This pipeline:\n\t * 1. Watches for changes in market subscriptions\n\t * 2. Only processes changes when the subscription list actually changes\n\t * 3. Manages the lifecycle of websocket subscriptions\n\t * 4. Cleans up when the client is destroyed\n\t */\n\tprivate setupSubscriptionManagement(): void {\n\t\tthis.marketSubscriptions$\n\t\t\t.pipe(\n\t\t\t\t// Only emit when the subscription list actually changes\n\t\t\t\t// Uses JSON.stringify for deep comparison of subscription arrays\n\t\t\t\tdistinctUntilChanged(\n\t\t\t\t\t(prev, curr) => JSON.stringify(prev) === JSON.stringify(curr)\n\t\t\t\t),\n\t\t\t\t// Switch to managing the new set of subscriptions\n\t\t\t\t// This will cancel any previous subscription management\n\t\t\t\tswitchMap((subscriptions) =>\n\t\t\t\t\tthis.manageNewSubscriptions(subscriptions)\n\t\t\t\t),\n\t\t\t\t// Clean up when the client is destroyed\n\t\t\t\ttakeUntil(this.destroy$)\n\t\t\t)\n\t\t\t.subscribe();\n\t}\n\n\t/**\n\t * Manages the lifecycle of websocket subscriptions by:\n\t * 1. Comparing current subscriptions with new subscriptions\n\t * 2. Unsubscribing from any subscriptions that are no longer needed\n\t * 3. Creating new subscriptions for markets that weren't previously subscribed\n\t *\n\t * @param newSubscriptions - The new set of market subscriptions to maintain\n\t * @returns An empty observable since this is a side-effect operation\n\t */\n\tprivate manageNewSubscriptions(\n\t\tnewSubscriptions: MarketSubscription[]\n\t): Observable<never> {\n\t\t// Get sets of subscription keys for efficient comparison\n\t\tconst currentKeys = new Set(this.subscriptions.keys());\n\t\tconst newKeys = new Set(\n\t\t\tnewSubscriptions.map((sub) => this.getSubscriptionKey(sub))\n\t\t);\n\n\t\t// Unsubscribe from removed subscriptions\n\t\tfor (const key of currentKeys) {\n\t\t\tif (!newKeys.has(key)) {\n\t\t\t\tconst subscription = this.subscriptions.get(key);\n\t\t\t\tif (subscription) {\n\t\t\t\t\tsubscription.unsubscribe();\n\t\t\t\t\tthis.subscriptions.delete(key);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Subscribe to new subscriptions\n\t\tfor (const subscription of newSubscriptions) {\n\t\t\tconst key = this.getSubscriptionKey(subscription);\n\t\t\tif (!currentKeys.has(key)) {\n\t\t\t\tthis.createSubscription(subscription);\n\t\t\t}\n\t\t}\n\n\t\treturn EMPTY;\n\t}\n\n\tprivate createSubscription(subscription: MarketSubscription): void {\n\t\tconst { marketId, channel, grouping } = subscription;\n\t\tconst subscriptionKey = this.getSubscriptionKey(subscription);\n\n\t\ttry {\n\t\t\tconst { unsubscribe } = MultiplexWebSocket.createWebSocketSubscription<{\n\t\t\t\tchannel: string;\n\t\t\t\tdata: string;\n\t\t\t}>({\n\t\t\t\twsUrl: this.config.websocketUrl,\n\t\t\t\tenableHeartbeatMonitoring: true,\n\t\t\t\tsubscriptionId: this.getCachedString(\n\t\t\t\t\t`${this.config.websocketUrl}_dlob_liquidity_${marketId.key}`,\n\t\t\t\t\t'subscription'\n\t\t\t\t),\n\t\t\t\tsubscribeMessage: JSON.stringify(\n\t\t\t\t\tDLOB_SERVER_WEBSOCKET_UTILS.getSubscriptionProps({\n\t\t\t\t\t\ttype: channel,\n\t\t\t\t\t\tmarket: marketId,\n\t\t\t\t\t\tgrouping,\n\t\t\t\t\t})\n\t\t\t\t),\n\t\t\t\tunsubscribeMessage: JSON.stringify(\n\t\t\t\t\tDLOB_SERVER_WEBSOCKET_UTILS.getUnsubscriptionProps({\n\t\t\t\t\t\ttype: channel,\n\t\t\t\t\t\tmarket: marketId,\n\t\t\t\t\t\tgrouping,\n\t\t\t\t\t})\n\t\t\t\t),\n\t\t\t\tonMessage: (message: { channel: string; data: string }) => {\n\t\t\t\t\tthis.rawMessages$.next({\n\t\t\t\t\t\tmarketId,\n\t\t\t\t\t\tchannel: message.channel,\n\t\t\t\t\t\tdata: message.data,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\tmessageFilter: (message: { channel: string; data: string }) => {\n\t\t\t\t\treturn DLOB_SERVER_WEBSOCKET_UTILS.getMessageFilter({\n\t\t\t\t\t\ttype: channel,\n\t\t\t\t\t\tmarket: marketId,\n\t\t\t\t\t\tgrouping,\n\t\t\t\t\t})(message);\n\t\t\t\t},\n\t\t\t\tonError: (error?: any) => {\n\t\t\t\t\tconsole.error('Caught error in createSubscription', error);\n\t\t\t\t\tthis.config.onFallback?.(marketId);\n\t\t\t\t},\n\t\t\t\terrorMessageFilter: (message?: any) => {\n\t\t\t\t\tif (message?.error) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tthis.subscriptions.set(subscriptionKey, { unsubscribe });\n\t\t} catch (error) {\n\t\t\tconsole.error('Failed to create subscription:', error);\n\t\t\tthis.config.onFallback?.(marketId);\n\t\t}\n\t}\n\n\tprivate processRawMessage(\n\t\tmarketId: MarketId,\n\t\tchannel: string,\n\t\tdata: string\n\t): ProcessedMarketData | null {\n\t\ttry {\n\t\t\tconst parsed = this.tryParse(data) as RawL2Output;\n\t\t\tconst resultKey = this.getCachedString(\n\t\t\t\t`${channel}_${marketId.key}`,\n\t\t\t\t'result'\n\t\t\t);\n\t\t\tconst messageTimestamp = Date.now(); // Capture when we received this message\n\n\t\t\tconst validResult = this.resultIncrementer.handleResult(\n\t\t\t\tresultKey,\n\t\t\t\tparsed.slot ?? 0,\n\t\t\t\tmessageTimestamp\n\t\t\t);\n\n\t\t\tif (!validResult) {\n\t\t\t\treturn null; // Skip results which aren't slot-increasing or are filtered due to tab return\n\t\t\t}\n\n\t\t\tconst deserializedData = deserializeL2Response(parsed);\n\n\t\t\treturn {\n\t\t\t\tmarketId,\n\t\t\t\trawData: parsed,\n\t\t\t\tdeserializedData,\n\t\t\t\tslot: parsed.slot ?? 0,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate tryParse(data: unknown): unknown {\n\t\ttry {\n\t\t\treturn JSON.parse(data as string, (key, value) => {\n\t\t\t\t// If the value is a number and it's too large to be safely represented as a JavaScript number,\n\t\t\t\t// convert it to a string to prevent precision loss\n\t\t\t\tif (typeof value === 'number' && !Number.isSafeInteger(value)) {\n\t\t\t\t\treturn value.toString();\n\t\t\t\t}\n\t\t\t\treturn value;\n\t\t\t});\n\t\t} catch (e) {\n\t\t\treturn data;\n\t\t}\n\t}\n\n\tprivate getSubscriptionKey(subscription: MarketSubscription): string {\n\t\tconst { marketId, channel, grouping } = subscription;\n\t\tconst baseKey = `${marketId.key}_${channel}${\n\t\t\tgrouping ? `_${grouping}` : ''\n\t\t}`;\n\t\treturn this.getCachedString(baseKey, 'subscription_key');\n\t}\n}\n\nexport default DlobWebsocketClient;\n"]}
@@ -28,14 +28,26 @@ const getBaseDataApiUrl = (env) => {
28
28
  const dataApiUrl = EnvironmentConstants_1.EnvironmentConstants.dataServerUrl[constantEnv];
29
29
  return dataApiUrl.replace('https://', '');
30
30
  };
31
+ // Cache for URL construction to prevent repeated string concatenation
32
+ const urlCache = new Map();
33
+ const MAX_URL_CACHE_SIZE = 500;
31
34
  const getCandleFetchUrl = ({ env, marketId, resolution, startTs, countToFetch, }) => {
32
35
  const baseDataApiUrl = getBaseDataApiUrl(env);
36
+ // Cache key for this URL configuration
37
+ const cacheKey = `${marketId.key}-${resolution}-${countToFetch}-${env.key}-${startTs !== null && startTs !== void 0 ? startTs : 'none'}`;
38
+ if (urlCache.has(cacheKey)) {
39
+ return urlCache.get(cacheKey);
40
+ }
33
41
  // Base URL without startTs parameter
34
42
  let fetchUrl = `https://${baseDataApiUrl}/market/${getMarketSymbolForMarketId(marketId, env)}/candles/${resolution}?limit=${Math.min(countToFetch, CANDLE_FETCH_LIMIT)}`;
35
43
  // Only add startTs parameter if it's provided
36
44
  if (startTs !== undefined) {
37
45
  fetchUrl += `&startTs=${startTs}`;
38
46
  }
47
+ // Cache the result if cache isn't too large
48
+ if (urlCache.size < MAX_URL_CACHE_SIZE) {
49
+ urlCache.set(cacheKey, fetchUrl);
50
+ }
39
51
  return fetchUrl;
40
52
  };
41
53
  // Separate event bus for candle events
@@ -1 +1 @@
1
- {"version":3,"file":"candleClient.js","sourceRoot":"","sources":["../../src/clients/candleClient.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tCandleResolution,\n\tDevnetPerpMarkets,\n\tDevnetSpotMarkets,\n\tMainnetPerpMarkets,\n\tMainnetSpotMarkets,\n} from '@drift-labs/sdk';\nimport { JsonCandle, MarketId, MarketSymbol } from '../types';\nimport { Candle } from '../utils/candles/Candle';\nimport { StrictEventEmitter } from '../utils/StrictEventEmitter';\nimport { EnvironmentConstants } from '../EnvironmentConstants';\nimport { UIEnv } from '../types/UIEnv';\nimport { assert } from '../utils/assert';\nimport { CandleSubscriberSubscription, MarketDataFeed } from './marketDataFeed';\n\n/**\n * # CANDLE CLIENT HIGH LEVEL EXPLANATION:\n * The Candle Client uses the Data API (see https://data.api.drift.trade/playground) to source candles to display.\n *\n * There are two key parts of the client:\n * - Fetching Candles\n * - Subscribing to Candles\n *\n * ## Fetching Candles:\n * - We can fetch candles between any timestamp range.\n * - The maximum number of candles we can fetch in a single request is 1000 (see CANDLE_FETCH_LIMIT).\n * - We define \"recent history\" to be the last 1000 candles .. basically whatever comes back from the infra when we don't use a startTs and use the maximum fetch limit.\n * - We want to avoid using high cardinality parameters in the fetch because otherwise we will miss the cache in the infra.\n * \t- A concrete example of this is that we don't attach a startTs parameter when we are fetching candles within recent history (past 1000 candles)\n * - We cache the recent candles in memory so that any subsequent fetches within recent history after the first one will be served from cache.\n * \t- e.g. moving back to a further timeframe on TradingView - the required candles could potentially be in the cache, so we don't need to refetch them.\n *\n * ## Subscribing to Candles:\n * - We subscribe to a websocket endpoint for a given market and resolution.\n * - We allow the client to support multiple concurrent subscriptions, because the TradingView comopnent will sometimes do this when switching between markets.\n *\n * ## Possible Improvements:\n * - Create a more advanced cache which can store more than the most recent 1000 candles, dynamically growing as more candles are added (for now seems unnecessary, rare for someone to go back further than 1000 candles)\n */\n\n// Used by the subscriber client to fetch candles from the data API\ntype CandleFetchConfig = {\n\tenv: UIEnv;\n\tmarketId: MarketId;\n\tresolution: CandleResolution;\n\tfromTs: number; // Seconds\n\ttoTs: number; // Seconds\n};\n\n// Used by the subscriber client to subscribe to the candles websocket endpoint\ntype CandleSubscriptionConfig = {\n\tresolution: CandleResolution;\n\tmarketId: MarketId;\n\tenv: UIEnv;\n};\n\n// This is what the client subscriber uses internally to fetch the candles from the data API\ntype CandleFetchUrlConfig = {\n\tenv: UIEnv;\n\tmarketId: MarketId;\n\tresolution: CandleResolution;\n\tstartTs?: number; // Seconds - now optional\n\tcountToFetch: number;\n};\n\ntype CandleFetchResponseJson = {\n\tsuccess: boolean;\n\trecords: JsonCandle[];\n};\n\nconst getMarketSymbolForMarketId = (marketId: MarketId, uiEnv: UIEnv) => {\n\tconst isPerp = marketId.isPerp;\n\n\tconst sdkEnv = uiEnv.sdkEnv;\n\n\tif (isPerp) {\n\t\tconst marketConfigs =\n\t\t\tsdkEnv === 'mainnet-beta' ? MainnetPerpMarkets : DevnetPerpMarkets;\n\t\tconst targetMarketConfig = marketConfigs.find(\n\t\t\t(config) => config.marketIndex === marketId.marketIndex\n\t\t);\n\t\treturn targetMarketConfig.symbol as MarketSymbol;\n\t} else {\n\t\tconst marketConfigs =\n\t\t\tsdkEnv === 'mainnet-beta' ? MainnetSpotMarkets : DevnetSpotMarkets;\n\t\tconst targetMarketConfig = marketConfigs.find(\n\t\t\t(config) => config.marketIndex === marketId.marketIndex\n\t\t);\n\t\treturn targetMarketConfig.symbol as MarketSymbol;\n\t}\n};\n\n// This is the maximum number of candles that can be fetched in a single GET request\nconst CANDLE_FETCH_LIMIT = 1000;\n\nconst getBaseDataApiUrl = (env: UIEnv) => {\n\tconst constantEnv: keyof typeof EnvironmentConstants.dataServerUrl =\n\t\tenv.isStaging ? 'staging' : env.isDevnet ? 'dev' : 'mainnet';\n\tconst dataApiUrl = EnvironmentConstants.dataServerUrl[constantEnv];\n\treturn dataApiUrl.replace('https://', '');\n};\n\nconst getCandleFetchUrl = ({\n\tenv,\n\tmarketId,\n\tresolution,\n\tstartTs,\n\tcountToFetch,\n}: CandleFetchUrlConfig) => {\n\tconst baseDataApiUrl = getBaseDataApiUrl(env);\n\n\t// Base URL without startTs parameter\n\tlet fetchUrl = `https://${baseDataApiUrl}/market/${getMarketSymbolForMarketId(\n\t\tmarketId,\n\t\tenv\n\t)}/candles/${resolution}?limit=${Math.min(countToFetch, CANDLE_FETCH_LIMIT)}`;\n\n\t// Only add startTs parameter if it's provided\n\tif (startTs !== undefined) {\n\t\tfetchUrl += `&startTs=${startTs}`;\n\t}\n\n\treturn fetchUrl;\n};\n\ntype CandleSubscriberEvents = {\n\t'candle-update': JsonCandle;\n};\n\n// Separate event bus for candle events\nclass CandleEventBus extends StrictEventEmitter<CandleSubscriberEvents> {\n\tconstructor() {\n\t\tsuper();\n\t}\n}\n\n// This class is reponsible for fetching candles from the data API's GET endpoint\nclass CandleFetcher {\n\tprivate readonly config: CandleFetchConfig;\n\n\t// Cache for storing recent candles by market ID and resolution\n\tpublic static recentCandlesCache: Map<\n\t\tstring, // key: `${marketId.key}-${resolution}`\n\t\t{\n\t\t\tcandles: JsonCandle[];\n\t\t\tearliestTs: number;\n\t\t\tlatestTs: number;\n\t\t\tfetchTime: number;\n\t\t}\n\t> = new Map();\n\n\t// Helper method to generate a cache key from market ID and resolution\n\tpublic static getCacheKey(\n\t\tmarketId: MarketId,\n\t\tresolution: CandleResolution\n\t): string {\n\t\treturn `${marketId.key}-${resolution}`;\n\t}\n\n\t// Public method to clear the entire cache\n\tpublic static clearWholeCache() {\n\t\tCandleFetcher.recentCandlesCache.clear();\n\t}\n\n\tpublic static clearCacheForSubscription(\n\t\tmarketId: MarketId,\n\t\tresolution: CandleResolution\n\t) {\n\t\tCandleFetcher.recentCandlesCache.delete(\n\t\t\tCandleFetcher.getCacheKey(marketId, resolution)\n\t\t);\n\t}\n\n\tconstructor(config: CandleFetchConfig) {\n\t\tthis.config = config;\n\t}\n\n\t/**\n\t * Candles are fetched in ascending order of time (index 0 -> oldest to index n -> newest)\n\t */\n\tprivate fetchCandlesFromApi = async (\n\t\tfetchUrl: string\n\t): Promise<JsonCandle[]> => {\n\t\tconst response = await fetch(fetchUrl);\n\t\tconst parsedResponse = (await response.json()) as CandleFetchResponseJson;\n\n\t\tif (!parsedResponse.success) {\n\t\t\tthrow new Error('Failed to fetch candles from data API');\n\t\t}\n\n\t\treturn parsedResponse.records;\n\t};\n\n\tprivate getCountOfCandlesBetweenStartAndEndTs = (\n\t\tstartTs: number,\n\t\tendTs: number\n\t) => {\n\t\tconst diffInSeconds = endTs - startTs;\n\t\tconst resolutionInSeconds =\n\t\t\tCandle.resolutionStringToCandleLengthMs(this.config.resolution) / 1000;\n\t\tconst diffInCandles = diffInSeconds / resolutionInSeconds;\n\t\treturn Math.ceil(diffInCandles);\n\t};\n\n\t/**\n\t * Try to get candles from the cache if they're available.\n\t * Returns null if no cached candles are available for the requested range.\n\t */\n\tprivate getFromCache = (): JsonCandle[] | null => {\n\t\t// Generate cache key for the current request\n\t\tconst cacheKey = CandleFetcher.getCacheKey(\n\t\t\tthis.config.marketId,\n\t\t\tthis.config.resolution\n\t\t);\n\t\tconst cachedCandles = CandleFetcher.recentCandlesCache.get(cacheKey);\n\n\t\t// Check if we have cached candles for this market and resolution\n\t\tif (cachedCandles) {\n\t\t\t// Check if the requested time range is within the bounds of cached candles\n\t\t\tif (\n\t\t\t\tthis.config.fromTs >= cachedCandles.earliestTs &&\n\t\t\t\tthis.config.toTs <= cachedCandles.latestTs\n\t\t\t) {\n\t\t\t\t// Filter cached candles to the requested time range\n\t\t\t\tconst filteredCandles = cachedCandles.candles.filter(\n\t\t\t\t\t(candle) =>\n\t\t\t\t\t\tcandle.ts >= this.config.fromTs && candle.ts <= this.config.toTs\n\t\t\t\t);\n\n\t\t\t\treturn filteredCandles;\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t};\n\n\t/**\n\t * Determines if we should use the recent candles approach (without startTs for better caching).\n\t */\n\tprivate isRequestingRecentCandles = (\n\t\tnowSeconds: number,\n\t\tcandleLengthSeconds: number\n\t): boolean => {\n\t\t// Calculate cutoff time for \"recent\" candles (now - 1000 candles worth of time)\n\t\tconst recentCandlesCutoffTs =\n\t\t\tnowSeconds - candleLengthSeconds * CANDLE_FETCH_LIMIT;\n\n\t\t// Check if we're fetching recent candles based on the fromTs\n\t\treturn this.config.fromTs >= recentCandlesCutoffTs;\n\t};\n\n\t/**\n\t * Fetch recent candles without using startTs for better caching.\n\t */\n\tprivate fetchRecentCandles = async (\n\t\tnowSeconds: number\n\t): Promise<JsonCandle[]> => {\n\t\t// Fetch recent candles without specifying startTs\n\t\tconst fetchUrl = getCandleFetchUrl({\n\t\t\tenv: this.config.env,\n\t\t\tmarketId: this.config.marketId,\n\t\t\tresolution: this.config.resolution,\n\t\t\tcountToFetch: CANDLE_FETCH_LIMIT, // Ask for max candles to ensure we get enough\n\t\t});\n\n\t\t// Get the candles and reverse them (into ascending order)\n\t\tconst fetchedCandles = await this.fetchCandlesFromApi(fetchUrl);\n\t\tfetchedCandles.reverse();\n\n\t\tif (fetchedCandles.length === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\t// Store the full fetchedCandles in cache before filtering\n\t\tthis.updateCandleCache(fetchedCandles, nowSeconds);\n\n\t\t// Filter to only include candles in the requested time range\n\t\tconst filteredCandles = this.filterCandlesByTimeRange(fetchedCandles);\n\n\t\treturn filteredCandles;\n\t};\n\n\t/**\n\t * Filter candles to only include those in the requested time range.\n\t */\n\tprivate filterCandlesByTimeRange = (candles: JsonCandle[]): JsonCandle[] => {\n\t\treturn candles.filter(\n\t\t\t(candle) =>\n\t\t\t\tcandle.ts >= this.config.fromTs && candle.ts <= this.config.toTs\n\t\t);\n\t};\n\n\t/**\n\t * Update the candle cache with the latest fetched candles.\n\t */\n\tprivate updateCandleCache = (\n\t\tfetchedCandles: JsonCandle[],\n\t\tnowSeconds: number\n\t): void => {\n\t\tif (fetchedCandles.length > 0) {\n\t\t\t// Generate cache key\n\t\t\tconst cacheKey = CandleFetcher.getCacheKey(\n\t\t\t\tthis.config.marketId,\n\t\t\t\tthis.config.resolution\n\t\t\t);\n\n\t\t\t// Sort candles by timestamp to find earliest and latest\n\t\t\tconst sortedCandles = [...fetchedCandles].sort((a, b) => a.ts - b.ts);\n\t\t\tconst earliestTs = sortedCandles[0].ts;\n\t\t\tconst latestTs = sortedCandles[sortedCandles.length - 1].ts;\n\n\t\t\t// Update or add to cache\n\t\t\tCandleFetcher.recentCandlesCache.set(cacheKey, {\n\t\t\t\tcandles: sortedCandles,\n\t\t\t\tearliestTs,\n\t\t\t\tlatestTs,\n\t\t\t\tfetchTime: nowSeconds,\n\t\t\t});\n\t\t}\n\t};\n\n\t/**\n\t * Fetch historical candles with pagination using startTs.\n\t */\n\tprivate fetchHistoricalCandles = async (): Promise<JsonCandle[]> => {\n\t\tlet candlesRemainingToFetch = this.getCountOfCandlesBetweenStartAndEndTs(\n\t\t\tthis.config.fromTs,\n\t\t\tthis.config.toTs\n\t\t);\n\n\t\tlet currentStartTs = this.config.toTs; // The data API takes \"startTs\" as the \"first timestamp you want going backwards in time\" e.g. all candles will be returned with descending time backwards from the startTs\n\t\tlet hitEndTsCutoff = false;\n\n\t\tlet candles: JsonCandle[] = [];\n\n\t\twhile (candlesRemainingToFetch > 0) {\n\t\t\tconst result = await this.fetchHistoricalCandlesBatch(\n\t\t\t\tcandlesRemainingToFetch,\n\t\t\t\tcurrentStartTs\n\t\t\t);\n\n\t\t\tif (result.fetchedCandles.length === 0) {\n\t\t\t\tcandlesRemainingToFetch = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// the deeper the loop, the older the result.candlesToAdd will be\n\t\t\tcandles = [...result.candlesToAdd, ...candles];\n\t\t\tcandlesRemainingToFetch -= result.candlesToAdd.length;\n\t\t\thitEndTsCutoff = result.hitEndTsCutoff;\n\n\t\t\tif (result.requiresAnotherFetch) {\n\t\t\t\tcurrentStartTs = result.nextStartTs;\n\t\t\t} else if (candlesRemainingToFetch > 0) {\n\t\t\t\t// This means we have fetched all the candles available for this time range and we can stop fetching\n\t\t\t\tcandlesRemainingToFetch = 0;\n\t\t\t}\n\t\t}\n\n\t\tif (hitEndTsCutoff) {\n\t\t\treturn this.filterCandlesByTimeRange(candles);\n\t\t}\n\n\t\treturn candles;\n\t};\n\n\t/**\n\t * Fetch historical candles with pagination, backwards from the toTs value given in the config.\n\t *\n\t * This method works by looping backwards from the LATEST (toTs) timestamp to the OLDEST (fromTs) timestamp.\n\t *\n\t * Things to note:\n\t * - There is a limit to how many candles can be fetched in a single request (see CANDLE_FETCH_LIMIT)\n\t * - We have implemented this to minimise the cardinality in the API request because that helps with caching\n\t */\n\tprivate fetchHistoricalCandlesBatch = async (\n\t\tcandlesRemainingToFetch: number,\n\t\tcurrentStartTs: number\n\t): Promise<{\n\t\tfetchedCandles: JsonCandle[];\n\t\tcandlesToAdd: JsonCandle[];\n\t\thitEndTsCutoff: boolean;\n\t\trequiresAnotherFetch: boolean;\n\t\tnextStartTs: number;\n\t}> => {\n\t\tconst candlesToFetch = Math.min(\n\t\t\tcandlesRemainingToFetch,\n\t\t\tCANDLE_FETCH_LIMIT\n\t\t);\n\n\t\tconst fetchUrl = getCandleFetchUrl({\n\t\t\tenv: this.config.env,\n\t\t\tmarketId: this.config.marketId,\n\t\t\tresolution: this.config.resolution,\n\t\t\tstartTs: currentStartTs, // Include startTs for historical candles\n\t\t\tcountToFetch: candlesToFetch,\n\t\t});\n\n\t\tconst fetchedCandles = await this.fetchCandlesFromApi(fetchUrl);\n\n\t\t// Reverse candles into ascending order\n\t\tfetchedCandles.reverse();\n\n\t\tif (fetchedCandles.length === 0) {\n\t\t\treturn {\n\t\t\t\tfetchedCandles,\n\t\t\t\tcandlesToAdd: [],\n\t\t\t\thitEndTsCutoff: false,\n\t\t\t\trequiresAnotherFetch: false,\n\t\t\t\tnextStartTs: currentStartTs,\n\t\t\t};\n\t\t}\n\n\t\tconst lastCandle = fetchedCandles[fetchedCandles.length - 1]; // This is the LATEST candle .. (they are sorted ascending by time right now)\n\n\t\tconst hitPageSizeCutoff = fetchedCandles.length === CANDLE_FETCH_LIMIT;\n\t\tconst hitEndTsCutoff = lastCandle.ts < this.config.fromTs;\n\n\t\tconst requiresAnotherFetch = hitPageSizeCutoff && !hitEndTsCutoff; // If the number of candles returned is equal to the maximum number of candles that can be fetched in a single GET request, then we need to fetch more candles\n\n\t\tlet candlesToAdd = fetchedCandles;\n\t\tlet nextStartTs = currentStartTs;\n\n\t\tif (requiresAnotherFetch) {\n\t\t\t// If we need to do another fetch, trim any candles with the same timestamp as the last candle in the previous fetch, because that is the pointer for our next fetch and we don't want to duplicate candles\n\t\t\tcandlesToAdd = candlesToAdd.filter((candle) => {\n\t\t\t\treturn candle.ts < lastCandle.ts;\n\t\t\t});\n\n\t\t\tconst oldestCandle = fetchedCandles[0]; // first candle is the oldest\n\t\t\tnextStartTs = oldestCandle.ts; // If we are doing another loop, then the trimmed candles have all the candles except for ones with the last candle's timestamp. For the next loop we want to fetch from that timestamp;\n\t\t}\n\n\t\treturn {\n\t\t\tfetchedCandles,\n\t\t\tcandlesToAdd,\n\t\t\thitEndTsCutoff,\n\t\t\trequiresAnotherFetch,\n\t\t\tnextStartTs,\n\t\t};\n\t};\n\n\t/**\n\t * This class needs to fetch candles based on the config.\n\t *\n\t * If the number of candles requested exceeds the maximum number of candles that can be fetched in a single GET request, then it needs to loop multiple get requests, using the last candle's timestamp as the offset startTs for each subsequent request. If the number of candles returned is less than the requested number of candles, then we have fetched all the candles available.\n\t *\n\t * For recent candles (ones where fromTs > now - candleLength*1000), we avoid using startTs in the URL to improve caching,\n\t * and instead fetch the most recent 1000 candles and then trim the result.\n\t */\n\tpublic fetchCandles = async () => {\n\t\t// Check cache first\n\t\tconst cachedCandles = this.getFromCache();\n\t\tif (cachedCandles) {\n\t\t\treturn cachedCandles;\n\t\t}\n\n\t\t// Calculate the candle length in seconds for the current resolution\n\t\tconst candleLengthMs = Candle.resolutionStringToCandleLengthMs(\n\t\t\tthis.config.resolution\n\t\t);\n\t\tconst candleLengthSeconds = candleLengthMs / 1000;\n\n\t\t// Get current time in seconds\n\t\tconst nowSeconds = Math.floor(Date.now() / 1000);\n\n\t\t// Check if we're fetching recent candles\n\t\tif (this.isRequestingRecentCandles(nowSeconds, candleLengthSeconds)) {\n\t\t\treturn this.fetchRecentCandles(nowSeconds);\n\t\t}\n\n\t\t// For historical candles (older than the last 1000 candles), use the previous approach\n\t\t// with startTs for pagination\n\t\treturn this.fetchHistoricalCandles();\n\t};\n}\n\nclass CandleSubscriber {\n\tprivate subscription: CandleSubscriberSubscription;\n\n\tconstructor(\n\t\treadonly config: CandleSubscriptionConfig,\n\t\treadonly eventBus: CandleEventBus\n\t) {}\n\n\tsubscribeToCandles = async () => {\n\t\tthis.subscription = MarketDataFeed.subscribe({\n\t\t\ttype: 'candles',\n\t\t\tresolution: this.config.resolution,\n\t\t\tenv: this.config.env,\n\t\t\tmarketSymbol: getMarketSymbolForMarketId(\n\t\t\t\tthis.config.marketId,\n\t\t\t\tthis.config.env\n\t\t\t),\n\t\t});\n\n\t\tthis.subscription.observable.subscribe((candle) => {\n\t\t\tthis.eventBus.emit('candle-update', candle);\n\t\t});\n\t};\n\n\tunsubscribe = () => {\n\t\tMarketDataFeed.unsubscribe(this.subscription.id);\n\t};\n}\n\n/**\n * This class will subscribe to candles from the Drift Data API.\n *\n * Note: If you are using TradingView you probably want to just use the DriftTvFeed class instead.\n */\nexport class CandleClient {\n\tprivate activeSubscriptions: Map<\n\t\tstring,\n\t\t{\n\t\t\tsubscriber: CandleSubscriber;\n\t\t\teventBus: CandleEventBus;\n\t\t}\n\t> = new Map();\n\n\tconstructor() {}\n\n\tpublic subscribe = async (\n\t\tconfig: CandleSubscriptionConfig,\n\t\tsubscriptionKey: string\n\t) => {\n\t\t// Kill any existing subscription with the same key before creating a new one\n\t\tif (this.activeSubscriptions.has(subscriptionKey)) {\n\t\t\tthis.unsubscribe(subscriptionKey);\n\t\t}\n\n\t\tconst eventBus = new CandleEventBus();\n\t\tconst subscriber = new CandleSubscriber(config, eventBus);\n\t\tawait subscriber.subscribeToCandles();\n\n\t\tthis.activeSubscriptions.set(subscriptionKey, {\n\t\t\tsubscriber,\n\t\t\teventBus,\n\t\t});\n\n\t\treturn;\n\t};\n\n\t/**\n\t *\n\t * @param config {\n\t *\n\t * env: UIEnv;\n\t *\n\t * marketId: MarketId;\n\t *\n\t * resolution: CandleResolution;\n\t *\n\t * fromTs: number; // Seconds :: This should be the START (oldest) timestamp of the candles to fetch\n\t *\n\t * toTs: number; // Seconds :: This should be the END (newest) timestamp of the candles to fetch\n\t *\n\t * }\n\t * @returns\n\t */\n\tpublic fetch = async (config: CandleFetchConfig): Promise<JsonCandle[]> => {\n\t\tassert(config.fromTs < config.toTs, 'fromTs must be less than toTs');\n\t\tconst nowSeconds = Math.floor(Date.now() / 1000);\n\t\tassert(\n\t\t\tconfig.fromTs <= nowSeconds && config.toTs <= nowSeconds,\n\t\t\t`fromTs and toTs cannot be in the future (Requested fromTs: ${new Date(\n\t\t\t\tconfig.fromTs * 1000\n\t\t\t).toISOString()} and toTs: ${new Date(\n\t\t\t\tconfig.toTs * 1000\n\t\t\t).toISOString()}, Current time: ${new Date(\n\t\t\t\tnowSeconds * 1000\n\t\t\t).toISOString()})`\n\t\t);\n\t\tconst candleFetcher = new CandleFetcher(config);\n\t\tconst candles = await candleFetcher.fetchCandles();\n\t\treturn candles;\n\t};\n\n\tpublic unsubscribe = (subscriptionKey: string) => {\n\t\tconst subscription = this.activeSubscriptions.get(subscriptionKey);\n\t\tif (subscription) {\n\t\t\tCandleFetcher.clearCacheForSubscription(\n\t\t\t\tsubscription.subscriber.config.marketId,\n\t\t\t\tsubscription.subscriber.config.resolution\n\t\t\t);\n\t\t\tsubscription.subscriber.unsubscribe();\n\t\t\tsubscription.eventBus.removeAllListeners();\n\t\t\tthis.activeSubscriptions.delete(subscriptionKey);\n\t\t}\n\t};\n\n\tpublic unsubscribeAll = () => {\n\t\tfor (const subscriptionKey of this.activeSubscriptions.keys()) {\n\t\t\tthis.unsubscribe(subscriptionKey);\n\t\t}\n\t};\n\n\tpublic on(\n\t\tsubscriptionKey: string,\n\t\tevent: keyof CandleSubscriberEvents,\n\t\tlistener: (candle: JsonCandle) => void\n\t) {\n\t\tconst subscription = this.activeSubscriptions.get(subscriptionKey);\n\t\tif (subscription) {\n\t\t\tsubscription.eventBus.on(event, listener);\n\t\t} else {\n\t\t\tconsole.warn(`No active subscription found for key: ${subscriptionKey}`);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"candleClient.js","sourceRoot":"","sources":["../../src/clients/candleClient.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tCandleResolution,\n\tDevnetPerpMarkets,\n\tDevnetSpotMarkets,\n\tMainnetPerpMarkets,\n\tMainnetSpotMarkets,\n} from '@drift-labs/sdk';\nimport { JsonCandle, MarketId, MarketSymbol } from '../types';\nimport { Candle } from '../utils/candles/Candle';\nimport { StrictEventEmitter } from '../utils/StrictEventEmitter';\nimport { EnvironmentConstants } from '../EnvironmentConstants';\nimport { UIEnv } from '../types/UIEnv';\nimport { assert } from '../utils/assert';\nimport { CandleSubscriberSubscription, MarketDataFeed } from './marketDataFeed';\n\n/**\n * # CANDLE CLIENT HIGH LEVEL EXPLANATION:\n * The Candle Client uses the Data API (see https://data.api.drift.trade/playground) to source candles to display.\n *\n * There are two key parts of the client:\n * - Fetching Candles\n * - Subscribing to Candles\n *\n * ## Fetching Candles:\n * - We can fetch candles between any timestamp range.\n * - The maximum number of candles we can fetch in a single request is 1000 (see CANDLE_FETCH_LIMIT).\n * - We define \"recent history\" to be the last 1000 candles .. basically whatever comes back from the infra when we don't use a startTs and use the maximum fetch limit.\n * - We want to avoid using high cardinality parameters in the fetch because otherwise we will miss the cache in the infra.\n * \t- A concrete example of this is that we don't attach a startTs parameter when we are fetching candles within recent history (past 1000 candles)\n * - We cache the recent candles in memory so that any subsequent fetches within recent history after the first one will be served from cache.\n * \t- e.g. moving back to a further timeframe on TradingView - the required candles could potentially be in the cache, so we don't need to refetch them.\n *\n * ## Subscribing to Candles:\n * - We subscribe to a websocket endpoint for a given market and resolution.\n * - We allow the client to support multiple concurrent subscriptions, because the TradingView comopnent will sometimes do this when switching between markets.\n *\n * ## Possible Improvements:\n * - Create a more advanced cache which can store more than the most recent 1000 candles, dynamically growing as more candles are added (for now seems unnecessary, rare for someone to go back further than 1000 candles)\n */\n\n// Used by the subscriber client to fetch candles from the data API\ntype CandleFetchConfig = {\n\tenv: UIEnv;\n\tmarketId: MarketId;\n\tresolution: CandleResolution;\n\tfromTs: number; // Seconds\n\ttoTs: number; // Seconds\n};\n\n// Used by the subscriber client to subscribe to the candles websocket endpoint\ntype CandleSubscriptionConfig = {\n\tresolution: CandleResolution;\n\tmarketId: MarketId;\n\tenv: UIEnv;\n};\n\n// This is what the client subscriber uses internally to fetch the candles from the data API\ntype CandleFetchUrlConfig = {\n\tenv: UIEnv;\n\tmarketId: MarketId;\n\tresolution: CandleResolution;\n\tstartTs?: number; // Seconds - now optional\n\tcountToFetch: number;\n};\n\ntype CandleFetchResponseJson = {\n\tsuccess: boolean;\n\trecords: JsonCandle[];\n};\n\nconst getMarketSymbolForMarketId = (marketId: MarketId, uiEnv: UIEnv) => {\n\tconst isPerp = marketId.isPerp;\n\n\tconst sdkEnv = uiEnv.sdkEnv;\n\n\tif (isPerp) {\n\t\tconst marketConfigs =\n\t\t\tsdkEnv === 'mainnet-beta' ? MainnetPerpMarkets : DevnetPerpMarkets;\n\t\tconst targetMarketConfig = marketConfigs.find(\n\t\t\t(config) => config.marketIndex === marketId.marketIndex\n\t\t);\n\t\treturn targetMarketConfig.symbol as MarketSymbol;\n\t} else {\n\t\tconst marketConfigs =\n\t\t\tsdkEnv === 'mainnet-beta' ? MainnetSpotMarkets : DevnetSpotMarkets;\n\t\tconst targetMarketConfig = marketConfigs.find(\n\t\t\t(config) => config.marketIndex === marketId.marketIndex\n\t\t);\n\t\treturn targetMarketConfig.symbol as MarketSymbol;\n\t}\n};\n\n// This is the maximum number of candles that can be fetched in a single GET request\nconst CANDLE_FETCH_LIMIT = 1000;\n\nconst getBaseDataApiUrl = (env: UIEnv) => {\n\tconst constantEnv: keyof typeof EnvironmentConstants.dataServerUrl =\n\t\tenv.isStaging ? 'staging' : env.isDevnet ? 'dev' : 'mainnet';\n\tconst dataApiUrl = EnvironmentConstants.dataServerUrl[constantEnv];\n\treturn dataApiUrl.replace('https://', '');\n};\n\n// Cache for URL construction to prevent repeated string concatenation\nconst urlCache = new Map<string, string>();\nconst MAX_URL_CACHE_SIZE = 500;\n\nconst getCandleFetchUrl = ({\n\tenv,\n\tmarketId,\n\tresolution,\n\tstartTs,\n\tcountToFetch,\n}: CandleFetchUrlConfig) => {\n\tconst baseDataApiUrl = getBaseDataApiUrl(env);\n\n\t// Cache key for this URL configuration\n\tconst cacheKey = `${marketId.key}-${resolution}-${countToFetch}-${env.key}-${\n\t\tstartTs ?? 'none'\n\t}`;\n\n\tif (urlCache.has(cacheKey)) {\n\t\treturn urlCache.get(cacheKey)!;\n\t}\n\n\t// Base URL without startTs parameter\n\tlet fetchUrl = `https://${baseDataApiUrl}/market/${getMarketSymbolForMarketId(\n\t\tmarketId,\n\t\tenv\n\t)}/candles/${resolution}?limit=${Math.min(countToFetch, CANDLE_FETCH_LIMIT)}`;\n\n\t// Only add startTs parameter if it's provided\n\tif (startTs !== undefined) {\n\t\tfetchUrl += `&startTs=${startTs}`;\n\t}\n\n\t// Cache the result if cache isn't too large\n\tif (urlCache.size < MAX_URL_CACHE_SIZE) {\n\t\turlCache.set(cacheKey, fetchUrl);\n\t}\n\n\treturn fetchUrl;\n};\n\ntype CandleSubscriberEvents = {\n\t'candle-update': JsonCandle;\n};\n\n// Separate event bus for candle events\nclass CandleEventBus extends StrictEventEmitter<CandleSubscriberEvents> {\n\tconstructor() {\n\t\tsuper();\n\t}\n}\n\n// This class is reponsible for fetching candles from the data API's GET endpoint\nclass CandleFetcher {\n\tprivate readonly config: CandleFetchConfig;\n\n\t// Cache for storing recent candles by market ID and resolution\n\tpublic static recentCandlesCache: Map<\n\t\tstring, // key: `${marketId.key}-${resolution}`\n\t\t{\n\t\t\tcandles: JsonCandle[];\n\t\t\tearliestTs: number;\n\t\t\tlatestTs: number;\n\t\t\tfetchTime: number;\n\t\t}\n\t> = new Map();\n\n\t// Helper method to generate a cache key from market ID and resolution\n\tpublic static getCacheKey(\n\t\tmarketId: MarketId,\n\t\tresolution: CandleResolution\n\t): string {\n\t\treturn `${marketId.key}-${resolution}`;\n\t}\n\n\t// Public method to clear the entire cache\n\tpublic static clearWholeCache() {\n\t\tCandleFetcher.recentCandlesCache.clear();\n\t}\n\n\tpublic static clearCacheForSubscription(\n\t\tmarketId: MarketId,\n\t\tresolution: CandleResolution\n\t) {\n\t\tCandleFetcher.recentCandlesCache.delete(\n\t\t\tCandleFetcher.getCacheKey(marketId, resolution)\n\t\t);\n\t}\n\n\tconstructor(config: CandleFetchConfig) {\n\t\tthis.config = config;\n\t}\n\n\t/**\n\t * Candles are fetched in ascending order of time (index 0 -> oldest to index n -> newest)\n\t */\n\tprivate fetchCandlesFromApi = async (\n\t\tfetchUrl: string\n\t): Promise<JsonCandle[]> => {\n\t\tconst response = await fetch(fetchUrl);\n\t\tconst parsedResponse = (await response.json()) as CandleFetchResponseJson;\n\n\t\tif (!parsedResponse.success) {\n\t\t\tthrow new Error('Failed to fetch candles from data API');\n\t\t}\n\n\t\treturn parsedResponse.records;\n\t};\n\n\tprivate getCountOfCandlesBetweenStartAndEndTs = (\n\t\tstartTs: number,\n\t\tendTs: number\n\t) => {\n\t\tconst diffInSeconds = endTs - startTs;\n\t\tconst resolutionInSeconds =\n\t\t\tCandle.resolutionStringToCandleLengthMs(this.config.resolution) / 1000;\n\t\tconst diffInCandles = diffInSeconds / resolutionInSeconds;\n\t\treturn Math.ceil(diffInCandles);\n\t};\n\n\t/**\n\t * Try to get candles from the cache if they're available.\n\t * Returns null if no cached candles are available for the requested range.\n\t */\n\tprivate getFromCache = (): JsonCandle[] | null => {\n\t\t// Generate cache key for the current request\n\t\tconst cacheKey = CandleFetcher.getCacheKey(\n\t\t\tthis.config.marketId,\n\t\t\tthis.config.resolution\n\t\t);\n\t\tconst cachedCandles = CandleFetcher.recentCandlesCache.get(cacheKey);\n\n\t\t// Check if we have cached candles for this market and resolution\n\t\tif (cachedCandles) {\n\t\t\t// Check if the requested time range is within the bounds of cached candles\n\t\t\tif (\n\t\t\t\tthis.config.fromTs >= cachedCandles.earliestTs &&\n\t\t\t\tthis.config.toTs <= cachedCandles.latestTs\n\t\t\t) {\n\t\t\t\t// Filter cached candles to the requested time range\n\t\t\t\tconst filteredCandles = cachedCandles.candles.filter(\n\t\t\t\t\t(candle) =>\n\t\t\t\t\t\tcandle.ts >= this.config.fromTs && candle.ts <= this.config.toTs\n\t\t\t\t);\n\n\t\t\t\treturn filteredCandles;\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t};\n\n\t/**\n\t * Determines if we should use the recent candles approach (without startTs for better caching).\n\t */\n\tprivate isRequestingRecentCandles = (\n\t\tnowSeconds: number,\n\t\tcandleLengthSeconds: number\n\t): boolean => {\n\t\t// Calculate cutoff time for \"recent\" candles (now - 1000 candles worth of time)\n\t\tconst recentCandlesCutoffTs =\n\t\t\tnowSeconds - candleLengthSeconds * CANDLE_FETCH_LIMIT;\n\n\t\t// Check if we're fetching recent candles based on the fromTs\n\t\treturn this.config.fromTs >= recentCandlesCutoffTs;\n\t};\n\n\t/**\n\t * Fetch recent candles without using startTs for better caching.\n\t */\n\tprivate fetchRecentCandles = async (\n\t\tnowSeconds: number\n\t): Promise<JsonCandle[]> => {\n\t\t// Fetch recent candles without specifying startTs\n\t\tconst fetchUrl = getCandleFetchUrl({\n\t\t\tenv: this.config.env,\n\t\t\tmarketId: this.config.marketId,\n\t\t\tresolution: this.config.resolution,\n\t\t\tcountToFetch: CANDLE_FETCH_LIMIT, // Ask for max candles to ensure we get enough\n\t\t});\n\n\t\t// Get the candles and reverse them (into ascending order)\n\t\tconst fetchedCandles = await this.fetchCandlesFromApi(fetchUrl);\n\t\tfetchedCandles.reverse();\n\n\t\tif (fetchedCandles.length === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\t// Store the full fetchedCandles in cache before filtering\n\t\tthis.updateCandleCache(fetchedCandles, nowSeconds);\n\n\t\t// Filter to only include candles in the requested time range\n\t\tconst filteredCandles = this.filterCandlesByTimeRange(fetchedCandles);\n\n\t\treturn filteredCandles;\n\t};\n\n\t/**\n\t * Filter candles to only include those in the requested time range.\n\t */\n\tprivate filterCandlesByTimeRange = (candles: JsonCandle[]): JsonCandle[] => {\n\t\treturn candles.filter(\n\t\t\t(candle) =>\n\t\t\t\tcandle.ts >= this.config.fromTs && candle.ts <= this.config.toTs\n\t\t);\n\t};\n\n\t/**\n\t * Update the candle cache with the latest fetched candles.\n\t */\n\tprivate updateCandleCache = (\n\t\tfetchedCandles: JsonCandle[],\n\t\tnowSeconds: number\n\t): void => {\n\t\tif (fetchedCandles.length > 0) {\n\t\t\t// Generate cache key\n\t\t\tconst cacheKey = CandleFetcher.getCacheKey(\n\t\t\t\tthis.config.marketId,\n\t\t\t\tthis.config.resolution\n\t\t\t);\n\n\t\t\t// Sort candles by timestamp to find earliest and latest\n\t\t\tconst sortedCandles = [...fetchedCandles].sort((a, b) => a.ts - b.ts);\n\t\t\tconst earliestTs = sortedCandles[0].ts;\n\t\t\tconst latestTs = sortedCandles[sortedCandles.length - 1].ts;\n\n\t\t\t// Update or add to cache\n\t\t\tCandleFetcher.recentCandlesCache.set(cacheKey, {\n\t\t\t\tcandles: sortedCandles,\n\t\t\t\tearliestTs,\n\t\t\t\tlatestTs,\n\t\t\t\tfetchTime: nowSeconds,\n\t\t\t});\n\t\t}\n\t};\n\n\t/**\n\t * Fetch historical candles with pagination using startTs.\n\t */\n\tprivate fetchHistoricalCandles = async (): Promise<JsonCandle[]> => {\n\t\tlet candlesRemainingToFetch = this.getCountOfCandlesBetweenStartAndEndTs(\n\t\t\tthis.config.fromTs,\n\t\t\tthis.config.toTs\n\t\t);\n\n\t\tlet currentStartTs = this.config.toTs; // The data API takes \"startTs\" as the \"first timestamp you want going backwards in time\" e.g. all candles will be returned with descending time backwards from the startTs\n\t\tlet hitEndTsCutoff = false;\n\n\t\tlet candles: JsonCandle[] = [];\n\n\t\twhile (candlesRemainingToFetch > 0) {\n\t\t\tconst result = await this.fetchHistoricalCandlesBatch(\n\t\t\t\tcandlesRemainingToFetch,\n\t\t\t\tcurrentStartTs\n\t\t\t);\n\n\t\t\tif (result.fetchedCandles.length === 0) {\n\t\t\t\tcandlesRemainingToFetch = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// the deeper the loop, the older the result.candlesToAdd will be\n\t\t\tcandles = [...result.candlesToAdd, ...candles];\n\t\t\tcandlesRemainingToFetch -= result.candlesToAdd.length;\n\t\t\thitEndTsCutoff = result.hitEndTsCutoff;\n\n\t\t\tif (result.requiresAnotherFetch) {\n\t\t\t\tcurrentStartTs = result.nextStartTs;\n\t\t\t} else if (candlesRemainingToFetch > 0) {\n\t\t\t\t// This means we have fetched all the candles available for this time range and we can stop fetching\n\t\t\t\tcandlesRemainingToFetch = 0;\n\t\t\t}\n\t\t}\n\n\t\tif (hitEndTsCutoff) {\n\t\t\treturn this.filterCandlesByTimeRange(candles);\n\t\t}\n\n\t\treturn candles;\n\t};\n\n\t/**\n\t * Fetch historical candles with pagination, backwards from the toTs value given in the config.\n\t *\n\t * This method works by looping backwards from the LATEST (toTs) timestamp to the OLDEST (fromTs) timestamp.\n\t *\n\t * Things to note:\n\t * - There is a limit to how many candles can be fetched in a single request (see CANDLE_FETCH_LIMIT)\n\t * - We have implemented this to minimise the cardinality in the API request because that helps with caching\n\t */\n\tprivate fetchHistoricalCandlesBatch = async (\n\t\tcandlesRemainingToFetch: number,\n\t\tcurrentStartTs: number\n\t): Promise<{\n\t\tfetchedCandles: JsonCandle[];\n\t\tcandlesToAdd: JsonCandle[];\n\t\thitEndTsCutoff: boolean;\n\t\trequiresAnotherFetch: boolean;\n\t\tnextStartTs: number;\n\t}> => {\n\t\tconst candlesToFetch = Math.min(\n\t\t\tcandlesRemainingToFetch,\n\t\t\tCANDLE_FETCH_LIMIT\n\t\t);\n\n\t\tconst fetchUrl = getCandleFetchUrl({\n\t\t\tenv: this.config.env,\n\t\t\tmarketId: this.config.marketId,\n\t\t\tresolution: this.config.resolution,\n\t\t\tstartTs: currentStartTs, // Include startTs for historical candles\n\t\t\tcountToFetch: candlesToFetch,\n\t\t});\n\n\t\tconst fetchedCandles = await this.fetchCandlesFromApi(fetchUrl);\n\n\t\t// Reverse candles into ascending order\n\t\tfetchedCandles.reverse();\n\n\t\tif (fetchedCandles.length === 0) {\n\t\t\treturn {\n\t\t\t\tfetchedCandles,\n\t\t\t\tcandlesToAdd: [],\n\t\t\t\thitEndTsCutoff: false,\n\t\t\t\trequiresAnotherFetch: false,\n\t\t\t\tnextStartTs: currentStartTs,\n\t\t\t};\n\t\t}\n\n\t\tconst lastCandle = fetchedCandles[fetchedCandles.length - 1]; // This is the LATEST candle .. (they are sorted ascending by time right now)\n\n\t\tconst hitPageSizeCutoff = fetchedCandles.length === CANDLE_FETCH_LIMIT;\n\t\tconst hitEndTsCutoff = lastCandle.ts < this.config.fromTs;\n\n\t\tconst requiresAnotherFetch = hitPageSizeCutoff && !hitEndTsCutoff; // If the number of candles returned is equal to the maximum number of candles that can be fetched in a single GET request, then we need to fetch more candles\n\n\t\tlet candlesToAdd = fetchedCandles;\n\t\tlet nextStartTs = currentStartTs;\n\n\t\tif (requiresAnotherFetch) {\n\t\t\t// If we need to do another fetch, trim any candles with the same timestamp as the last candle in the previous fetch, because that is the pointer for our next fetch and we don't want to duplicate candles\n\t\t\tcandlesToAdd = candlesToAdd.filter((candle) => {\n\t\t\t\treturn candle.ts < lastCandle.ts;\n\t\t\t});\n\n\t\t\tconst oldestCandle = fetchedCandles[0]; // first candle is the oldest\n\t\t\tnextStartTs = oldestCandle.ts; // If we are doing another loop, then the trimmed candles have all the candles except for ones with the last candle's timestamp. For the next loop we want to fetch from that timestamp;\n\t\t}\n\n\t\treturn {\n\t\t\tfetchedCandles,\n\t\t\tcandlesToAdd,\n\t\t\thitEndTsCutoff,\n\t\t\trequiresAnotherFetch,\n\t\t\tnextStartTs,\n\t\t};\n\t};\n\n\t/**\n\t * This class needs to fetch candles based on the config.\n\t *\n\t * If the number of candles requested exceeds the maximum number of candles that can be fetched in a single GET request, then it needs to loop multiple get requests, using the last candle's timestamp as the offset startTs for each subsequent request. If the number of candles returned is less than the requested number of candles, then we have fetched all the candles available.\n\t *\n\t * For recent candles (ones where fromTs > now - candleLength*1000), we avoid using startTs in the URL to improve caching,\n\t * and instead fetch the most recent 1000 candles and then trim the result.\n\t */\n\tpublic fetchCandles = async () => {\n\t\t// Check cache first\n\t\tconst cachedCandles = this.getFromCache();\n\t\tif (cachedCandles) {\n\t\t\treturn cachedCandles;\n\t\t}\n\n\t\t// Calculate the candle length in seconds for the current resolution\n\t\tconst candleLengthMs = Candle.resolutionStringToCandleLengthMs(\n\t\t\tthis.config.resolution\n\t\t);\n\t\tconst candleLengthSeconds = candleLengthMs / 1000;\n\n\t\t// Get current time in seconds\n\t\tconst nowSeconds = Math.floor(Date.now() / 1000);\n\n\t\t// Check if we're fetching recent candles\n\t\tif (this.isRequestingRecentCandles(nowSeconds, candleLengthSeconds)) {\n\t\t\treturn this.fetchRecentCandles(nowSeconds);\n\t\t}\n\n\t\t// For historical candles (older than the last 1000 candles), use the previous approach\n\t\t// with startTs for pagination\n\t\treturn this.fetchHistoricalCandles();\n\t};\n}\n\nclass CandleSubscriber {\n\tprivate subscription: CandleSubscriberSubscription;\n\n\tconstructor(\n\t\treadonly config: CandleSubscriptionConfig,\n\t\treadonly eventBus: CandleEventBus\n\t) {}\n\n\tsubscribeToCandles = async () => {\n\t\tthis.subscription = MarketDataFeed.subscribe({\n\t\t\ttype: 'candles',\n\t\t\tresolution: this.config.resolution,\n\t\t\tenv: this.config.env,\n\t\t\tmarketSymbol: getMarketSymbolForMarketId(\n\t\t\t\tthis.config.marketId,\n\t\t\t\tthis.config.env\n\t\t\t),\n\t\t});\n\n\t\tthis.subscription.observable.subscribe((candle) => {\n\t\t\tthis.eventBus.emit('candle-update', candle);\n\t\t});\n\t};\n\n\tunsubscribe = () => {\n\t\tMarketDataFeed.unsubscribe(this.subscription.id);\n\t};\n}\n\n/**\n * This class will subscribe to candles from the Drift Data API.\n *\n * Note: If you are using TradingView you probably want to just use the DriftTvFeed class instead.\n */\nexport class CandleClient {\n\tprivate activeSubscriptions: Map<\n\t\tstring,\n\t\t{\n\t\t\tsubscriber: CandleSubscriber;\n\t\t\teventBus: CandleEventBus;\n\t\t}\n\t> = new Map();\n\n\tconstructor() {}\n\n\tpublic subscribe = async (\n\t\tconfig: CandleSubscriptionConfig,\n\t\tsubscriptionKey: string\n\t) => {\n\t\t// Kill any existing subscription with the same key before creating a new one\n\t\tif (this.activeSubscriptions.has(subscriptionKey)) {\n\t\t\tthis.unsubscribe(subscriptionKey);\n\t\t}\n\n\t\tconst eventBus = new CandleEventBus();\n\t\tconst subscriber = new CandleSubscriber(config, eventBus);\n\t\tawait subscriber.subscribeToCandles();\n\n\t\tthis.activeSubscriptions.set(subscriptionKey, {\n\t\t\tsubscriber,\n\t\t\teventBus,\n\t\t});\n\n\t\treturn;\n\t};\n\n\t/**\n\t *\n\t * @param config {\n\t *\n\t * env: UIEnv;\n\t *\n\t * marketId: MarketId;\n\t *\n\t * resolution: CandleResolution;\n\t *\n\t * fromTs: number; // Seconds :: This should be the START (oldest) timestamp of the candles to fetch\n\t *\n\t * toTs: number; // Seconds :: This should be the END (newest) timestamp of the candles to fetch\n\t *\n\t * }\n\t * @returns\n\t */\n\tpublic fetch = async (config: CandleFetchConfig): Promise<JsonCandle[]> => {\n\t\tassert(config.fromTs < config.toTs, 'fromTs must be less than toTs');\n\t\tconst nowSeconds = Math.floor(Date.now() / 1000);\n\t\tassert(\n\t\t\tconfig.fromTs <= nowSeconds && config.toTs <= nowSeconds,\n\t\t\t`fromTs and toTs cannot be in the future (Requested fromTs: ${new Date(\n\t\t\t\tconfig.fromTs * 1000\n\t\t\t).toISOString()} and toTs: ${new Date(\n\t\t\t\tconfig.toTs * 1000\n\t\t\t).toISOString()}, Current time: ${new Date(\n\t\t\t\tnowSeconds * 1000\n\t\t\t).toISOString()})`\n\t\t);\n\t\tconst candleFetcher = new CandleFetcher(config);\n\t\tconst candles = await candleFetcher.fetchCandles();\n\t\treturn candles;\n\t};\n\n\tpublic unsubscribe = (subscriptionKey: string) => {\n\t\tconst subscription = this.activeSubscriptions.get(subscriptionKey);\n\t\tif (subscription) {\n\t\t\tCandleFetcher.clearCacheForSubscription(\n\t\t\t\tsubscription.subscriber.config.marketId,\n\t\t\t\tsubscription.subscriber.config.resolution\n\t\t\t);\n\t\t\tsubscription.subscriber.unsubscribe();\n\t\t\tsubscription.eventBus.removeAllListeners();\n\t\t\tthis.activeSubscriptions.delete(subscriptionKey);\n\t\t}\n\t};\n\n\tpublic unsubscribeAll = () => {\n\t\tfor (const subscriptionKey of this.activeSubscriptions.keys()) {\n\t\t\tthis.unsubscribe(subscriptionKey);\n\t\t}\n\t};\n\n\tpublic on(\n\t\tsubscriptionKey: string,\n\t\tevent: keyof CandleSubscriberEvents,\n\t\tlistener: (candle: JsonCandle) => void\n\t) {\n\t\tconst subscription = this.activeSubscriptions.get(subscriptionKey);\n\t\tif (subscription) {\n\t\t\tsubscription.eventBus.on(event, listener);\n\t\t} else {\n\t\t\tconsole.warn(`No active subscription found for key: ${subscriptionKey}`);\n\t\t}\n\t}\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  /// <reference types="node" />
2
+ /// <reference types="node" />
2
3
  import Redis, { Cluster, RedisOptions } from 'ioredis';
3
4
  export declare enum RedisClientPrefix {
4
5
  EXCHANGE = "",
@@ -1,4 +1,5 @@
1
1
  /// <reference types="node" />
2
+ /// <reference types="node" />
2
3
  import { AccountInfo, Connection, PublicKey } from '@solana/web3.js';
3
4
  import { DriftClient, MarketType, OrderType, SignedMsgOrderParamsDelegateMessage, SignedMsgOrderParamsMessage } from '@drift-labs/sdk';
4
5
  import { Observable, Subscriber } from 'rxjs';
@@ -2,6 +2,20 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SwiftClient = void 0;
4
4
  const sdk_1 = require("@drift-labs/sdk");
5
+ // Cache for URL construction to prevent repeated string concatenation
6
+ const swiftUrlCache = new Map();
7
+ const MAX_SWIFT_URL_CACHE_SIZE = 200;
8
+ function getCachedUrl(baseUrl, endpoint) {
9
+ const cacheKey = `${baseUrl}:${endpoint}`;
10
+ if (swiftUrlCache.has(cacheKey)) {
11
+ return swiftUrlCache.get(cacheKey);
12
+ }
13
+ const result = `${baseUrl}${endpoint}`;
14
+ if (swiftUrlCache.size < MAX_SWIFT_URL_CACHE_SIZE) {
15
+ swiftUrlCache.set(cacheKey, result);
16
+ }
17
+ return result;
18
+ }
5
19
  const rxjs_1 = require("rxjs");
6
20
  const logger_1 = require("../utils/logger");
7
21
  class SwiftClient {
@@ -17,7 +31,7 @@ class SwiftClient {
17
31
  const headers = new Headers({
18
32
  ...this.getSwiftHeaders(),
19
33
  });
20
- fetch(`${this.baseUrl}${url}`, {
34
+ fetch(getCachedUrl(this.baseUrl, url), {
21
35
  headers,
22
36
  })
23
37
  .then(async (response) => {
@@ -50,7 +64,7 @@ class SwiftClient {
50
64
  body: JSON.stringify(bodyObject),
51
65
  };
52
66
  return new Promise((res) => {
53
- const postRequest = new Request(`${this.baseUrl}${url}`, requestOptions);
67
+ const postRequest = new Request(getCachedUrl(this.baseUrl, url), requestOptions);
54
68
  fetch(postRequest)
55
69
  .then(async (response) => {
56
70
  let resBody = null;
@@ -156,7 +170,7 @@ class SwiftClient {
156
170
  return {
157
171
  success: true,
158
172
  status: 200,
159
- message: 'Confirmed hash: ' + hash,
173
+ message: `Confirmed hash: ${hash}`,
160
174
  body: {
161
175
  orderId: confirmResponse.body,
162
176
  status: 'confirmed',
@@ -173,7 +187,7 @@ class SwiftClient {
173
187
  return {
174
188
  success: false,
175
189
  status: 408,
176
- message: 'Failed to confirm hash: ' + hash,
190
+ message: `Failed to confirm hash: ${hash}`,
177
191
  body: {
178
192
  status: 'expired',
179
193
  },
@@ -227,7 +241,7 @@ class SwiftClient {
227
241
  subscriber.next({
228
242
  type: 'errored',
229
243
  hash: '',
230
- message: 'Error from swift node: ' + sendResponse.message,
244
+ message: `Error from swift node: ${sendResponse.message}`,
231
245
  status: sendResponse.status,
232
246
  });
233
247
  subscriber.error();
@@ -1 +1 @@
1
- {"version":3,"file":"swiftClient.js","sourceRoot":"","sources":["../../src/clients/swiftClient.ts"],"names":[],"mappings":"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","sourcesContent":["import { AccountInfo, Connection, PublicKey } from '@solana/web3.js';\nimport {\n\tDriftClient,\n\tMarketType,\n\tOrderType,\n\tSignedMsgOrderParamsDelegateMessage,\n\tSignedMsgOrderParamsMessage,\n\tSignedMsgUserOrdersAccount,\n\tdigestSignature,\n\tisVariant,\n} from '@drift-labs/sdk';\nimport { Observable, Subscriber } from 'rxjs';\nimport { allEnvDlog } from '../utils/logger';\nexport type SwiftServerOrderProcessResponse = {\n\terror?: string;\n\tmessage: string;\n};\n\ntype ClientResponse<T = void> = Promise<{\n\tsuccess: boolean;\n\tbody?: T;\n\tmessage?: string;\n\tstatus?: number;\n}>;\n\ntype BaseSwiftOrderEvent = {\n\thash: string;\n};\n\nexport interface SwiftOrderSentEvent extends BaseSwiftOrderEvent {\n\ttype: 'sent';\n}\n\nexport interface SwiftOrderErroredEvent extends BaseSwiftOrderEvent {\n\ttype: 'errored' | 'expired';\n\tmessage?: string;\n\tstatus?: number;\n}\n\nexport interface SwiftOrderConfirmedEvent extends BaseSwiftOrderEvent {\n\ttype: 'confirmed';\n\torderId: string;\n}\n\nexport type SwiftOrderEvent =\n\t| SwiftOrderErroredEvent\n\t| SwiftOrderConfirmedEvent\n\t| SwiftOrderSentEvent;\n\nexport type SwiftOrderEventWithParams<T extends SwiftOrderEvent> = T & {\n\tswiftOrderUuid: Uint8Array;\n\torderParamsMessage:\n\t\t| SignedMsgOrderParamsMessage\n\t\t| SignedMsgOrderParamsDelegateMessage;\n};\n\nexport class SwiftClient {\n\tprivate static baseUrl = '';\n\tprivate static swiftClientConsumer?: string;\n\n\tstatic supportedOrderTypes: OrderType[] = [OrderType.MARKET, OrderType.LIMIT];\n\n\tpublic static init(baseUrl: string, swiftClientConsumer?: string) {\n\t\tthis.baseUrl = baseUrl;\n\t\tthis.swiftClientConsumer = swiftClientConsumer;\n\t}\n\n\tprivate static get(url: string) {\n\t\tif (!this.baseUrl) {\n\t\t\tthrow new Error('SwiftClient not initialized');\n\t\t}\n\n\t\treturn new Promise<{ success: boolean; body: string; status: number }>(\n\t\t\t(res) => {\n\t\t\t\tconst headers = new Headers({\n\t\t\t\t\t...this.getSwiftHeaders(),\n\t\t\t\t});\n\n\t\t\t\tfetch(`${this.baseUrl}${url}`, {\n\t\t\t\t\theaders,\n\t\t\t\t})\n\t\t\t\t\t.then(async (response) => {\n\t\t\t\t\t\tif (!response.ok) {\n\t\t\t\t\t\t\tres({\n\t\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\t\tbody: await response.text(),\n\t\t\t\t\t\t\t\tstatus: response.status,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tres({\n\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\tbody: await response.text(),\n\t\t\t\t\t\t\tstatus: response.status,\n\t\t\t\t\t\t});\n\t\t\t\t\t})\n\t\t\t\t\t.catch((err) => {\n\t\t\t\t\t\tres({ success: false, body: err, status: 0 });\n\t\t\t\t\t});\n\t\t\t}\n\t\t);\n\t}\n\n\tprivate static post(url: string, bodyObject: any) {\n\t\tif (!this.baseUrl) {\n\t\t\tthrow new Error('SwiftClient not initialized');\n\t\t}\n\n\t\tconst requestOptions = {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { ...this.getSwiftHeaders() },\n\t\t\tbody: JSON.stringify(bodyObject),\n\t\t};\n\n\t\treturn new Promise<{\n\t\t\tsuccess: boolean;\n\t\t\tbody: SwiftServerOrderProcessResponse;\n\t\t\tstatus: number;\n\t\t}>((res) => {\n\t\t\tconst postRequest = new Request(`${this.baseUrl}${url}`, requestOptions);\n\n\t\t\tfetch(postRequest)\n\t\t\t\t.then(async (response) => {\n\t\t\t\t\tlet resBody: SwiftServerOrderProcessResponse | null = null;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tresBody =\n\t\t\t\t\t\t\t(await response.json()) as SwiftServerOrderProcessResponse;\n\n\t\t\t\t\t\tres({\n\t\t\t\t\t\t\tsuccess: response.ok,\n\t\t\t\t\t\t\tbody: resBody,\n\t\t\t\t\t\t\tstatus: response.status,\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tallEnvDlog('swiftClient', 'Error reading response body', err);\n\n\t\t\t\t\t\tres({\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\tbody: err as any,\n\t\t\t\t\t\t\tstatus: response.status,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tres({ success: false, body: err, status: 0 });\n\t\t\t\t});\n\t\t});\n\t}\n\n\tstatic async sendSwiftOrder(\n\t\tmarketIndex: number,\n\t\tmarketType: MarketType,\n\t\tmessage: string,\n\t\tsignature: Buffer,\n\t\ttakerPubkey: PublicKey,\n\t\tsigningAuthority?: PublicKey\n\t): ClientResponse<{\n\t\thash: string;\n\t}> {\n\t\tconst requestPayload = {\n\t\t\tmarket_index: marketIndex,\n\t\t\tmarket_type: isVariant(marketType, 'perp') ? 'perp' : 'spot',\n\t\t\tmessage,\n\t\t\tsignature: signature.toString('base64'),\n\t\t\tsigning_authority: signingAuthority?.toBase58() ?? '',\n\t\t\ttaker_authority: takerPubkey.toBase58(),\n\t\t};\n\n\t\tconst response = await this.post('/orders', requestPayload);\n\n\t\tif (response.status !== 200) {\n\t\t\tconsole.error(\n\t\t\t\t`Non-200 status code received for sent Swift order: ${response.status}`\n\t\t\t);\n\t\t\tallEnvDlog('swiftClient', 'full non-200 response body', response.body);\n\t\t\treturn {\n\t\t\t\tmessage:\n\t\t\t\t\tresponse.body?.error ||\n\t\t\t\t\tresponse.body?.message ||\n\t\t\t\t\t`HTTP ${response.status}: Error from Swift server`,\n\t\t\t\tstatus: response.status,\n\t\t\t\tsuccess: false,\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tmessage: `Successfully sent Swift order`,\n\t\t\tbody: {\n\t\t\t\thash: digestSignature(Uint8Array.from(signature)),\n\t\t\t},\n\t\t\tsuccess: true,\n\t\t\tstatus: 200,\n\t\t};\n\t}\n\n\tstatic async confirmSwiftOrderWS(\n\t\tconnection: Connection,\n\t\tclient: DriftClient,\n\t\tsignedMsgUserOrdersAccount: PublicKey,\n\t\tsignedMsgOrderUuid: Uint8Array,\n\t\tconfirmDuration: number\n\t): Promise<number | undefined> {\n\t\tallEnvDlog(\n\t\t\t'swiftClient',\n\t\t\t'confirmSwiftOrderWS - confirmation duration',\n\t\t\tconfirmDuration\n\t\t);\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst timeout = setTimeout(() => {\n\t\t\t\treject(new Error('Order not found'));\n\t\t\t}, confirmDuration);\n\n\t\t\tconnection\n\t\t\t\t.getAccountInfo(signedMsgUserOrdersAccount, 'confirmed')\n\t\t\t\t.then((accountInfo) => {\n\t\t\t\t\tif (!accountInfo) {\n\t\t\t\t\t\treject(new Error('Swift message account not found'));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst order = this.findOrderInSignedMsgUserOrdersAccount(\n\t\t\t\t\t\tclient,\n\t\t\t\t\t\taccountInfo,\n\t\t\t\t\t\tsignedMsgOrderUuid\n\t\t\t\t\t);\n\n\t\t\t\t\tif (order) {\n\t\t\t\t\t\tallEnvDlog(\n\t\t\t\t\t\t\t'swiftClient',\n\t\t\t\t\t\t\t'confirmed in initial fetch orderID\\n',\n\t\t\t\t\t\t\torder.orderId\n\t\t\t\t\t\t);\n\t\t\t\t\t\tclearTimeout(timeout);\n\t\t\t\t\t\tresolve(order.orderId);\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\tconst subId = connection.onAccountChange(\n\t\t\t\tsignedMsgUserOrdersAccount,\n\t\t\t\t(accountInfo) => {\n\t\t\t\t\tconst order = this.findOrderInSignedMsgUserOrdersAccount(\n\t\t\t\t\t\tclient,\n\t\t\t\t\t\taccountInfo,\n\t\t\t\t\t\tsignedMsgOrderUuid\n\t\t\t\t\t);\n\t\t\t\t\tif (order) {\n\t\t\t\t\t\tallEnvDlog(\n\t\t\t\t\t\t\t'swiftClient',\n\t\t\t\t\t\t\t'confirmed in onAccountChange orderID\\n',\n\t\t\t\t\t\t\torder.orderId\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconnection.removeAccountChangeListener(subId);\n\t\t\t\t\t\tclearTimeout(timeout);\n\t\t\t\t\t\tresolve(order.orderId);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t'confirmed'\n\t\t\t);\n\t\t});\n\t}\n\n\tstatic findOrderInSignedMsgUserOrdersAccount(\n\t\tclient: DriftClient,\n\t\tordersAccount: AccountInfo<Buffer>,\n\t\tsignedMsgOrderUuid: Uint8Array\n\t) {\n\t\tconst accountDecoder =\n\t\t\tclient.program.account.signedMsgUserOrders.coder.accounts.decodeUnchecked.bind(\n\t\t\t\tclient.program.account.signedMsgUserOrders.coder.accounts\n\t\t\t);\n\t\tconst decodedAccount = accountDecoder(\n\t\t\t'SignedMsgUserOrders',\n\t\t\tordersAccount.data\n\t\t) as SignedMsgUserOrdersAccount;\n\t\tallEnvDlog(\n\t\t\t'swiftClient findOrder',\n\t\t\t'decodedAccount\\n',\n\t\t\tdecodedAccount,\n\t\t\tsignedMsgOrderUuid.toString()\n\t\t);\n\t\tconst order = decodedAccount.signedMsgOrderData.find(\n\t\t\t(order) => order.uuid.toString() === signedMsgOrderUuid.toString()\n\t\t);\n\t\tallEnvDlog('swiftClient findOrder', 'order\\n', order);\n\t\treturn order;\n\t}\n\n\tstatic async confirmSwiftOrder(\n\t\thash: string,\n\t\tconfirmDuration: number\n\t): Promise<\n\t\tClientResponse<\n\t\t\t| {\n\t\t\t\t\torderId: string;\n\t\t\t\t\tstatus: 'confirmed';\n\t\t\t }\n\t\t\t| {\n\t\t\t\t\tstatus: 'expired';\n\t\t\t }\n\t\t>\n\t> {\n\t\tconst expireTime = Date.now() + confirmDuration;\n\n\t\twhile (Date.now() < expireTime) {\n\t\t\tconst confirmResponse = await this.get(\n\t\t\t\t`/confirmation/hash-status?hash=${encodeURIComponent(hash)}`\n\t\t\t);\n\n\t\t\tif (confirmResponse.status === 200) {\n\t\t\t\tconsole.log('Confirmed hash: ', hash);\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tstatus: 200,\n\t\t\t\t\tmessage: 'Confirmed hash: ' + hash,\n\t\t\t\t\tbody: {\n\t\t\t\t\t\torderId: confirmResponse.body,\n\t\t\t\t\t\tstatus: 'confirmed',\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t} else if (\n\t\t\t\tconfirmResponse.status >= 500 ||\n\t\t\t\tconfirmResponse.status < 200\n\t\t\t) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tawait new Promise((resolve) => setTimeout(resolve, 1000));\n\t\t}\n\n\t\tconsole.error('Failed to confirm hash: ', hash);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\tstatus: 408,\n\t\t\tmessage: 'Failed to confirm hash: ' + hash,\n\t\t\tbody: {\n\t\t\t\tstatus: 'expired',\n\t\t\t},\n\t\t};\n\t}\n\n\tstatic async handleSwiftOrderSubscriber(\n\t\tsubscriber: Subscriber<SwiftOrderEvent>,\n\t\tmarketIndex: number,\n\t\tmarketType: MarketType,\n\t\tmessage: string,\n\t\tsignature: Buffer,\n\t\ttakerPubkey: PublicKey,\n\t\tconfirmDuration: number,\n\t\tsigningAuthority?: PublicKey\n\t) {\n\t\t// First send the order\n\t\tconst sendResponse = await this.sendSwiftOrder(\n\t\t\tmarketIndex,\n\t\t\tmarketType,\n\t\t\tmessage,\n\t\t\tsignature,\n\t\t\ttakerPubkey,\n\t\t\tsigningAuthority\n\t\t);\n\n\t\tif (!sendResponse.success) {\n\t\t\tsubscriber.next({\n\t\t\t\ttype: 'errored',\n\t\t\t\thash: '',\n\t\t\t\tmessage: sendResponse.message,\n\t\t\t\tstatus: sendResponse.status,\n\t\t\t});\n\t\t\tsubscriber.error();\n\t\t\treturn;\n\t\t} else {\n\t\t\tsubscriber.next({\n\t\t\t\ttype: 'sent',\n\t\t\t\thash: sendResponse.body.hash,\n\t\t\t});\n\t\t}\n\n\t\tconst hash = sendResponse.body.hash;\n\n\t\t// Then confirm it\n\t\tconst confirmResponse = await this.confirmSwiftOrder(hash, confirmDuration);\n\n\t\tif (!confirmResponse.success) {\n\t\t\tsubscriber.next({\n\t\t\t\ttype: confirmResponse.body.status as 'expired',\n\t\t\t\thash,\n\t\t\t\tmessage: confirmResponse.message,\n\t\t\t\tstatus: confirmResponse.status,\n\t\t\t});\n\t\t\tsubscriber.error();\n\t\t}\n\t\tif (confirmResponse.body.status === 'confirmed') {\n\t\t\tsubscriber.next({\n\t\t\t\ttype: 'confirmed',\n\t\t\t\torderId: confirmResponse.body.orderId,\n\t\t\t\thash,\n\t\t\t});\n\t\t\tsubscriber.complete();\n\t\t}\n\t}\n\tstatic async handleSwiftOrderSubscriberWS(\n\t\tsubscriber: Subscriber<SwiftOrderEvent>,\n\t\tconnection: Connection,\n\t\tclient: DriftClient,\n\t\tmarketIndex: number,\n\t\tmarketType: MarketType,\n\t\tmessage: string,\n\t\tsignature: Buffer,\n\t\ttakerPubkey: PublicKey,\n\t\tsignedMsgUserOrdersAccountPubkey: PublicKey,\n\t\tsignedMsgOrderUuid: Uint8Array,\n\t\tconfirmDuration: number,\n\t\tsigningAuthority?: PublicKey\n\t) {\n\t\t// First send the order\n\t\tconst sendResponse = await this.sendSwiftOrder(\n\t\t\tmarketIndex,\n\t\t\tmarketType,\n\t\t\tmessage,\n\t\t\tsignature,\n\t\t\ttakerPubkey,\n\t\t\tsigningAuthority\n\t\t);\n\t\tallEnvDlog('swiftClient', 'sendResponse\\n', sendResponse);\n\n\t\tif (!sendResponse.success) {\n\t\t\tsubscriber.next({\n\t\t\t\ttype: 'errored',\n\t\t\t\thash: '',\n\t\t\t\tmessage: 'Error from swift node: ' + sendResponse.message,\n\t\t\t\tstatus: sendResponse.status,\n\t\t\t});\n\t\t\tsubscriber.error();\n\t\t\treturn;\n\t\t} else {\n\t\t\tsubscriber.next({\n\t\t\t\ttype: 'sent',\n\t\t\t\thash: sendResponse.body.hash,\n\t\t\t});\n\t\t}\n\n\t\tconst hash = sendResponse.body.hash;\n\n\t\t// Then confirm it\n\t\tconst orderID = await this.confirmSwiftOrderWS(\n\t\t\tconnection,\n\t\t\tclient,\n\t\t\tsignedMsgUserOrdersAccountPubkey,\n\t\t\tsignedMsgOrderUuid,\n\t\t\tconfirmDuration\n\t\t).catch((err) => {\n\t\t\tallEnvDlog('swiftClient', 'confirmSwiftOrderWS error', err);\n\t\t\tsubscriber.next({\n\t\t\t\ttype: 'expired',\n\t\t\t\thash,\n\t\t\t\tmessage: 'Order failed to confirm',\n\t\t\t\tstatus: 408,\n\t\t\t});\n\t\t\tsubscriber.error();\n\t\t});\n\n\t\tif (!orderID) {\n\t\t\tsubscriber.next({\n\t\t\t\ttype: 'expired',\n\t\t\t\thash,\n\t\t\t\tmessage: 'Order failed to confirm',\n\t\t\t\tstatus: 408,\n\t\t\t});\n\t\t\tsubscriber.error();\n\t\t} else {\n\t\t\tsubscriber.next({\n\t\t\t\ttype: 'confirmed',\n\t\t\t\torderId: orderID.toString(),\n\t\t\t\thash,\n\t\t\t});\n\t\t\tsubscriber.complete();\n\t\t}\n\t}\n\n\tpublic static sendAndConfirmSwiftOrder(\n\t\tmarketIndex: number,\n\t\tmarketType: MarketType,\n\t\tmessage: string,\n\t\tsignature: Buffer,\n\t\ttakerPubkey: PublicKey,\n\t\tconfirmDuration: number,\n\t\tsigningAuthority: PublicKey\n\t): Observable<SwiftOrderEvent> {\n\t\treturn new Observable<SwiftOrderEvent>((subscriber) => {\n\t\t\tthis.handleSwiftOrderSubscriber(\n\t\t\t\tsubscriber,\n\t\t\t\tmarketIndex,\n\t\t\t\tmarketType,\n\t\t\t\tmessage,\n\t\t\t\tsignature,\n\t\t\t\ttakerPubkey,\n\t\t\t\tconfirmDuration,\n\t\t\t\tsigningAuthority\n\t\t\t);\n\t\t});\n\t}\n\n\tpublic static sendAndConfirmSwiftOrderWS(\n\t\tconnection: Connection,\n\t\tclient: DriftClient,\n\t\tmarketIndex: number,\n\t\tmarketType: MarketType,\n\t\tmessage: string,\n\t\tsignature: Buffer,\n\t\ttakerPubkey: PublicKey,\n\t\tsignedMsgUserOrdersAccountPubkey: PublicKey,\n\t\tsignedMsgOrderUuid: Uint8Array,\n\t\tconfirmDuration: number,\n\t\tsigningAuthority: PublicKey\n\t): Observable<SwiftOrderEvent> {\n\t\treturn new Observable<SwiftOrderEvent>((subscriber) => {\n\t\t\tthis.handleSwiftOrderSubscriberWS(\n\t\t\t\tsubscriber,\n\t\t\t\tconnection,\n\t\t\t\tclient,\n\t\t\t\tmarketIndex,\n\t\t\t\tmarketType,\n\t\t\t\tmessage,\n\t\t\t\tsignature,\n\t\t\t\ttakerPubkey,\n\t\t\t\tsignedMsgUserOrdersAccountPubkey,\n\t\t\t\tsignedMsgOrderUuid,\n\t\t\t\tconfirmDuration,\n\t\t\t\tsigningAuthority\n\t\t\t);\n\t\t});\n\t}\n\n\tpublic static async isSwiftServerHealthy(): Promise<boolean> {\n\t\tconst response = await this.get('/health');\n\t\treturn response.status === 200;\n\t}\n\n\tprivate static getSwiftHeaders(): Record<string, string> {\n\t\treturn {\n\t\t\t'Content-Type': 'application/json',\n\t\t\t'X-Swift-Client-Consumer': this.swiftClientConsumer ?? 'default',\n\t\t};\n\t}\n\n\tpublic static isSupportedOrderType(orderType: OrderType) {\n\t\treturn this.supportedOrderTypes.includes(orderType);\n\t}\n}\n"]}
1
+ {"version":3,"file":"swiftClient.js","sourceRoot":"","sources":["../../src/clients/swiftClient.ts"],"names":[],"mappings":"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","sourcesContent":["import { AccountInfo, Connection, PublicKey } from '@solana/web3.js';\nimport {\n\tDriftClient,\n\tMarketType,\n\tOrderType,\n\tSignedMsgOrderParamsDelegateMessage,\n\tSignedMsgOrderParamsMessage,\n\tSignedMsgUserOrdersAccount,\n\tdigestSignature,\n\tisVariant,\n} from '@drift-labs/sdk';\n\n// Cache for URL construction to prevent repeated string concatenation\nconst swiftUrlCache = new Map<string, string>();\nconst MAX_SWIFT_URL_CACHE_SIZE = 200;\n\nfunction getCachedUrl(baseUrl: string, endpoint: string): string {\n\tconst cacheKey = `${baseUrl}:${endpoint}`;\n\n\tif (swiftUrlCache.has(cacheKey)) {\n\t\treturn swiftUrlCache.get(cacheKey)!;\n\t}\n\n\tconst result = `${baseUrl}${endpoint}`;\n\n\tif (swiftUrlCache.size < MAX_SWIFT_URL_CACHE_SIZE) {\n\t\tswiftUrlCache.set(cacheKey, result);\n\t}\n\n\treturn result;\n}\nimport { Observable, Subscriber } from 'rxjs';\nimport { allEnvDlog } from '../utils/logger';\nexport type SwiftServerOrderProcessResponse = {\n\terror?: string;\n\tmessage: string;\n};\n\ntype ClientResponse<T = void> = Promise<{\n\tsuccess: boolean;\n\tbody?: T;\n\tmessage?: string;\n\tstatus?: number;\n}>;\n\ntype BaseSwiftOrderEvent = {\n\thash: string;\n};\n\nexport interface SwiftOrderSentEvent extends BaseSwiftOrderEvent {\n\ttype: 'sent';\n}\n\nexport interface SwiftOrderErroredEvent extends BaseSwiftOrderEvent {\n\ttype: 'errored' | 'expired';\n\tmessage?: string;\n\tstatus?: number;\n}\n\nexport interface SwiftOrderConfirmedEvent extends BaseSwiftOrderEvent {\n\ttype: 'confirmed';\n\torderId: string;\n}\n\nexport type SwiftOrderEvent =\n\t| SwiftOrderErroredEvent\n\t| SwiftOrderConfirmedEvent\n\t| SwiftOrderSentEvent;\n\nexport type SwiftOrderEventWithParams<T extends SwiftOrderEvent> = T & {\n\tswiftOrderUuid: Uint8Array;\n\torderParamsMessage:\n\t\t| SignedMsgOrderParamsMessage\n\t\t| SignedMsgOrderParamsDelegateMessage;\n};\n\nexport class SwiftClient {\n\tprivate static baseUrl = '';\n\tprivate static swiftClientConsumer?: string;\n\n\tstatic supportedOrderTypes: OrderType[] = [OrderType.MARKET, OrderType.LIMIT];\n\n\tpublic static init(baseUrl: string, swiftClientConsumer?: string) {\n\t\tthis.baseUrl = baseUrl;\n\t\tthis.swiftClientConsumer = swiftClientConsumer;\n\t}\n\n\tprivate static get(url: string) {\n\t\tif (!this.baseUrl) {\n\t\t\tthrow new Error('SwiftClient not initialized');\n\t\t}\n\n\t\treturn new Promise<{ success: boolean; body: string; status: number }>(\n\t\t\t(res) => {\n\t\t\t\tconst headers = new Headers({\n\t\t\t\t\t...this.getSwiftHeaders(),\n\t\t\t\t});\n\n\t\t\t\tfetch(getCachedUrl(this.baseUrl, url), {\n\t\t\t\t\theaders,\n\t\t\t\t})\n\t\t\t\t\t.then(async (response) => {\n\t\t\t\t\t\tif (!response.ok) {\n\t\t\t\t\t\t\tres({\n\t\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\t\tbody: await response.text(),\n\t\t\t\t\t\t\t\tstatus: response.status,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tres({\n\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\tbody: await response.text(),\n\t\t\t\t\t\t\tstatus: response.status,\n\t\t\t\t\t\t});\n\t\t\t\t\t})\n\t\t\t\t\t.catch((err) => {\n\t\t\t\t\t\tres({ success: false, body: err, status: 0 });\n\t\t\t\t\t});\n\t\t\t}\n\t\t);\n\t}\n\n\tprivate static post(url: string, bodyObject: any) {\n\t\tif (!this.baseUrl) {\n\t\t\tthrow new Error('SwiftClient not initialized');\n\t\t}\n\n\t\tconst requestOptions = {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { ...this.getSwiftHeaders() },\n\t\t\tbody: JSON.stringify(bodyObject),\n\t\t};\n\n\t\treturn new Promise<{\n\t\t\tsuccess: boolean;\n\t\t\tbody: SwiftServerOrderProcessResponse;\n\t\t\tstatus: number;\n\t\t}>((res) => {\n\t\t\tconst postRequest = new Request(\n\t\t\t\tgetCachedUrl(this.baseUrl, url),\n\t\t\t\trequestOptions\n\t\t\t);\n\n\t\t\tfetch(postRequest)\n\t\t\t\t.then(async (response) => {\n\t\t\t\t\tlet resBody: SwiftServerOrderProcessResponse | null = null;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tresBody =\n\t\t\t\t\t\t\t(await response.json()) as SwiftServerOrderProcessResponse;\n\n\t\t\t\t\t\tres({\n\t\t\t\t\t\t\tsuccess: response.ok,\n\t\t\t\t\t\t\tbody: resBody,\n\t\t\t\t\t\t\tstatus: response.status,\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tallEnvDlog('swiftClient', 'Error reading response body', err);\n\n\t\t\t\t\t\tres({\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\tbody: err as any,\n\t\t\t\t\t\t\tstatus: response.status,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tres({ success: false, body: err, status: 0 });\n\t\t\t\t});\n\t\t});\n\t}\n\n\tstatic async sendSwiftOrder(\n\t\tmarketIndex: number,\n\t\tmarketType: MarketType,\n\t\tmessage: string,\n\t\tsignature: Buffer,\n\t\ttakerPubkey: PublicKey,\n\t\tsigningAuthority?: PublicKey\n\t): ClientResponse<{\n\t\thash: string;\n\t}> {\n\t\tconst requestPayload = {\n\t\t\tmarket_index: marketIndex,\n\t\t\tmarket_type: isVariant(marketType, 'perp') ? 'perp' : 'spot',\n\t\t\tmessage,\n\t\t\tsignature: signature.toString('base64'),\n\t\t\tsigning_authority: signingAuthority?.toBase58() ?? '',\n\t\t\ttaker_authority: takerPubkey.toBase58(),\n\t\t};\n\n\t\tconst response = await this.post('/orders', requestPayload);\n\n\t\tif (response.status !== 200) {\n\t\t\tconsole.error(\n\t\t\t\t`Non-200 status code received for sent Swift order: ${response.status}`\n\t\t\t);\n\t\t\tallEnvDlog('swiftClient', 'full non-200 response body', response.body);\n\t\t\treturn {\n\t\t\t\tmessage:\n\t\t\t\t\tresponse.body?.error ||\n\t\t\t\t\tresponse.body?.message ||\n\t\t\t\t\t`HTTP ${response.status}: Error from Swift server`,\n\t\t\t\tstatus: response.status,\n\t\t\t\tsuccess: false,\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tmessage: `Successfully sent Swift order`,\n\t\t\tbody: {\n\t\t\t\thash: digestSignature(Uint8Array.from(signature)),\n\t\t\t},\n\t\t\tsuccess: true,\n\t\t\tstatus: 200,\n\t\t};\n\t}\n\n\tstatic async confirmSwiftOrderWS(\n\t\tconnection: Connection,\n\t\tclient: DriftClient,\n\t\tsignedMsgUserOrdersAccount: PublicKey,\n\t\tsignedMsgOrderUuid: Uint8Array,\n\t\tconfirmDuration: number\n\t): Promise<number | undefined> {\n\t\tallEnvDlog(\n\t\t\t'swiftClient',\n\t\t\t'confirmSwiftOrderWS - confirmation duration',\n\t\t\tconfirmDuration\n\t\t);\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst timeout = setTimeout(() => {\n\t\t\t\treject(new Error('Order not found'));\n\t\t\t}, confirmDuration);\n\n\t\t\tconnection\n\t\t\t\t.getAccountInfo(signedMsgUserOrdersAccount, 'confirmed')\n\t\t\t\t.then((accountInfo) => {\n\t\t\t\t\tif (!accountInfo) {\n\t\t\t\t\t\treject(new Error('Swift message account not found'));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst order = this.findOrderInSignedMsgUserOrdersAccount(\n\t\t\t\t\t\tclient,\n\t\t\t\t\t\taccountInfo,\n\t\t\t\t\t\tsignedMsgOrderUuid\n\t\t\t\t\t);\n\n\t\t\t\t\tif (order) {\n\t\t\t\t\t\tallEnvDlog(\n\t\t\t\t\t\t\t'swiftClient',\n\t\t\t\t\t\t\t'confirmed in initial fetch orderID\\n',\n\t\t\t\t\t\t\torder.orderId\n\t\t\t\t\t\t);\n\t\t\t\t\t\tclearTimeout(timeout);\n\t\t\t\t\t\tresolve(order.orderId);\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\tconst subId = connection.onAccountChange(\n\t\t\t\tsignedMsgUserOrdersAccount,\n\t\t\t\t(accountInfo) => {\n\t\t\t\t\tconst order = this.findOrderInSignedMsgUserOrdersAccount(\n\t\t\t\t\t\tclient,\n\t\t\t\t\t\taccountInfo,\n\t\t\t\t\t\tsignedMsgOrderUuid\n\t\t\t\t\t);\n\t\t\t\t\tif (order) {\n\t\t\t\t\t\tallEnvDlog(\n\t\t\t\t\t\t\t'swiftClient',\n\t\t\t\t\t\t\t'confirmed in onAccountChange orderID\\n',\n\t\t\t\t\t\t\torder.orderId\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconnection.removeAccountChangeListener(subId);\n\t\t\t\t\t\tclearTimeout(timeout);\n\t\t\t\t\t\tresolve(order.orderId);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t'confirmed'\n\t\t\t);\n\t\t});\n\t}\n\n\tstatic findOrderInSignedMsgUserOrdersAccount(\n\t\tclient: DriftClient,\n\t\tordersAccount: AccountInfo<Buffer>,\n\t\tsignedMsgOrderUuid: Uint8Array\n\t) {\n\t\tconst accountDecoder =\n\t\t\tclient.program.account.signedMsgUserOrders.coder.accounts.decodeUnchecked.bind(\n\t\t\t\tclient.program.account.signedMsgUserOrders.coder.accounts\n\t\t\t);\n\t\tconst decodedAccount = accountDecoder(\n\t\t\t'SignedMsgUserOrders',\n\t\t\tordersAccount.data\n\t\t) as SignedMsgUserOrdersAccount;\n\t\tallEnvDlog(\n\t\t\t'swiftClient findOrder',\n\t\t\t'decodedAccount\\n',\n\t\t\tdecodedAccount,\n\t\t\tsignedMsgOrderUuid.toString()\n\t\t);\n\t\tconst order = decodedAccount.signedMsgOrderData.find(\n\t\t\t(order) => order.uuid.toString() === signedMsgOrderUuid.toString()\n\t\t);\n\t\tallEnvDlog('swiftClient findOrder', 'order\\n', order);\n\t\treturn order;\n\t}\n\n\tstatic async confirmSwiftOrder(\n\t\thash: string,\n\t\tconfirmDuration: number\n\t): Promise<\n\t\tClientResponse<\n\t\t\t| {\n\t\t\t\t\torderId: string;\n\t\t\t\t\tstatus: 'confirmed';\n\t\t\t }\n\t\t\t| {\n\t\t\t\t\tstatus: 'expired';\n\t\t\t }\n\t\t>\n\t> {\n\t\tconst expireTime = Date.now() + confirmDuration;\n\n\t\twhile (Date.now() < expireTime) {\n\t\t\tconst confirmResponse = await this.get(\n\t\t\t\t`/confirmation/hash-status?hash=${encodeURIComponent(hash)}`\n\t\t\t);\n\n\t\t\tif (confirmResponse.status === 200) {\n\t\t\t\tconsole.log('Confirmed hash: ', hash);\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tstatus: 200,\n\t\t\t\t\tmessage: `Confirmed hash: ${hash}`,\n\t\t\t\t\tbody: {\n\t\t\t\t\t\torderId: confirmResponse.body,\n\t\t\t\t\t\tstatus: 'confirmed',\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t} else if (\n\t\t\t\tconfirmResponse.status >= 500 ||\n\t\t\t\tconfirmResponse.status < 200\n\t\t\t) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tawait new Promise((resolve) => setTimeout(resolve, 1000));\n\t\t}\n\n\t\tconsole.error('Failed to confirm hash: ', hash);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\tstatus: 408,\n\t\t\tmessage: `Failed to confirm hash: ${hash}`,\n\t\t\tbody: {\n\t\t\t\tstatus: 'expired',\n\t\t\t},\n\t\t};\n\t}\n\n\tstatic async handleSwiftOrderSubscriber(\n\t\tsubscriber: Subscriber<SwiftOrderEvent>,\n\t\tmarketIndex: number,\n\t\tmarketType: MarketType,\n\t\tmessage: string,\n\t\tsignature: Buffer,\n\t\ttakerPubkey: PublicKey,\n\t\tconfirmDuration: number,\n\t\tsigningAuthority?: PublicKey\n\t) {\n\t\t// First send the order\n\t\tconst sendResponse = await this.sendSwiftOrder(\n\t\t\tmarketIndex,\n\t\t\tmarketType,\n\t\t\tmessage,\n\t\t\tsignature,\n\t\t\ttakerPubkey,\n\t\t\tsigningAuthority\n\t\t);\n\n\t\tif (!sendResponse.success) {\n\t\t\tsubscriber.next({\n\t\t\t\ttype: 'errored',\n\t\t\t\thash: '',\n\t\t\t\tmessage: sendResponse.message,\n\t\t\t\tstatus: sendResponse.status,\n\t\t\t});\n\t\t\tsubscriber.error();\n\t\t\treturn;\n\t\t} else {\n\t\t\tsubscriber.next({\n\t\t\t\ttype: 'sent',\n\t\t\t\thash: sendResponse.body.hash,\n\t\t\t});\n\t\t}\n\n\t\tconst hash = sendResponse.body.hash;\n\n\t\t// Then confirm it\n\t\tconst confirmResponse = await this.confirmSwiftOrder(hash, confirmDuration);\n\n\t\tif (!confirmResponse.success) {\n\t\t\tsubscriber.next({\n\t\t\t\ttype: confirmResponse.body.status as 'expired',\n\t\t\t\thash,\n\t\t\t\tmessage: confirmResponse.message,\n\t\t\t\tstatus: confirmResponse.status,\n\t\t\t});\n\t\t\tsubscriber.error();\n\t\t}\n\t\tif (confirmResponse.body.status === 'confirmed') {\n\t\t\tsubscriber.next({\n\t\t\t\ttype: 'confirmed',\n\t\t\t\torderId: confirmResponse.body.orderId,\n\t\t\t\thash,\n\t\t\t});\n\t\t\tsubscriber.complete();\n\t\t}\n\t}\n\tstatic async handleSwiftOrderSubscriberWS(\n\t\tsubscriber: Subscriber<SwiftOrderEvent>,\n\t\tconnection: Connection,\n\t\tclient: DriftClient,\n\t\tmarketIndex: number,\n\t\tmarketType: MarketType,\n\t\tmessage: string,\n\t\tsignature: Buffer,\n\t\ttakerPubkey: PublicKey,\n\t\tsignedMsgUserOrdersAccountPubkey: PublicKey,\n\t\tsignedMsgOrderUuid: Uint8Array,\n\t\tconfirmDuration: number,\n\t\tsigningAuthority?: PublicKey\n\t) {\n\t\t// First send the order\n\t\tconst sendResponse = await this.sendSwiftOrder(\n\t\t\tmarketIndex,\n\t\t\tmarketType,\n\t\t\tmessage,\n\t\t\tsignature,\n\t\t\ttakerPubkey,\n\t\t\tsigningAuthority\n\t\t);\n\t\tallEnvDlog('swiftClient', 'sendResponse\\n', sendResponse);\n\n\t\tif (!sendResponse.success) {\n\t\t\tsubscriber.next({\n\t\t\t\ttype: 'errored',\n\t\t\t\thash: '',\n\t\t\t\tmessage: `Error from swift node: ${sendResponse.message}`,\n\t\t\t\tstatus: sendResponse.status,\n\t\t\t});\n\t\t\tsubscriber.error();\n\t\t\treturn;\n\t\t} else {\n\t\t\tsubscriber.next({\n\t\t\t\ttype: 'sent',\n\t\t\t\thash: sendResponse.body.hash,\n\t\t\t});\n\t\t}\n\n\t\tconst hash = sendResponse.body.hash;\n\n\t\t// Then confirm it\n\t\tconst orderID = await this.confirmSwiftOrderWS(\n\t\t\tconnection,\n\t\t\tclient,\n\t\t\tsignedMsgUserOrdersAccountPubkey,\n\t\t\tsignedMsgOrderUuid,\n\t\t\tconfirmDuration\n\t\t).catch((err) => {\n\t\t\tallEnvDlog('swiftClient', 'confirmSwiftOrderWS error', err);\n\t\t\tsubscriber.next({\n\t\t\t\ttype: 'expired',\n\t\t\t\thash,\n\t\t\t\tmessage: 'Order failed to confirm',\n\t\t\t\tstatus: 408,\n\t\t\t});\n\t\t\tsubscriber.error();\n\t\t});\n\n\t\tif (!orderID) {\n\t\t\tsubscriber.next({\n\t\t\t\ttype: 'expired',\n\t\t\t\thash,\n\t\t\t\tmessage: 'Order failed to confirm',\n\t\t\t\tstatus: 408,\n\t\t\t});\n\t\t\tsubscriber.error();\n\t\t} else {\n\t\t\tsubscriber.next({\n\t\t\t\ttype: 'confirmed',\n\t\t\t\torderId: orderID.toString(),\n\t\t\t\thash,\n\t\t\t});\n\t\t\tsubscriber.complete();\n\t\t}\n\t}\n\n\tpublic static sendAndConfirmSwiftOrder(\n\t\tmarketIndex: number,\n\t\tmarketType: MarketType,\n\t\tmessage: string,\n\t\tsignature: Buffer,\n\t\ttakerPubkey: PublicKey,\n\t\tconfirmDuration: number,\n\t\tsigningAuthority: PublicKey\n\t): Observable<SwiftOrderEvent> {\n\t\treturn new Observable<SwiftOrderEvent>((subscriber) => {\n\t\t\tthis.handleSwiftOrderSubscriber(\n\t\t\t\tsubscriber,\n\t\t\t\tmarketIndex,\n\t\t\t\tmarketType,\n\t\t\t\tmessage,\n\t\t\t\tsignature,\n\t\t\t\ttakerPubkey,\n\t\t\t\tconfirmDuration,\n\t\t\t\tsigningAuthority\n\t\t\t);\n\t\t});\n\t}\n\n\tpublic static sendAndConfirmSwiftOrderWS(\n\t\tconnection: Connection,\n\t\tclient: DriftClient,\n\t\tmarketIndex: number,\n\t\tmarketType: MarketType,\n\t\tmessage: string,\n\t\tsignature: Buffer,\n\t\ttakerPubkey: PublicKey,\n\t\tsignedMsgUserOrdersAccountPubkey: PublicKey,\n\t\tsignedMsgOrderUuid: Uint8Array,\n\t\tconfirmDuration: number,\n\t\tsigningAuthority: PublicKey\n\t): Observable<SwiftOrderEvent> {\n\t\treturn new Observable<SwiftOrderEvent>((subscriber) => {\n\t\t\tthis.handleSwiftOrderSubscriberWS(\n\t\t\t\tsubscriber,\n\t\t\t\tconnection,\n\t\t\t\tclient,\n\t\t\t\tmarketIndex,\n\t\t\t\tmarketType,\n\t\t\t\tmessage,\n\t\t\t\tsignature,\n\t\t\t\ttakerPubkey,\n\t\t\t\tsignedMsgUserOrdersAccountPubkey,\n\t\t\t\tsignedMsgOrderUuid,\n\t\t\t\tconfirmDuration,\n\t\t\t\tsigningAuthority\n\t\t\t);\n\t\t});\n\t}\n\n\tpublic static async isSwiftServerHealthy(): Promise<boolean> {\n\t\tconst response = await this.get('/health');\n\t\treturn response.status === 200;\n\t}\n\n\tprivate static getSwiftHeaders(): Record<string, string> {\n\t\treturn {\n\t\t\t'Content-Type': 'application/json',\n\t\t\t'X-Swift-Client-Consumer': this.swiftClientConsumer ?? 'default',\n\t\t};\n\t}\n\n\tpublic static isSupportedOrderType(orderType: OrderType) {\n\t\treturn this.supportedOrderTypes.includes(orderType);\n\t}\n}\n"]}