@pear-protocol/exchanges-sdk 0.21.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -69,7 +69,6 @@ if (status.status === 'warning') {
69
69
  }
70
70
 
71
71
  const tracker = exchanges.createTracker(connection);
72
- const syncer = exchanges.createSyncer(connection);
73
72
  await tracker.start();
74
73
 
75
74
  const offBalance = tracker.trackBalance((balance) => {
@@ -97,7 +96,7 @@ Hyperliquid and Lighter public REST/WebSocket reads are routed to testnet. Light
97
96
 
98
97
  ## Connection
99
98
 
100
- `connect(tradeAccountId)` fetches decrypted credentials and the trade account from the core API. It does **not** open a socket — the exchange WebSocket is created lazily when you call `tracker.start()` and torn down by `tracker.disconnect()`. Syncers perform one-shot REST reads. For an account snapshot with no socket, use [`snapshot()`](#one-shot-snapshot-no-websocket).
99
+ `connect(tradeAccountId)` fetches decrypted credentials and the trade account from the core API. It does **not** open a socket — the exchange WebSocket is created lazily when you call `tracker.start()` and torn down by `tracker.disconnect()`. For an account snapshot with no socket, use [`snapshot()`](#one-shot-snapshot-no-websocket).
101
100
 
102
101
  The connector comes from the backend trade account.
103
102
 
@@ -126,8 +125,6 @@ const exchanges = new ExchangesSDK({
126
125
 
127
126
  Hyperliquid status requires the builder address and the minimum approved fee in tenths of a basis point. Lighter status requires the integrator account and minimum maker/taker approval caps in Lighter fee ticks (parts per million). These thresholds must match the fees configured by the trading backend.
128
127
 
129
- Historical Hyperliquid fill synchronization identifies fills by bare `tid` and requests them aggregated by time, so partial fills of one crossing order arrive as a single fill. Consumers that persisted composite `time:coin:tid` identifiers must account for the transition overlap. Hyperliquid serves only the 10,000 most recent fills; an account with a longer history imports from that boundary forward, and the snapshot-alignment synthetics cover the residual gap. Synchronization fails closed when the exchange reports a maker rebate: the current core external-fill contract accepts only non-negative fees, so the SDK will not silently turn a rebate into a cost or advance the synchronization watermark past it.
130
-
131
128
  ## Public Tracker (address only)
132
129
 
133
130
  Hyperliquid answers account state for any address with no key, so an account the caller does not own, such as a vault's agent, can be tracked without `connect()`. `createPublicTracker` fetches no credentials and needs no trade account:
@@ -76,71 +76,5 @@ async function fetchMultiAssetsMargin(creds, demo) {
76
76
  return res.multiAssetsMargin;
77
77
  }
78
78
  __name(fetchMultiAssetsMargin, "fetchMultiAssetsMargin");
79
- const USER_TRADES_LIMIT = 1e3;
80
- const ALL_ORDERS_LIMIT = 1e3;
81
- const BINANCE_MAX_WINDOW_MS = 7 * 24 * 60 * 60 * 1e3;
82
- async function fetchUserTradesSince(creds, symbols, since, demo) {
83
- const now = Date.now();
84
- const results = [];
85
- for (const symbol of symbols) {
86
- let windowStart = since.getTime();
87
- while (windowStart <= now) {
88
- const windowEnd = Math.min(windowStart + BINANCE_MAX_WINDOW_MS - 1, now);
89
- let pageStart = windowStart;
90
- while (pageStart <= windowEnd) {
91
- const page = await signedGet(creds, "/fapi/v1/userTrades", {
92
- symbol,
93
- startTime: pageStart.toString(),
94
- endTime: windowEnd.toString(),
95
- limit: USER_TRADES_LIMIT.toString()
96
- }, demo);
97
- results.push(...page);
98
- if (page.length < USER_TRADES_LIMIT) break;
99
- const last = page[page.length - 1];
100
- if (!last || last.time <= pageStart) break;
101
- pageStart = last.time + 1;
102
- }
103
- windowStart = windowEnd + 1;
104
- }
105
- }
106
- const byId = /* @__PURE__ */ new Map();
107
- for (const trade of results) {
108
- byId.set(`${trade.symbol}:${trade.id}`, trade);
109
- }
110
- return Array.from(byId.values());
111
- }
112
- __name(fetchUserTradesSince, "fetchUserTradesSince");
113
- async function fetchAllOrdersSince(creds, symbols, since, demo) {
114
- const now = Date.now();
115
- const results = [];
116
- for (const symbol of symbols) {
117
- let windowStart = since.getTime();
118
- while (windowStart <= now) {
119
- const windowEnd = Math.min(windowStart + BINANCE_MAX_WINDOW_MS - 1, now);
120
- let pageStart = windowStart;
121
- while (pageStart <= windowEnd) {
122
- const page = await signedGet(creds, "/fapi/v1/allOrders", {
123
- symbol,
124
- startTime: pageStart.toString(),
125
- endTime: windowEnd.toString(),
126
- limit: ALL_ORDERS_LIMIT.toString()
127
- }, demo);
128
- results.push(...page);
129
- if (page.length < ALL_ORDERS_LIMIT) break;
130
- const last = page[page.length - 1];
131
- const lastTs = last ? Math.max(last.updateTime, last.time) : pageStart;
132
- if (lastTs <= pageStart) break;
133
- pageStart = lastTs + 1;
134
- }
135
- windowStart = windowEnd + 1;
136
- }
137
- }
138
- const byId = /* @__PURE__ */ new Map();
139
- for (const order of results) {
140
- byId.set(`${order.symbol}:${order.orderId}`, order);
141
- }
142
- return Array.from(byId.values());
143
- }
144
- __name(fetchAllOrdersSince, "fetchAllOrdersSince");
145
79
 
146
- export { createListenKey, fetchAccountStatus, fetchAllOrdersSince, fetchAllPositionRisk, fetchMultiAssetsMargin, fetchPositionRisk, fetchUserTradesSince, keepAliveListenKey };
80
+ export { createListenKey, fetchAccountStatus, fetchAllPositionRisk, fetchMultiAssetsMargin, fetchPositionRisk, keepAliveListenKey };
@@ -76,36 +76,5 @@ async function fetchCollateralRatios(creds, demo) {
76
76
  return ratios;
77
77
  }
78
78
  __name(fetchCollateralRatios, "fetchCollateralRatios");
79
- const EXECUTIONS_LIMIT = 100;
80
- const BYBIT_MAX_WINDOW_MS = 7 * 24 * 60 * 60 * 1e3;
81
- async function fetchExecutionsSince(creds, since, demo) {
82
- const results = [];
83
- const now = Date.now();
84
- let windowStart = since.getTime();
85
- while (windowStart <= now) {
86
- const windowEnd = Math.min(windowStart + BYBIT_MAX_WINDOW_MS - 1, now);
87
- let cursor;
88
- do {
89
- const result = await signedGet(creds, "/v5/execution/list", {
90
- category: "linear",
91
- startTime: windowStart.toString(),
92
- endTime: windowEnd.toString(),
93
- limit: EXECUTIONS_LIMIT.toString(),
94
- ...cursor ? {
95
- cursor
96
- } : {}
97
- }, demo);
98
- results.push(...result.list ?? []);
99
- cursor = result.nextPageCursor || void 0;
100
- } while (cursor);
101
- windowStart = windowEnd + 1;
102
- }
103
- const byId = /* @__PURE__ */ new Map();
104
- for (const execution of results) {
105
- byId.set(execution.execId, execution);
106
- }
107
- return Array.from(byId.values());
108
- }
109
- __name(fetchExecutionsSince, "fetchExecutionsSince");
110
79
 
111
- export { fetchAccountInfo, fetchCollateralRatios, fetchExecutionsSince, fetchPositionLeverage, fetchPositions, fetchTransferableAmount, fetchWalletBalance };
80
+ export { fetchAccountInfo, fetchCollateralRatios, fetchPositionLeverage, fetchPositions, fetchTransferableAmount, fetchWalletBalance };
@@ -45,11 +45,9 @@ async function createExchangeWs(exchange, config) {
45
45
  connector: exchange,
46
46
  demo: config.demo
47
47
  });
48
- default: {
49
- const _exhaustive = exchange;
50
- throw new Error(`Unsupported exchange: ${String(_exhaustive)}`);
51
- }
52
48
  }
49
+ const unsupported = exchange;
50
+ throw new Error(`Unsupported exchange: ${String(unsupported)}`);
53
51
  }
54
52
  __name(createExchangeWs, "createExchangeWs");
55
53
 
@@ -187,11 +187,9 @@ async function fetchAccountsByL1Address(l1Address, connector, demo = false) {
187
187
  };
188
188
  }
189
189
  __name(fetchAccountsByL1Address, "fetchAccountsByL1Address");
190
- async function fetchAccount(accountIndex, authToken, connector, demo = false, throttle) {
191
- return gatedLighterRead(throttle, "account", async () => {
192
- const response = await authenticatedLighterFetch(`${getLighterBaseUrl(connector, demo)}/api/v1/account?by=index&value=${accountIndex}`, authToken);
193
- return readLighterResponse(response, "/account");
194
- }, () => 0);
190
+ async function fetchAccount(accountIndex, authToken, connector, demo = false) {
191
+ const response = await authenticatedLighterFetch(`${getLighterBaseUrl(connector, demo)}/api/v1/account?by=index&value=${accountIndex}`, authToken);
192
+ return readLighterResponse(response, "/account");
195
193
  }
196
194
  __name(fetchAccount, "fetchAccount");
197
195
  async function fetchPublicAccount(accountIndex, connector, demo = false) {
@@ -256,78 +254,6 @@ async function fetchAssetDetails(connector, demo) {
256
254
  return data.asset_details;
257
255
  }
258
256
  __name(fetchAssetDetails, "fetchAssetDetails");
259
- async function fetchTradesSince(accountIndex, authToken, since, connector, demo = false, throttle) {
260
- const results = [];
261
- const seenCursors = /* @__PURE__ */ new Set();
262
- let cursor;
263
- do {
264
- const params = new URLSearchParams({
265
- market_type: "all",
266
- account_index: accountIndex.toString(),
267
- sort_by: "timestamp",
268
- sort_dir: "desc",
269
- ask_filter: "-1",
270
- role: "all",
271
- type: "all",
272
- limit: "100",
273
- aggregate: "false",
274
- ...cursor ? {
275
- cursor
276
- } : {}
277
- });
278
- const data = await gatedLighterRead(throttle, "trades", async () => {
279
- const response = await authenticatedLighterFetch(`${getLighterBaseUrl(connector, demo)}/api/v1/trades?${params.toString()}`, authToken);
280
- return readLighterResponse(response, "/trades");
281
- }, (page2) => (page2.trades ?? []).length);
282
- const page = data.trades ?? [];
283
- results.push(...page.filter((trade) => trade.timestamp >= since.getTime()));
284
- const reachedSince = page.some((trade) => trade.timestamp < since.getTime());
285
- const nextCursor = data.next_cursor || void 0;
286
- cursor = !reachedSince && nextCursor && !seenCursors.has(nextCursor) ? nextCursor : void 0;
287
- if (cursor) {
288
- seenCursors.add(cursor);
289
- }
290
- } while (cursor);
291
- const byId = /* @__PURE__ */ new Map();
292
- for (const trade of results) {
293
- byId.set(trade.trade_id_str ?? trade.trade_id.toString(), trade);
294
- }
295
- return Array.from(byId.values());
296
- }
297
- __name(fetchTradesSince, "fetchTradesSince");
298
- async function fetchPositionFundingSince(accountIndex, authToken, since, connector, demo = false, throttle) {
299
- const results = /* @__PURE__ */ new Map();
300
- const seenCursors = /* @__PURE__ */ new Set();
301
- let cursor;
302
- const sinceSeconds = Math.max(1, Math.floor(since.getTime() / 1e3));
303
- do {
304
- const params = new URLSearchParams({
305
- account_index: accountIndex.toString(),
306
- limit: "100",
307
- side: "all",
308
- start_timestamp: sinceSeconds.toString(),
309
- ...cursor ? {
310
- cursor
311
- } : {}
312
- });
313
- const data = await gatedLighterRead(throttle, "positionFunding", async () => {
314
- const response = await authenticatedLighterFetch(`${getLighterBaseUrl(connector, demo)}/api/v1/positionFunding?${params.toString()}`, authToken);
315
- return readLighterResponse(response, "/positionFunding");
316
- }, (page) => (page.position_fundings ?? []).length);
317
- for (const funding of data.position_fundings ?? []) {
318
- if (funding.timestamp >= sinceSeconds) {
319
- results.set(`${funding.market_id}:${funding.funding_id}`, funding);
320
- }
321
- }
322
- const nextCursor = data.next_cursor || void 0;
323
- cursor = nextCursor && !seenCursors.has(nextCursor) ? nextCursor : void 0;
324
- if (cursor) {
325
- seenCursors.add(cursor);
326
- }
327
- } while (cursor);
328
- return Array.from(results.values());
329
- }
330
- __name(fetchPositionFundingSince, "fetchPositionFundingSince");
331
257
  async function submitReferralCode(params, connector, demo = false) {
332
258
  const body = new URLSearchParams({
333
259
  l1_address: params.l1Address,
@@ -402,21 +328,6 @@ async function fetchUserReferrals(params, connector, demo = false) {
402
328
  };
403
329
  }
404
330
  __name(fetchUserReferrals, "fetchUserReferrals");
405
- async function gatedLighterRead(throttle, requestType, run, countItems) {
406
- if (!throttle) {
407
- return run();
408
- }
409
- await throttle.beforeRequest(requestType);
410
- try {
411
- const result = await run();
412
- throttle.afterRequest(requestType, countItems(result));
413
- return result;
414
- } catch (error) {
415
- throttle.afterRequestError(requestType, error);
416
- throw error;
417
- }
418
- }
419
- __name(gatedLighterRead, "gatedLighterRead");
420
331
  async function authenticatedLighterFetch(url, authToken, init) {
421
332
  const headers = new Headers(init?.headers);
422
333
  headers.set("authorization", authToken);
@@ -536,4 +447,4 @@ function readReferralAmount(value) {
536
447
  }
537
448
  __name(readReferralAmount, "readReferralAmount");
538
449
 
539
- export { LighterRequestError, createDepositIntentAddress, fetchAccount, fetchAccountLimits, fetchAccountMetadata, fetchAccountMetadataByL1Address, fetchAccountsByL1Address, fetchApiKeys, fetchAssetDetails, fetchDepositHistory, fetchDepositNetworks, fetchOrderBookDetails, fetchPositionFundingSince, fetchPublicAccount, fetchPublicOrderBookDetails, fetchTradesSince, fetchUserReferrals, fetchWithdrawHistory, submitReferralCode };
450
+ export { LighterRequestError, createDepositIntentAddress, fetchAccount, fetchAccountLimits, fetchAccountMetadata, fetchAccountMetadataByL1Address, fetchAccountsByL1Address, fetchApiKeys, fetchAssetDetails, fetchDepositHistory, fetchDepositNetworks, fetchOrderBookDetails, fetchPublicAccount, fetchPublicOrderBookDetails, fetchUserReferrals, fetchWithdrawHistory, submitReferralCode };
@@ -72,30 +72,5 @@ async function fetchLeverageInfos(creds, instIds, demo) {
72
72
  return results.flat();
73
73
  }
74
74
  __name(fetchLeverageInfos, "fetchLeverageInfos");
75
- const FILLS_HISTORY_LIMIT = 100;
76
- async function fetchFillsHistorySince(creds, since, demo) {
77
- const results = [];
78
- let after;
79
- do {
80
- const page = await signedGet(creds, "/api/v5/trade/fills-history", {
81
- instType: "SWAP",
82
- begin: since.getTime().toString(),
83
- end: Date.now().toString(),
84
- limit: FILLS_HISTORY_LIMIT.toString(),
85
- ...after ? {
86
- after
87
- } : {}
88
- }, demo);
89
- results.push(...page);
90
- const last = page[page.length - 1];
91
- after = page.length === FILLS_HISTORY_LIMIT && last?.billId ? last.billId : void 0;
92
- } while (after);
93
- const byId = /* @__PURE__ */ new Map();
94
- for (const fill of results) {
95
- byId.set(fill.billId || fill.tradeId, fill);
96
- }
97
- return Array.from(byId.values());
98
- }
99
- __name(fetchFillsHistorySince, "fetchFillsHistorySince");
100
75
 
101
- export { fetchAccountConfig, fetchBalance, fetchFillsHistorySince, fetchLeverageInfos, fetchMaxWithdrawals, fetchPositions };
76
+ export { fetchAccountConfig, fetchBalance, fetchLeverageInfos, fetchMaxWithdrawals, fetchPositions };