@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.
@@ -1,241 +0,0 @@
1
- import { computePositionEntryPrices, abs, isZero, add, multiply, divide, computeSyncPayload } from '@pear-protocol/utils';
2
-
3
- var __defProp = Object.defineProperty;
4
- var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
5
- const OVERLAP_MS = 6e4;
6
- class BaseAccountSyncer {
7
- static {
8
- __name(this, "BaseAccountSyncer");
9
- }
10
- sdk;
11
- connector;
12
- account;
13
- credentials;
14
- demo;
15
- throttle;
16
- constructor(config) {
17
- this.sdk = config.sdk;
18
- this.connector = config.connector;
19
- this.account = config.account;
20
- this.credentials = config.credentials;
21
- this.demo = config.demo;
22
- this.throttle = config.throttle;
23
- }
24
- // Phase A: Load current Pear state (positions, fills, instruments, watermark).
25
- async loadPearState() {
26
- const watermark = await this.sdk.core.sync.fills.since({
27
- overlapMs: OVERLAP_MS
28
- });
29
- const [{ positions: openPositions }, { fills: provisionalFills }, { instruments }] = await Promise.all([
30
- this.sdk.core.positions.openLite(),
31
- this.sdk.core.fills.provisionals(),
32
- this.sdk.core.instruments.lite({
33
- connector: this.connector
34
- })
35
- ]);
36
- const entryBySymbol = await this.loadEntryBySymbol(openPositions);
37
- return {
38
- openPositions,
39
- entryBySymbol,
40
- provisionalFills,
41
- instruments,
42
- watermark
43
- };
44
- }
45
- /**
46
- * Global per-symbol cost-basis entry of existing open positions, derived from each
47
- * position's assigned ledger and remaining exposure. One call per open position today —
48
- * can collapse to a single endpoint later.
49
- */
50
- async loadEntryBySymbol(openPositions) {
51
- const states = await Promise.all(openPositions.map(async (position) => {
52
- const { fills } = await this.sdk.core.fills.details(position.id);
53
- fills.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
54
- return {
55
- exposure: position.exposure,
56
- entryPrices: computePositionEntryPrices(position.exposure, fills)
57
- };
58
- }));
59
- const totals = /* @__PURE__ */ new Map();
60
- for (const state of states) {
61
- for (const [symbol, signedQuantity] of Object.entries(state.exposure)) {
62
- const quantity = abs(signedQuantity);
63
- if (isZero(quantity)) {
64
- continue;
65
- }
66
- const entry = state.entryPrices[symbol];
67
- const existing = totals.get(symbol);
68
- totals.set(symbol, {
69
- quantity: add(existing?.quantity ?? "0", quantity).toFixed(),
70
- notional: add(existing?.notional ?? "0", multiply(quantity, entry ?? "0")).toFixed(),
71
- unpriced: existing?.unpriced === true || entry === void 0
72
- });
73
- }
74
- }
75
- const entryBySymbol = {};
76
- for (const [symbol, total] of totals) {
77
- if (!total.unpriced) {
78
- entryBySymbol[symbol] = divide(total.notional, total.quantity).toFixed();
79
- }
80
- }
81
- return entryBySymbol;
82
- }
83
- // Phase B: Snapshot the exchange — fetch positions and fills since the watermark.
84
- async snapshotExchange(state) {
85
- const { openPositions, provisionalFills, instruments, watermark } = state;
86
- const { firstSyncStartedAt } = watermark;
87
- const externalFills = await this.listExternalFills({
88
- since: new Date(watermark.sinceMs),
89
- instruments,
90
- openPositions,
91
- provisionalFills
92
- });
93
- const exchangePositions = await this.listExternalPositions({
94
- instruments
95
- });
96
- return {
97
- exchangePositions,
98
- externalFills,
99
- firstSyncStartedAt
100
- };
101
- }
102
- // Phase C: Compute the sync payload and post the applicables to the create endpoint.
103
- async computeAndPostSync(state, snapshot) {
104
- const { openPositions, entryBySymbol, provisionalFills, watermark } = state;
105
- const { exchangePositions, externalFills, firstSyncStartedAt } = snapshot;
106
- let payload = await computeSyncPayload({
107
- tradeAccountId: this.account.id,
108
- connector: this.connector,
109
- openPositions,
110
- entryBySymbol,
111
- provisionalFills,
112
- confirmedExchangeFillIds: watermark.confirmedExchangeFillIds ?? [],
113
- externalFills,
114
- exchangePositions,
115
- neverSynced: watermark.neverSynced
116
- });
117
- if (!payload) {
118
- if (watermark.hasPendingSynthetics !== true) {
119
- return;
120
- }
121
- payload = {
122
- fills: [],
123
- applicables: [],
124
- exchangePositions,
125
- meta: {
126
- externalMaxTs: null
127
- },
128
- firstSyncTrim: null
129
- };
130
- }
131
- const { firstSyncTrim } = payload;
132
- let meta = {
133
- ...payload.meta,
134
- ...payload.meta.externalMaxTs === null ? {} : {
135
- newestFillTs: payload.meta.externalMaxTs
136
- },
137
- ...firstSyncStartedAt === void 0 ? {} : {
138
- firstSyncStartedAt
139
- },
140
- // Core defers the create when any execution write postdates this server-minted token.
141
- executionsUpdatedAtEpochMs: watermark.executionsUpdatedAtEpochMs
142
- };
143
- if (firstSyncTrim) {
144
- console.log(`${this.connector}: first-sync trim \u2014 dropped ${firstSyncTrim.trimmedCount} pre-cutoff fills (cutoffTs=${firstSyncTrim.cutoffTs})`);
145
- if (meta.externalMaxTs !== null && firstSyncTrim.trimmedMaxTs >= meta.externalMaxTs - OVERLAP_MS) {
146
- meta = {
147
- ...meta,
148
- externalMaxTs: meta.externalMaxTs + OVERLAP_MS + 1
149
- };
150
- }
151
- }
152
- const createBody = {
153
- fills: payload.fills,
154
- applicables: payload.applicables,
155
- exchangePositions: payload.exchangePositions,
156
- meta
157
- };
158
- const createBytes = new Blob([
159
- JSON.stringify(createBody)
160
- ]).size;
161
- if (createBytes > 50 * 1024) {
162
- await this.sdk.core.sync.fills.createBulk(createBody);
163
- } else {
164
- await this.sdk.core.sync.fills.create(createBody);
165
- }
166
- }
167
- /**
168
- * Pull historical funding payments since the funding watermark and post them.
169
- * Independent of the fill sync cycle (`synchronizeFills`) — funding rows are
170
- * immutable and upsert idempotently, so this is driven on its own cadence.
171
- */
172
- async synchronizeFunding() {
173
- const [{ instruments }, watermark] = await Promise.all([
174
- this.sdk.core.instruments.lite({
175
- connector: this.connector
176
- }),
177
- this.sdk.core.sync.funding.since({
178
- overlapMs: 6e4
179
- })
180
- ]);
181
- let payments;
182
- try {
183
- payments = await this.listExternalFunding({
184
- since: new Date(watermark.sinceMs),
185
- instruments
186
- });
187
- } catch (error) {
188
- console.error(`${this.connector}: funding fetch failed \u2014 funding sync aborted, retry next tick`, error);
189
- return "aborted";
190
- }
191
- if (payments.length === 0) {
192
- return "synced";
193
- }
194
- const externalMaxTs = Math.max(...payments.map((payment) => payment.timestamp));
195
- const createBody = {
196
- payments,
197
- meta: {
198
- externalMaxTs
199
- }
200
- };
201
- const bytes = new Blob([
202
- JSON.stringify(createBody)
203
- ]).size;
204
- if (bytes > 50 * 1024) {
205
- await this.sdk.core.sync.funding.createBulk(createBody);
206
- return "synced";
207
- }
208
- await this.sdk.core.sync.funding.create(createBody);
209
- return "synced";
210
- }
211
- async synchronizeFills() {
212
- const state = await this.loadPearState();
213
- let snapshot;
214
- try {
215
- snapshot = await this.snapshotExchange(state);
216
- } catch (error) {
217
- console.error(`${this.connector}: position/fill fetch failed \u2014 sync cycle aborted, retry next tick`, error);
218
- return "aborted";
219
- }
220
- try {
221
- await this.computeAndPostSync(state, snapshot);
222
- } catch (error) {
223
- if (isSyncCreateDeferral(error)) {
224
- console.log(`${this.connector}: sync cycle deferred by the server (${error.errorCode}) \u2014 retry next tick`);
225
- return "deferred";
226
- }
227
- throw error;
228
- }
229
- return "synced";
230
- }
231
- }
232
- function isSyncCreateDeferral(error) {
233
- if (typeof error !== "object" || error === null) {
234
- return false;
235
- }
236
- const { status, errorCode } = error;
237
- return status === 409 && typeof errorCode === "string" && errorCode.startsWith("SYNC_CREATE_");
238
- }
239
- __name(isSyncCreateDeferral, "isSyncCreateDeferral");
240
-
241
- export { BaseAccountSyncer };
@@ -1,112 +0,0 @@
1
- import BigNumber from 'bignumber.js';
2
- import { fetchAllPositionRisk, fetchUserTradesSince, fetchAllOrdersSince } from '../../client/binance/rest';
3
- import { BaseAccountSyncer } from '../base';
4
- import { toSignedTradeFee } from '../trade-fee';
5
-
6
- var __defProp = Object.defineProperty;
7
- var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
8
- class BinanceAccountSyncer extends BaseAccountSyncer {
9
- static {
10
- __name(this, "BinanceAccountSyncer");
11
- }
12
- creds;
13
- constructor(config) {
14
- super(config);
15
- this.creds = config.credentials;
16
- }
17
- async listExternalPositions({ instruments }) {
18
- const contractSizeByInstrumentId = new Map(instruments.map((instrument) => [
19
- instrument.id,
20
- instrument.precision.quantity.contractSize
21
- ]));
22
- const rawPositions = await fetchAllPositionRisk(this.creds, this.demo);
23
- return rawPositions.reduce((positions, rawPosition) => {
24
- const signedSize = new BigNumber(rawPosition.positionAmt);
25
- if (signedSize.isZero()) {
26
- return positions;
27
- }
28
- const contractSize = contractSizeByInstrumentId.get(rawPosition.symbol) ?? 1;
29
- const size = signedSize.abs().times(contractSize).toFixed();
30
- const side = signedSize.isGreaterThan(0) ? "BUY" : "SELL";
31
- positions.push({
32
- symbol: rawPosition.symbol,
33
- size,
34
- side,
35
- entryPrice: rawPosition.entryPrice,
36
- markPrice: rawPosition.markPrice
37
- });
38
- return positions;
39
- }, []);
40
- }
41
- async listExternalFills({ since, instruments, openPositions, provisionalFills }) {
42
- const knownInstrumentIds = new Set(instruments.map((instrument) => instrument.id));
43
- const requestedInstrumentIds = this.collectRequestedInstrumentIds(openPositions, provisionalFills);
44
- const instrumentIds = [
45
- ...requestedInstrumentIds
46
- ].filter((instrumentId) => knownInstrumentIds.has(instrumentId));
47
- if (!instrumentIds.length) {
48
- return [];
49
- }
50
- const [trades, orders] = await Promise.all([
51
- fetchUserTradesSince(this.creds, instrumentIds, since, this.demo),
52
- fetchAllOrdersSince(this.creds, instrumentIds, since, this.demo)
53
- ]);
54
- const ordersBySymbolOrderId = this.mapOrdersBySymbolOrderId(orders);
55
- return trades.reduce((fills, rawFill) => {
56
- if (!requestedInstrumentIds.has(rawFill.symbol)) {
57
- return fills;
58
- }
59
- const order = ordersBySymbolOrderId.get(this.orderKey(rawFill.symbol, rawFill.orderId));
60
- fills.push({
61
- connector: "binance",
62
- exchangeFillId: rawFill.id.toString(),
63
- cloid: order?.clientOrderId || null,
64
- symbol: rawFill.symbol,
65
- price: rawFill.price,
66
- quantity: rawFill.qty,
67
- side: rawFill.side,
68
- reduceOnly: this.isReduceOnly(rawFill),
69
- timestamp: rawFill.time,
70
- tradeFee: toSignedTradeFee("binance", rawFill.commission),
71
- builderFee: "0",
72
- liquidation: null
73
- });
74
- return fills;
75
- }, []);
76
- }
77
- async listExternalFunding(_params) {
78
- throw new Error("funding sync is not implemented for binance");
79
- }
80
- isReduceOnly(rawFill) {
81
- if (rawFill.positionSide === "LONG") return rawFill.side === "SELL";
82
- if (rawFill.positionSide === "SHORT") return rawFill.side === "BUY";
83
- if (rawFill.positionSide === "BOTH") {
84
- return !new BigNumber(rawFill.realizedPnl || 0).isZero();
85
- }
86
- return false;
87
- }
88
- collectRequestedInstrumentIds(openPositions, provisionalFills) {
89
- const instrumentIds = /* @__PURE__ */ new Set();
90
- for (const position of openPositions) {
91
- for (const instrumentId of Object.keys(position.exposure)) {
92
- instrumentIds.add(instrumentId);
93
- }
94
- }
95
- for (const fill of provisionalFills) {
96
- instrumentIds.add(fill.symbol);
97
- }
98
- return instrumentIds;
99
- }
100
- mapOrdersBySymbolOrderId(orders) {
101
- const bySymbolOrderId = /* @__PURE__ */ new Map();
102
- for (const order of orders) {
103
- bySymbolOrderId.set(this.orderKey(order.symbol, order.orderId), order);
104
- }
105
- return bySymbolOrderId;
106
- }
107
- orderKey(symbol, orderId) {
108
- return `${symbol}:${orderId}`;
109
- }
110
- }
111
-
112
- export { BinanceAccountSyncer };
@@ -1,68 +0,0 @@
1
- import BigNumber from 'bignumber.js';
2
- import { fetchPositions, fetchExecutionsSince } from '../../client/bybit/rest';
3
- import { BaseAccountSyncer } from '../base';
4
- import { toSignedTradeFee } from '../trade-fee';
5
-
6
- var __defProp = Object.defineProperty;
7
- var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
8
- class BybitAccountSyncer extends BaseAccountSyncer {
9
- static {
10
- __name(this, "BybitAccountSyncer");
11
- }
12
- creds;
13
- constructor(config) {
14
- super(config);
15
- this.creds = config.credentials;
16
- }
17
- async listExternalPositions({ instruments }) {
18
- const rawPositions = await fetchPositions(this.creds, "USDT", this.demo);
19
- const contractSizeByInstrumentId = new Map(instruments.map((instrument) => [
20
- instrument.id,
21
- instrument.precision.quantity.contractSize
22
- ]));
23
- return rawPositions.reduce((positions, rawPosition) => {
24
- if (!rawPosition.size || rawPosition.size === "0" || !rawPosition.side) return positions;
25
- const contractSize = contractSizeByInstrumentId.get(rawPosition.symbol) ?? 1;
26
- const size = new BigNumber(rawPosition.size).abs().times(contractSize).toFixed();
27
- positions.push({
28
- symbol: rawPosition.symbol,
29
- size,
30
- side: rawPosition.side === "Buy" ? "BUY" : "SELL",
31
- entryPrice: rawPosition.avgPrice,
32
- markPrice: rawPosition.avgPrice
33
- });
34
- return positions;
35
- }, []);
36
- }
37
- async listExternalFills({ since }) {
38
- const executions = await fetchExecutionsSince(this.creds, since, this.demo);
39
- return executions.reduce((fills, rawFill) => {
40
- if (rawFill.execType === "Funding") {
41
- return fills;
42
- }
43
- fills.push({
44
- connector: "bybit",
45
- exchangeFillId: rawFill.execId,
46
- cloid: rawFill.orderLinkId || null,
47
- symbol: rawFill.symbol,
48
- price: rawFill.execPrice,
49
- quantity: rawFill.execQty,
50
- side: rawFill.side === "Buy" ? "BUY" : "SELL",
51
- reduceOnly: this.isReduceOnly(rawFill),
52
- timestamp: Number(rawFill.execTime),
53
- tradeFee: toSignedTradeFee("bybit", rawFill.execFee),
54
- builderFee: "0",
55
- liquidation: null
56
- });
57
- return fills;
58
- }, []);
59
- }
60
- async listExternalFunding(_params) {
61
- throw new Error("funding sync is not implemented for bybit");
62
- }
63
- isReduceOnly(rawFill) {
64
- return Number(rawFill.closedSize || "0") > 0;
65
- }
66
- }
67
-
68
- export { BybitAccountSyncer };
@@ -1,33 +0,0 @@
1
- import { BinanceAccountSyncer } from './binance/syncer';
2
- import { BybitAccountSyncer } from './bybit/syncer';
3
- import { HyperliquidAccountSyncer } from './hyperliquid/syncer';
4
- import { LighterAccountSyncer } from './lighter/syncer';
5
- import { OkxAccountSyncer } from './okx/syncer';
6
-
7
- var __defProp = Object.defineProperty;
8
- var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
9
- function createAccountSyncer(exchange, config) {
10
- switch (exchange) {
11
- case "binance":
12
- return new BinanceAccountSyncer(config);
13
- case "bybit":
14
- return new BybitAccountSyncer(config);
15
- case "okx":
16
- return new OkxAccountSyncer(config);
17
- case "hyperliquid":
18
- return new HyperliquidAccountSyncer(config);
19
- case "lighter":
20
- case "lighter_rhc":
21
- return new LighterAccountSyncer({
22
- ...config,
23
- connector: exchange
24
- });
25
- default: {
26
- const _exhaustive = exchange;
27
- throw new Error(`Unsupported exchange: ${String(_exhaustive)}`);
28
- }
29
- }
30
- }
31
- __name(createAccountSyncer, "createAccountSyncer");
32
-
33
- export { createAccountSyncer };
@@ -1,27 +0,0 @@
1
- import BigNumber from 'bignumber.js';
2
-
3
- var __defProp = Object.defineProperty;
4
- var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
5
- function toFundingPaymentEntries(events, symbolToInstrumentId) {
6
- return events.reduce((entries, event) => {
7
- const instrumentId = symbolToInstrumentId[event.delta.coin];
8
- if (!instrumentId) {
9
- console.warn(`Instrument symbol ${event.delta.coin} not found in instrument symbol to id map.`);
10
- return entries;
11
- }
12
- const size = new BigNumber(event.delta.szi);
13
- const fundingPerUnit = size.isZero() ? "0" : new BigNumber(event.delta.usdc).div(size).toFixed();
14
- entries.push({
15
- connector: "hyperliquid",
16
- instrumentId,
17
- fundingRate: event.delta.fundingRate,
18
- notional: event.delta.usdc,
19
- fundingPerUnit,
20
- timestamp: event.time
21
- });
22
- return entries;
23
- }, []);
24
- }
25
- __name(toFundingPaymentEntries, "toFundingPaymentEntries");
26
-
27
- export { toFundingPaymentEntries };
@@ -1,208 +0,0 @@
1
- import BigNumber from 'bignumber.js';
2
- import { fetchInfo } from '../../client/hyperliquid/rest';
3
- import { BaseAccountSyncer } from '../base';
4
- import { toFundingPaymentEntries } from './funding';
5
-
6
- var __defProp = Object.defineProperty;
7
- var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
8
- const HYPERLIQUID_FILL_PAGE_LIMIT = 2e3;
9
- class HyperliquidAccountSyncer extends BaseAccountSyncer {
10
- static {
11
- __name(this, "HyperliquidAccountSyncer");
12
- }
13
- address;
14
- perpDexNames;
15
- constructor(config) {
16
- super(config);
17
- this.address = config.account.exchangeIdentifier;
18
- this.perpDexNames = config.hyperliquidPerpDexNames;
19
- }
20
- async listExternalPositions({ instruments }) {
21
- const instrumentByBase = new Map(instruments.map((instrument) => [
22
- instrument.base,
23
- {
24
- id: instrument.id,
25
- contractSize: instrument.precision.quantity.contractSize
26
- }
27
- ]));
28
- const dexNames = this.perpDexNames ?? await this.fetchPerpDexNames();
29
- const states = await Promise.all(dexNames.map((dex) => this.fetchInfoGated({
30
- type: "clearinghouseState",
31
- user: this.address,
32
- dex
33
- })));
34
- const assetPositions = states.flatMap((state) => state.assetPositions);
35
- return assetPositions.reduce((positions, { position: rawPosition }) => {
36
- const szi = new BigNumber(rawPosition.szi);
37
- if (szi.isZero()) {
38
- return positions;
39
- }
40
- const instrument = instrumentByBase.get(rawPosition.coin);
41
- if (!instrument) {
42
- console.warn(`Instrument for coin ${rawPosition.coin} not found.`);
43
- return positions;
44
- }
45
- const size = szi.abs().times(instrument.contractSize).toFixed();
46
- positions.push({
47
- symbol: instrument.id,
48
- size,
49
- side: szi.isPositive() ? "BUY" : "SELL",
50
- entryPrice: rawPosition.entryPx,
51
- markPrice: new BigNumber(rawPosition.positionValue).abs().div(size).toFixed()
52
- });
53
- return positions;
54
- }, []);
55
- }
56
- async listExternalFills({ since, instruments }) {
57
- const instrumentSymbolToIdMap = instruments.reduce((acc, instrument) => {
58
- acc[instrument.base] = instrument.id;
59
- return acc;
60
- }, {});
61
- const allFills = [];
62
- const seenTids = /* @__PURE__ */ new Set();
63
- const addFill = /* @__PURE__ */ __name((fill) => {
64
- if (!seenTids.has(fill.tid)) {
65
- seenTids.add(fill.tid);
66
- allFills.push(fill);
67
- }
68
- }, "addFill");
69
- let cursor = since.getTime();
70
- let batchSize;
71
- do {
72
- const batch = await this.fetchInfoGated({
73
- type: "userFillsByTime",
74
- user: this.address,
75
- startTime: cursor,
76
- aggregateByTime: true
77
- });
78
- batchSize = batch.length;
79
- for (const rawFill of batch) {
80
- addFill(rawFill);
81
- }
82
- if (batchSize === HYPERLIQUID_FILL_PAGE_LIMIT) {
83
- const nextCursor = batch[batch.length - 1]?.time ?? cursor;
84
- if (nextCursor <= cursor) {
85
- warnStalledPagination("fills", cursor);
86
- break;
87
- }
88
- cursor = nextCursor;
89
- }
90
- } while (batchSize === HYPERLIQUID_FILL_PAGE_LIMIT);
91
- cursor = since.getTime();
92
- do {
93
- const twapBatch = await this.fetchInfoGated({
94
- type: "userTwapSliceFillsByTime",
95
- user: this.address,
96
- startTime: cursor,
97
- aggregateByTime: true
98
- });
99
- batchSize = twapBatch.length;
100
- for (const item of twapBatch) {
101
- addFill(item.fill);
102
- }
103
- if (batchSize === HYPERLIQUID_FILL_PAGE_LIMIT) {
104
- const nextCursor = twapBatch[twapBatch.length - 1]?.fill.time ?? cursor;
105
- if (nextCursor <= cursor) {
106
- warnStalledPagination("TWAP fills", cursor);
107
- break;
108
- }
109
- cursor = nextCursor;
110
- }
111
- } while (batchSize === HYPERLIQUID_FILL_PAGE_LIMIT);
112
- allFills.sort((a, b) => a.time - b.time);
113
- return allFills.reduce((fills, rawFill) => {
114
- const instrumentId = instrumentSymbolToIdMap[rawFill.coin];
115
- if (!instrumentId) {
116
- console.warn(`Instrument symbol ${rawFill.coin} not found in instrument symbol to id map.`);
117
- return fills;
118
- }
119
- const tradeFee = new BigNumber(rawFill.fee).minus(rawFill.builderFee ?? "0");
120
- fills.push({
121
- connector: "hyperliquid",
122
- exchangeFillId: rawFill.tid.toFixed(),
123
- cloid: rawFill.cloid ?? null,
124
- symbol: instrumentId,
125
- price: rawFill.px,
126
- quantity: rawFill.sz,
127
- side: rawFill.side === "B" ? "BUY" : "SELL",
128
- reduceOnly: this.isReduceOnly(rawFill),
129
- timestamp: rawFill.time,
130
- tradeFee: tradeFee.toFixed(),
131
- builderFee: new BigNumber(rawFill.builderFee ?? "0").abs().toFixed(),
132
- // Preserve the existing marker when attribution is absent; reject explicitly named counterparties.
133
- liquidation: rawFill.liquidation && (!rawFill.liquidation.liquidatedUser || rawFill.liquidation.liquidatedUser.toLowerCase() === this.address.toLowerCase()) ? {
134
- mark: rawFill.liquidation.markPx
135
- } : null
136
- });
137
- return fills;
138
- }, []);
139
- }
140
- async listExternalFunding({ since, instruments }) {
141
- const instrumentSymbolToIdMap = instruments.reduce((acc, instrument) => {
142
- acc[instrument.base] = instrument.id;
143
- return acc;
144
- }, {});
145
- const PAGE_LIMIT = 500;
146
- const allEvents = /* @__PURE__ */ new Map();
147
- let cursor = since.getTime();
148
- let batchSize;
149
- do {
150
- const batch = await this.fetchInfoGated({
151
- type: "userFunding",
152
- user: this.address,
153
- startTime: cursor
154
- });
155
- batchSize = batch.length;
156
- for (const event of batch) {
157
- allEvents.set(`${event.time}:${event.delta.coin}:${event.hash}`, event);
158
- }
159
- if (batchSize === PAGE_LIMIT) {
160
- const nextCursor = batch[batch.length - 1]?.time ?? cursor;
161
- if (nextCursor <= cursor) {
162
- warnStalledPagination("funding", cursor);
163
- break;
164
- }
165
- cursor = nextCursor;
166
- }
167
- } while (batchSize === PAGE_LIMIT);
168
- return toFundingPaymentEntries([
169
- ...allEvents.values()
170
- ], instrumentSymbolToIdMap);
171
- }
172
- isReduceOnly(rawFill) {
173
- return rawFill.dir.includes("Close");
174
- }
175
- /** Fetch the perp-dex descriptors and reduce them to unique dex names ('' = the main dex). */
176
- async fetchPerpDexNames() {
177
- const perpDexs = await this.fetchInfoGated({
178
- type: "perpDexs"
179
- });
180
- return [
181
- ...new Set(perpDexs.map((perpDex) => perpDex?.name ?? ""))
182
- ];
183
- }
184
- /**
185
- * Route one info request through the optional throttle seam: gate before the
186
- * call, report the response size after, surface a failure before it propagates.
187
- */
188
- async fetchInfoGated(body) {
189
- if (!this.throttle) {
190
- return fetchInfo(body, this.demo);
191
- }
192
- await this.throttle.beforeRequest(body.type);
193
- try {
194
- const response = await fetchInfo(body, this.demo);
195
- this.throttle.afterRequest(body.type, Array.isArray(response) ? response.length : 0);
196
- return response;
197
- } catch (error) {
198
- this.throttle.afterRequestError(body.type, error);
199
- throw error;
200
- }
201
- }
202
- }
203
- function warnStalledPagination(stream, cursor) {
204
- console.warn(`Hyperliquid ${stream} pagination stalled at timestamp ${cursor}; returned history may be incomplete.`);
205
- }
206
- __name(warnStalledPagination, "warnStalledPagination");
207
-
208
- export { HyperliquidAccountSyncer };
@@ -1,7 +0,0 @@
1
- export * from './base';
2
- export * from './binance/syncer';
3
- export * from './bybit/syncer';
4
- export * from './factory';
5
- export * from './hyperliquid/syncer';
6
- export * from './lighter/syncer';
7
- export * from './okx/syncer';