@riocrypto/common-server 1.0.2882 → 1.0.2884

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.
@@ -33,6 +33,13 @@ export interface KrakenWithdrawInfo {
33
33
  amount: string;
34
34
  fee: string;
35
35
  }
36
+ export interface KrakenFundingQuery {
37
+ method?: string;
38
+ start?: number;
39
+ end?: number;
40
+ maxPages?: number;
41
+ stopWhen?: (row: KrakenFundingStatus) => boolean;
42
+ }
36
43
  export interface KrakenLedgerEntry {
37
44
  refid: string;
38
45
  time: number;
@@ -62,16 +69,19 @@ declare class KrakenClient {
62
69
  getAllBalances(assets: string[]): Promise<Record<string, number>>;
63
70
  getDepositMethods(asset: string): Promise<KrakenDepositMethod[]>;
64
71
  getDepositAddresses(asset: string, method: string, isNew?: boolean): Promise<KrakenDepositAddress[]>;
65
- getDepositStatus(asset: string, method?: string): Promise<KrakenFundingStatus[]>;
72
+ private listFunding;
73
+ getDepositStatus(asset: string, query?: KrakenFundingQuery): Promise<KrakenFundingStatus[]>;
66
74
  getWithdrawInfo(asset: string, key: string, amount: number): Promise<KrakenWithdrawInfo>;
67
75
  withdraw(asset: string, key: string, amount: number): Promise<string>;
68
- getWithdrawStatus(asset: string, method?: string): Promise<KrakenFundingStatus[]>;
76
+ getWithdrawStatus(asset: string, query?: KrakenFundingQuery): Promise<KrakenFundingStatus[]>;
69
77
  getWithdrawStatusByRefid(asset: string, refid: string): Promise<KrakenFundingStatus | undefined>;
70
78
  getDepositByTxId(asset: string, txid: string): Promise<KrakenFundingStatus | undefined>;
71
79
  getLedgers(params?: {
72
80
  type?: string;
73
81
  asset?: string;
74
82
  start?: number;
83
+ end?: number;
84
+ maxPages?: number;
75
85
  }): Promise<Record<string, KrakenLedgerEntry>>;
76
86
  }
77
87
  export declare const buildKrakenClient: () => Promise<KrakenClient>;
@@ -28,6 +28,42 @@ const getKrakenRestBaseUrl = (env) => env === common_1.RioEnv.Production
28
28
  : KRAKEN_UAT_REST_BASE_URL;
29
29
  // Kraken Balance keys. Fiat USD is `ZUSD`; the stablecoins use their ticker.
30
30
  exports.KRAKEN_ASSET_USD = "ZUSD";
31
+ // Page size we ask the funding endpoints for. Their own defaults differ sharply
32
+ // - DepositStatus returns 25 rows and WithdrawStatus 500 - so always send an
33
+ // explicit limit rather than inheriting whichever default applies. Reading only
34
+ // the first default-size page is what previously made older deposits invisible.
35
+ const FUNDING_PAGE_LIMIT = 500;
36
+ // Ledgers returns 50 rows per call and pages via a numeric `ofs` offset.
37
+ const LEDGER_PAGE_SIZE = 50;
38
+ // Ceiling on pages any single call will walk. The private-request queue
39
+ // serializes these, so an unbounded walk would both stall the queue and burn
40
+ // the per-account rate counter that liquidity-management and external-trading
41
+ // share.
42
+ const MAX_FUNDING_PAGES = 40;
43
+ const MAX_LEDGER_PAGES = 40;
44
+ // The funding endpoints answer either with a bare array (when `cursor` is
45
+ // disabled) or with an object holding the rows plus `next_cursor`. That
46
+ // object's row property is documented as `deposit` for DepositStatus and is not
47
+ // documented at all for WithdrawStatus, so take the first array-valued property
48
+ // instead of trusting a name: guessing the key wrong yields a silent zero rows
49
+ // rather than an error.
50
+ const normalizeFundingPage = (raw) => {
51
+ var _a;
52
+ if (Array.isArray(raw)) {
53
+ return { rows: raw };
54
+ }
55
+ if (!raw || typeof raw !== "object") {
56
+ return { rows: [] };
57
+ }
58
+ const record = raw;
59
+ const rows = (_a = Object.values(record).find((value) => Array.isArray(value))) !== null && _a !== void 0 ? _a : [];
60
+ const nextCursor = typeof record.next_cursor === "string" ? record.next_cursor : undefined;
61
+ return { rows, nextCursor };
62
+ };
63
+ // Cursor pages restart from the newest record, so the same row can appear on
64
+ // more than one page. Identity is the funding reference plus its transaction
65
+ // and timestamp.
66
+ const fundingRowKey = (row) => `${row.refid}:${row.txid}:${row.time}`;
31
67
  class KrakenClient {
32
68
  constructor(apiKey, apiSecret, env) {
33
69
  this.apiKey = apiKey;
@@ -146,12 +182,51 @@ class KrakenClient {
146
182
  });
147
183
  });
148
184
  }
