@0xmonaco/core 1.0.73 → 1.0.74-develop.367860f

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.
@@ -17,7 +17,7 @@
17
17
  * console.log(`User: ${profile.username} (${profile.address})`);
18
18
  * ```
19
19
  */
20
- import { GetUserMovementsSchema, GetUserTradesSchema, ListFundingPaymentsSchema, SetSelfTradePreventionDefaultSchema, validate } from "@0xmonaco/types";
20
+ import { GetUserBalancesSchema, GetUserMovementsSchema, GetUserTradesSchema, ListFundingPaymentsSchema, SetSelfTradePreventionDefaultSchema, validate, } from "@0xmonaco/types";
21
21
  import { APIError } from "../../errors";
22
22
  import { BaseAPI } from "../base";
23
23
  export class ProfileAPIImpl extends BaseAPI {
@@ -53,14 +53,19 @@ export class ProfileAPIImpl extends BaseAPI {
53
53
  * Fetches the user's transaction history from the /api/v1/accounts/movements endpoint.
54
54
  * Requires a valid access token to be set.
55
55
  *
56
- * The response `total` and `totalPages` are bounded by the listing's pagination reach:
57
- * exact up to `pageSize` x 10,000 rows (10,000 pages) and saturating there, so a user
58
- * with more matching movements than the listing can page through reads the cap, not
59
- * their lifetime total. A `page` above 10,000 is rejected.
56
+ * Cursor paged (0XM-2788/0XM-2820): omit `pageToken` or pass `""` to start,
57
+ * then follow `nextPageToken` from each response until it comes back empty.
58
+ * There is no `page` — sending one is a `400`.
59
+ *
60
+ * The response `total` and `totalPages` are bounded by the server count
61
+ * ceiling; `totalCapped` is `true` when more than that matched, so `total`
62
+ * is a lower bound. Do not drive a paging loop off `total`: a user past the
63
+ * ceiling would stop early. Follow `nextPageToken`.
60
64
  *
61
65
  * @param params - Optional query parameters for pagination and filtering
62
- * @param params.page - Page number (starts from 1, default: 1, at most 10,000)
63
- * @param params.limit - Number of items per page (default: 20, max: 100)
66
+ * @param params.pageToken - Cursor from a previous response's `nextPageToken`
67
+ * @param params.pageSize - Number of items per page (default: 20, max: 1000)
68
+ * @param params.orderBy - Sort direction for `createdAt` (default: `"DESC"`)
64
69
  * @param params.entryType - Filter by entry type (CREDIT, DEBIT, LOCK, UNLOCK, FEE)
65
70
  * @param params.transactionType - Filter by transaction type (DEPOSIT, WITHDRAWAL, TRADE, FEE, FUNDING, LIQUIDATION, INTEREST, REWARD)
66
71
  * @param params.assetId - Filter by asset ID (UUID)
@@ -73,9 +78,14 @@ export class ProfileAPIImpl extends BaseAPI {
73
78
  * const movements = await profileAPI.getPaginatedUserMovements();
74
79
  * console.log(`Total movements: ${movements.total}`);
75
80
  *
76
- * // Get second page with custom pageSize
77
- * const page2 = await profileAPI.getPaginatedUserMovements({ page: 2, pageSize: 50 });
78
- * console.log(`Page ${page2.page} of ${page2.totalPages}`);
81
+ * // Walk every page
82
+ * let pageToken = "";
83
+ * for (;;) {
84
+ * const page = await profileAPI.getPaginatedUserMovements({ pageToken, pageSize: 50 });
85
+ * console.log(page.movements.length);
86
+ * if (page.nextPageToken === "") break;
87
+ * pageToken = page.nextPageToken;
88
+ * }
79
89
  *
80
90
  * // Filter by entry type and transaction type
81
91
  * const deposits = await profileAPI.getPaginatedUserMovements({ entryType: "CREDIT", transactionType: "DEPOSIT" });
@@ -89,12 +99,17 @@ export class ProfileAPIImpl extends BaseAPI {
89
99
  validate(GetUserMovementsSchema, params);
90
100
  }
91
101
  const searchParams = new URLSearchParams();
92
- if (params?.page !== undefined) {
93
- searchParams.append("page", params.page.toString());
94
- }
95
102
  if (params?.pageSize !== undefined) {
96
103
  searchParams.append("pageSize", params.pageSize.toString());
97
104
  }
105
+ // An empty token is a distinct, meaningful value — "start the walk" — so
106
+ // it must reach the wire rather than be dropped as falsy.
107
+ if (params?.pageToken !== undefined) {
108
+ searchParams.append("pageToken", params.pageToken);
109
+ }
110
+ if (params?.orderBy !== undefined) {
111
+ searchParams.append("orderBy", params.orderBy);
112
+ }
98
113
  if (params?.entryType !== undefined) {
99
114
  searchParams.append("entryType", params.entryType);
100
115
  }
@@ -114,9 +129,13 @@ export class ProfileAPIImpl extends BaseAPI {
114
129
  * Fetches the user's token balances from the /api/v1/accounts/balances endpoint.
115
130
  * Requires a valid access token to be set.
116
131
  *
132
+ * Cursor paged (0XM-2788/0XM-2821): omit `pageToken` or pass `""` to start,
133
+ * then follow `nextPageToken` until it comes back empty. There is no `page`
134
+ * — sending one is a `400`.
135
+ *
117
136
  * @param params - Optional query parameters for pagination
118
- * @param params.page - Page number (starts from 1, default: 1)
119
- * @param params.pageSize - Number of items per page (default: 20, max: 100)
137
+ * @param params.pageToken - Cursor from a previous response's `nextPageToken`
138
+ * @param params.pageSize - Number of items per page (default: 20, max: 1000)
120
139
  * @returns Promise resolving to paginated balances response
121
140
  * @throws {APIError} When the request fails or user is not authenticated
122
141
  *
@@ -129,19 +148,29 @@ export class ProfileAPIImpl extends BaseAPI {
129
148
  * console.log(`${balance.symbol}: ${balance.availableBalance}`);
130
149
  * });
131
150
  *
132
- * // Get page 2 with 50 items per page
133
- * const nextBalances = await profileAPI.getUserBalances({ page: 2, pageSize: 50 });
134
- * console.log(`Returned ${nextBalances.balances.length} balances`);
151
+ * // Walk every page
152
+ * let pageToken = "";
153
+ * for (;;) {
154
+ * const page = await profileAPI.getUserBalances({ pageToken, pageSize: 50 });
155
+ * console.log(page.balances.length);
156
+ * if (page.nextPageToken === "") break;
157
+ * pageToken = page.nextPageToken;
158
+ * }
135
159
  * ```