149
- getDepositStatus(asset, method) {
185
+ // Walk a funding endpoint to exhaustion (or to `maxPages`). The first call
186
+ // deliberately omits `cursor` so it matches the documented unpaginated form;
187
+ // pagination is enabled from the second call onward, which restarts at the
188
+ // newest record and so overlaps the first page. Duplicates are dropped by
189
+ // reference id, making the overlap harmless.
190
+ listFunding(endpoint, asset, query) {
191
+ var _a;
150
192
  return __awaiter(this, void 0, void 0, function* () {
151
- return this.privatePost("DepositStatus", {
152
- asset,
153
- method,
154
- });
193
+ const maxPages = (_a = query.maxPages) !== null && _a !== void 0 ? _a : MAX_FUNDING_PAGES;
194
+ const rows = [];
195
+ const seen = new Set();
196
+ let cursor;
197
+ for (let page = 0; page < maxPages; page++) {
198
+ const raw = yield this.privatePost(endpoint, {
199
+ asset,
200
+ method: query.method,
201
+ start: query.start,
202
+ end: query.end,
203
+ limit: FUNDING_PAGE_LIMIT,
204
+ cursor: page === 0 ? undefined : cursor !== null && cursor !== void 0 ? cursor : true,
205
+ });
206
+ const { rows: batch, nextCursor } = normalizeFundingPage(raw);
207
+ for (const row of batch) {
208
+ const key = fundingRowKey(row);
209
+ if (!seen.has(key)) {
210
+ seen.add(key);
211
+ rows.push(row);
212
+ }
213
+ }
214
+ if (query.stopWhen && batch.some(query.stopWhen)) {
215
+ break;
216
+ }
217
+ // Page 0 carries no cursor, so allow one more call to obtain one; after
218
+ // that, no cursor means there is nothing further back.
219
+ if (batch.length === 0 || (page > 0 && !nextCursor)) {
220
+ break;
221
+ }
222
+ cursor = nextCursor;
223
+ }
224
+ return rows;
225
+ });
226
+ }
227
+ getDepositStatus(asset, query = {}) {
228
+ return __awaiter(this, void 0, void 0, function* () {
229
+ return this.listFunding("DepositStatus", asset, query);
155
230
  });
156
231
  }
157
232
  // ----- Withdrawals -----
@@ -181,41 +256,64 @@ class KrakenClient {
181
256
  return result.refid;
182
257
  });
183
258
  }
184
- getWithdrawStatus(asset, method) {
259
+ getWithdrawStatus(asset, query = {}) {
185
260
  return __awaiter(this, void 0, void 0, function* () {
186
- return this.privatePost("WithdrawStatus", {
187
- asset,
188
- method,
189
- });
261
+ return this.listFunding("WithdrawStatus", asset, query);
190
262
  });
191
263
  }
192
264
  getWithdrawStatusByRefid(asset, refid) {
193
265
  return __awaiter(this, void 0, void 0, function* () {
194
- const statuses = yield this.getWithdrawStatus(asset);
266
+ const statuses = yield this.getWithdrawStatus(asset, {
267
+ stopWhen: (row) => row.refid === refid,
268
+ });
195
269
  return statuses.find((s) => s.refid === refid);
196
270
  });
197
271
  }
198
272
  getDepositByTxId(asset, txid) {
199
273
  return __awaiter(this, void 0, void 0, function* () {
200
- const statuses = yield this.getDepositStatus(asset);
274
+ const statuses = yield this.getDepositStatus(asset, {
275
+ stopWhen: (row) => row.txid === txid,
276
+ });
201
277
  return statuses.find((s) => s.txid === txid);
202
278
  });
203
279
  }
204
280
  // ----- Ledgers -----
205
- // Returns the ledger entries (keyed by ledger id) for the given filters.
206
- // This is the authoritative source for fiat deposits/withdrawals, which do
207
- // not appear in DepositStatus/WithdrawStatus. `start` is a unix timestamp in
208
- // seconds; Kraken returns the most recent entries (max 50 per page) at or
209
- // after it.
281
+ // Returns the ledger entries (keyed by ledger id) for the given filters,
282
+ // walking every page rather than just the most recent 50. Kraken caps a
283
+ // response at 50 rows and reports the full match count, so pagination is via
284
+ // the numeric `ofs` offset until that count is covered. `start`/`end` are
285
+ // unix timestamps in seconds.
210
286
  getLedgers(params = {}) {
211
- var _a;
287
+ var _a, _b, _c;
212
288
  return __awaiter(this, void 0, void 0, function* () {
213
- const result = yield this.privatePost("Ledgers", {
214
- type: params.type,
215
- asset: params.asset,
216
- start: params.start,
217
- });
218
- return (_a = result === null || result === void 0 ? void 0 : result.ledger) !== null && _a !== void 0 ? _a : {};
289
+ const maxPages = (_a = params.maxPages) !== null && _a !== void 0 ? _a : MAX_LEDGER_PAGES;
290
+ const merged = {};
291
+ for (let page = 0; page < maxPages; page++) {
292
+ const result = yield this.privatePost("Ledgers", {
293
+ type: params.type,
294
+ asset: params.asset,
295
+ start: params.start,
296
+ end: params.end,
297
+ ofs: page * LEDGER_PAGE_SIZE,
298
+ });
299
+ const rows = (_b = result === null || result === void 0 ? void 0 : result.ledger) !== null && _b !== void 0 ? _b : {};
300
+ const pageSize = Object.keys(rows).length;
301
+ if (pageSize === 0) {
302
+ break;
303
+ }
304
+ Object.assign(merged, rows);
305
+ // A short page is the last one. `count` is Kraken's total for the filter;
306
+ // it is only trusted when present so a missing count cannot terminate the
307
+ // walk after a single page.
308
+ if (pageSize < LEDGER_PAGE_SIZE) {
309
+ break;
310
+ }
311
+ const count = (_c = result === null || result === void 0 ? void 0 : result.count) !== null && _c !== void 0 ? _c : 0;
312
+ if (count > 0 && Object.keys(merged).length >= count) {
313
+ break;
314
+ }
315
+ }
316
+ return merged;
219
317
  });
220
318
  }
221
319
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riocrypto/common-server",
3
- "version": "1.0.2882",
3
+ "version": "1.0.2884",
4
4
  "description": "",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",