136
160
  */
137
161
  async getUserBalances(params) {
138
- const searchParams = new URLSearchParams();
139
- if (params?.page !== undefined) {
140
- searchParams.append("page", params.page.toString());
162
+ if (params) {
163
+ validate(GetUserBalancesSchema, params);
141
164
  }
165
+ const searchParams = new URLSearchParams();
142
166
  if (params?.pageSize !== undefined) {
143
167
  searchParams.append("pageSize", params.pageSize.toString());
144
168
  }
169
+ // An empty token is a distinct, meaningful value — "start the walk" — so
170
+ // it must reach the wire rather than be dropped as falsy.
171
+ if (params?.pageToken !== undefined) {
172
+ searchParams.append("pageToken", params.pageToken);
173
+ }
145
174
  const queryString = searchParams.toString();
146
175
  const url = queryString ? `/api/v1/accounts/balances?${queryString}` : "/api/v1/accounts/balances";
147
176
  return await this.makeAuthenticatedRequest(url);
@@ -315,12 +344,21 @@ export class ProfileAPIImpl extends BaseAPI {
315
344
  * Fetches funding payments from the /api/v1/accounts/funding-payments endpoint.
316
345
  * Requires a valid access token to be set.
317
346
  *
318
- * The response `total` and `totalPages` are bounded by the listing's pagination reach:
319
- * exact up to `pageSize` x 10,000 rows (10,000 pages) and saturating there, so a user
320
- * with more matching payments than the listing can page through reads the cap, not
321
- * their lifetime total. A `page` above 10,000 is rejected.
347
+ * Cursor paged (0XM-2788/0XM-2818): omit `pageToken` or pass `""` to start,
348
+ * then follow `nextPageToken` from each response until it comes back empty.
349
+ * There is no `page` — sending one is a `400`.
350
+ *
351
+ * The response `total` and `totalPages` are bounded by the server count
352
+ * ceiling; `totalCapped` is `true` when more than that matched, so `total`
353
+ * is a lower bound. Do not drive a paging loop off `total`: a user past the
354
+ * ceiling would stop early. Follow `nextPageToken`.
322
355
  *
323
356
  * @param params - Optional query parameters for pagination and filtering
357
+ * @param params.pageToken - Cursor from a previous response's `nextPageToken`
358
+ * @param params.pageSize - Number of items per page (default: 20, max: 1000)
359
+ * @param params.tradingPairId - Filter by trading pair ID (UUID)
360
+ * @param params.positionId - Filter by margin position ID (UUID)
361
+ * @param params.marginAccountId - Filter by margin account ID (UUID)
324
362
  * @returns Promise resolving to paginated funding payments response
325
363
  * @throws {APIError} When the request fails or user is not authenticated
326
364
  *
@@ -328,6 +366,15 @@ export class ProfileAPIImpl extends BaseAPI {
328
366
  * ```typescript
329
367
  * const funding = await profileAPI.listFundingPayments({ tradingPairId: "550e8400-e29b-41d4-a716-446655440000" });
330
368
  * console.log(`Total funding payments: ${funding.total}`);
369
+ *
370
+ * // Walk every page
371
+ * let pageToken = "";
372
+ * for (;;) {
373
+ * const page = await profileAPI.listFundingPayments({ pageToken, pageSize: 100 });
374
+ * console.log(page.records.length);
375
+ * if (page.nextPageToken === "") break;
376
+ * pageToken = page.nextPageToken;
377
+ * }
331
378
  * ```
332
379
  */
333
380
  async listFundingPayments(params) {
@@ -335,12 +382,14 @@ export class ProfileAPIImpl extends BaseAPI {
335
382
  validate(ListFundingPaymentsSchema, params);
336
383
  }
337
384
  const searchParams = new URLSearchParams();
338
- if (params?.page !== undefined) {
339
- searchParams.append("page", params.page.toString());
340
- }
341
385
  if (params?.pageSize !== undefined) {
342
386
  searchParams.append("pageSize", params.pageSize.toString());
343
387
  }
388
+ // An empty token is a distinct, meaningful value — "start the walk" — so
389
+ // it must reach the wire rather than be dropped as falsy.
390
+ if (params?.pageToken !== undefined) {
391
+ searchParams.append("pageToken", params.pageToken);
392
+ }
344
393
  if (params?.tradingPairId !== undefined) {
345
394
  searchParams.append("tradingPairId", params.tradingPairId);
346
395
  }
@@ -26,23 +26,54 @@ export declare function parseRawTradeEvent(raw: RawTradeEvent): TradeEvent;
26
26
  * Options for fetching trades
27
27
  */
28
28
  export interface GetTradesOptions {
29
- /** Page number (starts from 1, default: 1) */
30
- page?: number;
31
- /** Maximum number of records to return (default: 25, max: 100) */
29
+ /** Maximum number of records to return (default: 25, max: 1000) */
32
30
  pageSize?: number;
31
+ /**
32
+ * Cursor from a previous response's `nextPageToken`. Omit (or pass "") to
33
+ * start at the most recent trade.
34
+ */
35
+ pageToken?: string;
36
+ }
37
+ /**
38
+ * One page of a pair's trade feed.
39
+ *
40
+ * `nextPageToken` is empty exactly when the walk is finished — the server
41
+ * over-fetches one row to prove it — so a caller can stop on the empty token
42
+ * without a confirming extra request.
43
+ */
44
+ export interface GetTradesPage {
45
+ /** Trades in this page, newest first. */
46
+ trades: TradeEvent[];
47
+ /** Cursor to pass as `pageToken` for the next page; "" when exhausted. */
48
+ nextPageToken: string;
33
49
  }
34
50
  /**
35
51
  * Trades API for fetching historical trade data
36
52
  */
37
53
  export declare class TradesAPIImpl extends BaseAPI {
38
54
  /**
39
- * Get historical trades for a trading pair
55
+ * Get one page of historical trades for a trading pair.
56
+ *
57
+ * Cursor paged (0XM-2821): the page number was removed because `trades` is
58
+ * appended to continuously, so an offset silently repeated the newest trades
59
+ * as fills landed mid-walk. Start with no `pageToken` and follow
60
+ * `nextPageToken` until it comes back empty.
40
61
  *
41
62
  * @param tradingPairId - The trading pair UUID
42
- * @param options - Pagination options
43
- * @returns Array of TradeEvent records sorted by executedAt descending (newest first)
63
+ * @param options - Cursor pagination options
64
+ * @returns One page of TradeEvent records sorted by executedAt descending (newest first), plus the next cursor
65
+ *
66
+ * @example
67
+ * ```typescript
68
+ * let pageToken: string | undefined;
69
+ * do {
70
+ * const page = await sdk.trades.getTrades(pairId, { pageSize: 100, pageToken });
71
+ * handle(page.trades);
72
+ * pageToken = page.nextPageToken || undefined;
73
+ * } while (pageToken);
74
+ * ```
44
75
  */
45
- getTrades(tradingPairId: string, options?: GetTradesOptions): Promise<TradeEvent[]>;
76
+ getTrades(tradingPairId: string, options?: GetTradesOptions): Promise<GetTradesPage>;
46
77
  /**
47
78
  * Get a single trade by its UUID.
48
79
  *
@@ -1,3 +1,4 @@
1
+ import { GetTradesSchema, validate } from "@0xmonaco/types";
1
2
  import { BaseAPI } from "../base";
2
3
  import { perpRoutes } from "../perp";
3
4
  /**
@@ -22,23 +23,49 @@ export function parseRawTradeEvent(raw) {
22
23
  */
23
24
  export class TradesAPIImpl extends BaseAPI {
24
25
  /**
25
- * Get historical trades for a trading pair
26
+ * Get one page of historical trades for a trading pair.
27
+ *
28
+ * Cursor paged (0XM-2821): the page number was removed because `trades` is
29
+ * appended to continuously, so an offset silently repeated the newest trades
30
+ * as fills landed mid-walk. Start with no `pageToken` and follow
31
+ * `nextPageToken` until it comes back empty.
26
32
  *
27
33
  * @param tradingPairId - The trading pair UUID
28
- * @param options - Pagination options
29
- * @returns Array of TradeEvent records sorted by executedAt descending (newest first)
34
+ * @param options - Cursor pagination options
35
+ * @returns One page of TradeEvent records sorted by executedAt descending (newest first), plus the next cursor
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * let pageToken: string | undefined;
40
+ * do {
41
+ * const page = await sdk.trades.getTrades(pairId, { pageSize: 100, pageToken });
42
+ * handle(page.trades);
43
+ * pageToken = page.nextPageToken || undefined;
44
+ * } while (pageToken);
45
+ * ```
30
46
  */
31
47
  async getTrades(tradingPairId, options = {}) {
32
- const { page = 1 } = options;
33
- // Ensure pageSize is a positive number between 1 and 100, defaulting to 25
34
- const pageSize = options.pageSize != null && options.pageSize > 0 ? Math.min(options.pageSize, 100) : 25;
48
+ validate(GetTradesSchema, options);
35
49
  const params = new URLSearchParams();
36
- if (page > 1) {
37
- params.set("page", String(page));
50
+ // Only send what the caller set: the server owns the 25 default, so
51
+ // restating it here would pin the SDK to a value the API can move.
52
+ if (options.pageSize != null) {
53
+ params.set("pageSize", String(options.pageSize));
54
+ }
55
+ if (options.pageToken != null && options.pageToken !== "") {
56
+ params.set("pageToken", options.pageToken);
38
57
  }
39
- params.set("pageSize", String(pageSize));
40
- const response = await this.makePublicRequest(`/api/v1/trades/${encodeURIComponent(tradingPairId)}?${params.toString()}`);
41
- return response.trades.map(parseRawTradeEvent);
58
+ const query = params.toString();
59
+ const response = await this.makePublicRequest(`/api/v1/trades/${encodeURIComponent(tradingPairId)}${query === "" ? "" : `?${query}`}`);
60
+ return {
61
+ // `?? []` because an empty page omits `trades` entirely; without it the
62
+ // last page of a walk would throw instead of ending it.
63
+ trades: (response.trades ?? []).map(parseRawTradeEvent),
64
+ // Absent rather than "" on a wire that omits empty strings: either way the
65
+ // walk is finished, and a caller testing `nextPageToken || undefined`
66
+ // must not see `undefined` become a truthy string.
67
+ nextPageToken: response.nextPageToken ?? "",
68
+ };
42
69
  }
43
70
  /**
44
71
  * Get a single trade by its UUID.
@@ -315,11 +315,10 @@ export declare class TradingAPIImpl extends BaseAPI implements TradingAPI {
315
315
  * Gets orders based on query parameters, cursor-paginated by default.
316
316
  *
317
317
  * Retrieves orders with optional filtering by status, trading pair, and pagination.
318
- * Every kind of order comes back in ONE stream ordered by timestamp: book orders,
319
- * TWAP parents (`orderType: "TWAP"`, detail on `twap`) and conditional orders
320
- * (`orderType: "CONDITIONAL"`, detail on `conditional`). There is no separate
321
- * open-orders call — filter `status` to `["SUBMITTED", "PARTIALLY_FILLED"]` for the
322
- * working set.
318
+ * Every row is a BOOK order (`LIMIT` or `MARKET`); filter `status` to
319
+ * `["SUBMITTED", "PARTIALLY_FILLED"]` for the resting working set. TWAP parents and
320
+ * conditional (TP/SL) orders are not rows here — list them with `listTwapOrders`
321
+ * and `listConditionalOrders`.
323
322
  * When neither `page` nor `pageToken` is passed, the SDK drives cursor (pageToken)
324
323
  * pagination: it starts a cursor walk from the newest (or oldest, for
325
324
  * `orderBy: "ASC"`) row and the response's `nextPageToken` resumes it. Both modes
@@ -342,13 +341,9 @@ export declare class TradingAPIImpl extends BaseAPI implements TradingAPI {
342
341
  *
343
342
  * @example
344
343
  * ```typescript
345
- * // Every working order, of every kind, in one page
344
+ * // The resting working set (book orders only)
346
345
  * const open = await tradingAPI.getPaginatedOrders({ status: ["SUBMITTED", "PARTIALLY_FILLED"] });
347
- * for (const order of open.orders) {
348
- * if (order.orderType === "TWAP") console.log("TWAP parent", order.twap?.slicesPlaced);
349
- * else if (order.orderType === "CONDITIONAL") console.log("trigger", order.conditional?.triggerPrice);
350
- * else console.log("resting", order.price);
351
- * }
346
+ * for (const order of open.orders) console.log("resting", order.quantity, "@", order.price);
352
347
  * ```
353
348
  *
354
349
  * @example
@@ -495,11 +495,10 @@ export class TradingAPIImpl extends BaseAPI {
495
495
  * Gets orders based on query parameters, cursor-paginated by default.
496
496
  *
497
497
  * Retrieves orders with optional filtering by status, trading pair, and pagination.
498
- * Every kind of order comes back in ONE stream ordered by timestamp: book orders,
499
- * TWAP parents (`orderType: "TWAP"`, detail on `twap`) and conditional orders
500
- * (`orderType: "CONDITIONAL"`, detail on `conditional`). There is no separate
501
- * open-orders call — filter `status` to `["SUBMITTED", "PARTIALLY_FILLED"]` for the
502
- * working set.
498
+ * Every row is a BOOK order (`LIMIT` or `MARKET`); filter `status` to
499
+ * `["SUBMITTED", "PARTIALLY_FILLED"]` for the resting working set. TWAP parents and
500
+ * conditional (TP/SL) orders are not rows here — list them with `listTwapOrders`
501
+ * and `listConditionalOrders`.
503
502
  * When neither `page` nor `pageToken` is passed, the SDK drives cursor (pageToken)
504
503
  * pagination: it starts a cursor walk from the newest (or oldest, for
505
504
  * `orderBy: "ASC"`) row and the response's `nextPageToken` resumes it. Both modes
@@ -522,13 +521,9 @@ export class TradingAPIImpl extends BaseAPI {
522
521
  *
523
522
  * @example
524
523
  * ```typescript
525
- * // Every working order, of every kind, in one page
524
+ * // The resting working set (book orders only)
526
525
  * const open = await tradingAPI.getPaginatedOrders({ status: ["SUBMITTED", "PARTIALLY_FILLED"] });
527
- * for (const order of open.orders) {
528
- * if (order.orderType === "TWAP") console.log("TWAP parent", order.twap?.slicesPlaced);
529
- * else if (order.orderType === "CONDITIONAL") console.log("trigger", order.conditional?.triggerPrice);
530
- * else console.log("resting", order.price);
531
- * }
526
+ * for (const order of open.orders) console.log("resting", order.quantity, "@", order.price);
532
527
  * ```
533
528
  *
534
529
  * @example