@0xmonaco/core 1.0.55 → 1.0.57

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/dist/api/base.js CHANGED
@@ -20,7 +20,7 @@
20
20
  * }
21
21
  * ```
22
22
  */
23
- import { bytesToHex, utf8ToBytes } from "@noble/hashes/utils";
23
+ import { bytesToHex, utf8ToBytes } from "@noble/hashes/utils.js";
24
24
  import { StatusCodes } from "http-status-codes";
25
25
  import { buildHttpSignatureHeaders } from "../crypto/http-signatures";
26
26
  import { composeSigningString, keypairFromHex, sha256Hex, signMessage } from "../crypto/session";
@@ -164,6 +164,7 @@ export declare const perpRoutes: {
164
164
  };
165
165
  readonly sweeper: {
166
166
  readonly register: () => string;
167
+ readonly chains: () => string;
167
168
  };
168
169
  readonly whitelist: {
169
170
  readonly submit: () => string;
@@ -89,6 +89,7 @@ export const perpRoutes = {
89
89
  },
90
90
  sweeper: {
91
91
  register: () => `${API_V1}/sweeper/register`,
92
+ chains: () => `${API_V1}/sweeper/chains`,
92
93
  },
93
94
  whitelist: {
94
95
  submit: () => `${API_V1}/whitelist`,
@@ -22,7 +22,7 @@
22
22
  * const margin = await sdk.sweeper.register({ depositTarget: "margin" });
23
23
  * ```
24
24
  */
25
- import type { RegisterSweeperRequest, RegisterSweeperResponse, SweeperAPI } from "@0xmonaco/types";
25
+ import type { GetSweeperChainsResponse, RegisterSweeperRequest, RegisterSweeperResponse, SweeperAPI } from "@0xmonaco/types";
26
26
  import { BaseAPI } from "../base";
27
27
  export declare class SweeperAPIImpl extends BaseAPI implements SweeperAPI {
28
28
  /**
@@ -42,10 +42,25 @@ export declare class SweeperAPIImpl extends BaseAPI implements SweeperAPI {
42
42
  * rather than a guarantee, since a deposit the backend cannot route to
43
43
  * collateral is credited to the spot wallet instead.
44
44
  *
45
+ * The deposit address is identical on every supported chain and one
46
+ * registration is watched on all of them (see {@link getChains} for the
47
+ * set), so there is nothing chain-specific to pass.
48
+ *
45
49
  * @param body - Optionally the destination ledger; everything else comes
46
50
  * from the session
47
51
  * @returns Promise resolving to the deposit address and the initial-check
48
52
  * state
49
53
  */
50
54
  register(body?: RegisterSweeperRequest): Promise<RegisterSweeperResponse>;
55
+ /**
56
+ * The chains and assets the sweeper watches: where a registered deposit
57
+ * address accepts deposits, which tokens are swept on each chain, and each
58
+ * token's minimum sweep amount in raw base units.
59
+ *
60
+ * Public — no session needed. The set changes only with operator
61
+ * configuration, so it is safe to cache briefly.
62
+ *
63
+ * @returns Promise resolving to the supported chain/asset listing
64
+ */
65
+ getChains(): Promise<GetSweeperChainsResponse>;
51
66
  }
@@ -69,6 +69,10 @@ export class SweeperAPIImpl extends BaseAPI {
69
69
  * rather than a guarantee, since a deposit the backend cannot route to
70
70
  * collateral is credited to the spot wallet instead.
71
71
  *
72
+ * The deposit address is identical on every supported chain and one
73
+ * registration is watched on all of them (see {@link getChains} for the
74
+ * set), so there is nothing chain-specific to pass.
75
+ *
72
76
  * @param body - Optionally the destination ledger; everything else comes
73
77
  * from the session
74
78
  * @returns Promise resolving to the deposit address and the initial-check
@@ -82,4 +86,19 @@ export class SweeperAPIImpl extends BaseAPI {
82
86
  body: JSON.stringify(wireBody),
83
87
  });
84
88
  }
89
+ /**
90
+ * The chains and assets the sweeper watches: where a registered deposit
91
+ * address accepts deposits, which tokens are swept on each chain, and each
92
+ * token's minimum sweep amount in raw base units.
93
+ *
94
+ * Public — no session needed. The set changes only with operator
95
+ * configuration, so it is safe to cache briefly.
96
+ *
97
+ * @returns Promise resolving to the supported chain/asset listing
98
+ */
99
+ async getChains() {
100
+ return await this.makePublicRequest(perpRoutes.sweeper.chains(), {
101
+ method: "GET",
102
+ });
103
+ }
85
104
  }
@@ -257,6 +257,17 @@ export declare class TradingAPIImpl extends BaseAPI implements TradingAPI {
257
257
  * Each order is canceled and re-created with new parameters.
258
258
  * Failed replacements do not prevent other orders from being replaced.
259
259
  *
260
+ * Pre-sequencing validation is scoped per item too, matching `batchCreate`:
261
+ * a malformed `orderId`, `price`, `quantity` or `clientOrderId`, or a
262
+ * `postOnly` violation, fails only that item — its result slot carries a
263
+ * structured `error.code` (`INVALID_ORDER_ID`, `INVALID_PRICE`,
264
+ * `INVALID_QUANTITY`, `INVALID_CLIENT_ORDER_ID`, `INVALID_POST_ONLY`) and its
265
+ * original is left untouched — while the valid items proceed. A `2xx`
266
+ * response can therefore mix executed replacements and failed items: read
267
+ * `results[]` per item rather than treating the status code as the verdict.
268
+ * Only an empty or oversize (>100) batch rejects the whole request.
269
+ * `totalRequested === totalSucceeded + totalFailed` always holds.
270
+ *
260
271
  * @param orders - Array of order replacement parameters
261
272
  * @returns Promise resolving to BatchReplaceOrdersResponse with individual results
262
273
  * @throws {APIError} When API communication fails
@@ -281,6 +292,11 @@ export declare class TradingAPIImpl extends BaseAPI implements TradingAPI {
281
292
  * Gets orders based on query parameters, cursor-paginated by default.
282
293
  *
283
294
  * Retrieves orders with optional filtering by status, trading pair, and pagination.
295
+ * Every kind of order comes back in ONE stream ordered by timestamp: book orders,
296
+ * TWAP parents (`orderType: "TWAP"`, detail on `twap`) and conditional orders
297
+ * (`orderType: "CONDITIONAL"`, detail on `conditional`). There is no separate
298
+ * open-orders call — filter `status` to `["SUBMITTED", "PARTIALLY_FILLED"]` for the
299
+ * working set.
284
300
  * When neither `page` nor `pageToken` is passed, the SDK drives cursor (pageToken)
285
301
  * pagination: it starts a cursor walk from the newest (or oldest, for
286
302
  * `orderBy: "ASC"`) row and the response's `nextPageToken` resumes it. Cursor
@@ -305,6 +321,17 @@ export declare class TradingAPIImpl extends BaseAPI implements TradingAPI {
305
321
  *
306
322
  * @example
307
323
  * ```typescript
324
+ * // Every working order, of every kind, in one page
325
+ * const open = await tradingAPI.getPaginatedOrders({ status: ["SUBMITTED", "PARTIALLY_FILLED"] });
326
+ * for (const order of open.orders) {
327
+ * if (order.orderType === "TWAP") console.log("TWAP parent", order.twap?.slicesPlaced);
328
+ * else if (order.orderType === "CONDITIONAL") console.log("trigger", order.conditional?.triggerPrice);
329
+ * else console.log("resting", order.price);
330
+ * }
331
+ * ```
332
+ *
333
+ * @example
334
+ * ```typescript
308
335
  * // Walk full order history with cursor pagination (the default mode)
309
336
  * let pageToken: string | undefined;
310
337
  * do {
@@ -403,6 +403,17 @@ export class TradingAPIImpl extends BaseAPI {
403
403
  * Each order is canceled and re-created with new parameters.
404
404
  * Failed replacements do not prevent other orders from being replaced.
405
405
  *
406
+ * Pre-sequencing validation is scoped per item too, matching `batchCreate`:
407
+ * a malformed `orderId`, `price`, `quantity` or `clientOrderId`, or a
408
+ * `postOnly` violation, fails only that item — its result slot carries a
409
+ * structured `error.code` (`INVALID_ORDER_ID`, `INVALID_PRICE`,
410
+ * `INVALID_QUANTITY`, `INVALID_CLIENT_ORDER_ID`, `INVALID_POST_ONLY`) and its
411
+ * original is left untouched — while the valid items proceed. A `2xx`
412
+ * response can therefore mix executed replacements and failed items: read
413
+ * `results[]` per item rather than treating the status code as the verdict.
414
+ * Only an empty or oversize (>100) batch rejects the whole request.
415
+ * `totalRequested === totalSucceeded + totalFailed` always holds.
416
+ *
406
417
  * @param orders - Array of order replacement parameters
407
418
  * @returns Promise resolving to BatchReplaceOrdersResponse with individual results
408
419
  * @throws {APIError} When API communication fails
@@ -443,6 +454,11 @@ export class TradingAPIImpl extends BaseAPI {
443
454
  * Gets orders based on query parameters, cursor-paginated by default.
444
455
  *
445
456
  * Retrieves orders with optional filtering by status, trading pair, and pagination.
457
+ * Every kind of order comes back in ONE stream ordered by timestamp: book orders,
458
+ * TWAP parents (`orderType: "TWAP"`, detail on `twap`) and conditional orders
459
+ * (`orderType: "CONDITIONAL"`, detail on `conditional`). There is no separate
460
+ * open-orders call — filter `status` to `["SUBMITTED", "PARTIALLY_FILLED"]` for the
461
+ * working set.
446
462
  * When neither `page` nor `pageToken` is passed, the SDK drives cursor (pageToken)
447
463
  * pagination: it starts a cursor walk from the newest (or oldest, for
448
464
  * `orderBy: "ASC"`) row and the response's `nextPageToken` resumes it. Cursor
@@ -467,6 +483,17 @@ export class TradingAPIImpl extends BaseAPI {
467
483
  *
468
484
  * @example
469
485
  * ```typescript
486
+ * // Every working order, of every kind, in one page
487
+ * const open = await tradingAPI.getPaginatedOrders({ status: ["SUBMITTED", "PARTIALLY_FILLED"] });
488
+ * for (const order of open.orders) {
489
+ * if (order.orderType === "TWAP") console.log("TWAP parent", order.twap?.slicesPlaced);
490
+ * else if (order.orderType === "CONDITIONAL") console.log("trigger", order.conditional?.triggerPrice);
491
+ * else console.log("resting", order.price);
492
+ * }
493
+ * ```
494
+ *
495
+ * @example
496
+ * ```typescript
470
497
  * // Walk full order history with cursor pagination (the default mode)
471
498
  * let pageToken: string | undefined;
472
499
  * do {
@@ -1,4 +1,4 @@
1
- import { hexToBytes } from "@noble/hashes/utils";
1
+ import { hexToBytes } from "@noble/hashes/utils.js";
2
2
  import { composeWsSigningString, signMessage } from "../../crypto/session";
3
3
  import { ALL_MAGNITUDES } from "../../utils";
4
4
  import { keysToCamelCase } from "./utils";
@@ -82,6 +82,12 @@ function readOptionalNumber(source, field) {
82
82
  }
83
83
  return value;
84
84
  }
85
+ function readOptionalSequence(source, field) {
86
+ const value = source[field];
87
+ if (value === undefined || value === null)
88
+ return undefined;
89
+ return readSequence(source, field);
90
+ }
85
91
  function readStringArray(source, field) {
86
92
  const value = source[field];
87
93
  if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
@@ -492,6 +498,7 @@ function parseUserBalanceEventData(raw) {
492
498
  lockedRaw: readString(data, "lockedRaw"),
493
499
  total: readString(data, "total"),
494
500
  totalRaw: readString(data, "totalRaw"),
501
+ version: readOptionalSequence(data, "version"),
495
502
  // Free-form on the wire and read leniently: a snapshot row carries the
496
503
  // `snapshot` literal, a live frame the mutation cause, and a reason added
497
504
  // server-side after this SDK shipped must not drop the row (0XM-2627).
@@ -500,6 +507,20 @@ function parseUserBalanceEventData(raw) {
500
507
  updatedAt: readString(data, "updatedAt"),
501
508
  };
502
509
  }
510
+ /**
511
+ * Parse one row from a `balances` channel snapshot.
512
+ *
513
+ * `version` is read leniently, for the same reason `reason` is: a pre-feature
514
+ * server sends no version at all, and rejecting the row would drop the entire
515
+ * subscribe-time baseline for a client whose SDK is ahead of the server it is
516
+ * talking to. `UserBalanceEventData.version` is optional precisely so an absent
517
+ * value is representable, and consumers already treat it as the unranked
518
+ * sentinel. A version that IS present is still validated — `readSequence`
519
+ * rejects negative and unsafe values.
520
+ */
521
+ function parseBalanceSnapshotItem(raw) {
522
+ return parseUserBalanceEventData(raw);
523
+ }
503
524
  /**
504
525
  * Parse one resting order from an `orders` channel snapshot.
505
526
  *
@@ -847,9 +868,14 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
847
868
  sendAuthenticate();
848
869
  resubscribeAll();
849
870
  options.onStatusChange?.("connected");
850
- // Send auth/subscribe requests before asking the application to
851
- // reconcile. This callback does not wait for acknowledgements or
852
- // snapshots, and the SDK does not perform REST recovery itself.
871
+ // Resync-join order: the live stream resumes first (auth +
872
+ // subscribe above), then the application is asked to refetch
873
+ // snapshots and reconcile against each channel's authoritative
874
+ // identity/version. Balance ordering uses the durable row version,
875
+ // never `updatedAt`, arrival time, or receiver backlog.
876
+ //
877
+ // This callback waits for neither acknowledgements nor snapshots,
878
+ // and the SDK performs no REST recovery itself.
853
879
  if (pendingResyncCode !== null) {
854
880
  const code = pendingResyncCode;
855
881
  pendingResyncCode = null;
@@ -1201,7 +1227,7 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
1201
1227
  catch (err) {
1202
1228
  console.error("WebSocket: Error processing balance event", err);
1203
1229
  }
1204
- }, onSnapshot && snapshotFrameHandler("balances", parseUserBalanceEventData, onSnapshot));
1230
+ }, onSnapshot && snapshotFrameHandler("balances", parseBalanceSnapshotItem, onSnapshot));
1205
1231
  };
1206
1232
  const subscribeConditionalOrders = (handler, tradingPairId, onSnapshot) => {
1207
1233
  if (!handler) {
@@ -35,6 +35,7 @@ export declare const COVERED: {
35
35
  get_market_stats: string;
36
36
  get_my_fee_tier: string;
37
37
  get_pulse: string;
38
+ get_sweeper_chains: string;
38
39
  get_my_trader_code: string;
39
40
  get_rewards_balance: string;
40
41
  get_trader_code_info: string;
package/dist/coverage.js CHANGED
@@ -35,6 +35,7 @@ export const COVERED = {
35
35
  get_market_stats: "getMarketStats",
36
36
  get_my_fee_tier: "getMyFeeTier",
37
37
  get_pulse: "pulse.getPulse",
38
+ get_sweeper_chains: "sweeper.getChains",
38
39
  get_my_trader_code: "pitpass.getMyTraderCode",
39
40
  get_rewards_balance: "pitpass.getRewardsBalance",
40
41
  get_trader_code_info: "pitpass.getTraderCodeInfo",
@@ -1,7 +1,7 @@
1
1
  /** Monaco's RFC 9421 Ed25519 profile; key registration remains wallet-authorized. */
2
2
  import { ed25519 } from "@noble/curves/ed25519";
3
- import { sha256 } from "@noble/hashes/sha2";
4
- import { bytesToHex, utf8ToBytes } from "@noble/hashes/utils";
3
+ import { sha256 } from "@noble/hashes/sha2.js";
4
+ import { bytesToHex, utf8ToBytes } from "@noble/hashes/utils.js";
5
5
  function base64(bytes) {
6
6
  return btoa(String.fromCharCode(...bytes));
7
7
  }
@@ -8,8 +8,8 @@
8
8
  * key — it never sees the private key, so it cannot mint requests as the user.
9
9
  */
10
10
  import { ed25519 } from "@noble/curves/ed25519";
11
- import { sha256 } from "@noble/hashes/sha2";
12
- import { bytesToHex, hexToBytes, utf8ToBytes } from "@noble/hashes/utils";
11
+ import { sha256 } from "@noble/hashes/sha2.js";
12
+ import { bytesToHex, hexToBytes, utf8ToBytes } from "@noble/hashes/utils.js";
13
13
  /**
14
14
  * Generate a fresh ed25519 session keypair.
15
15
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0xmonaco/core",
3
- "version": "1.0.55",
3
+ "version": "1.0.57",
4
4
  "type": "module",
5
5
  "homepage": "https://docs.0xmonaco.com/sdk/typescript",
6
6
  "main": "./dist/index.js",
@@ -19,10 +19,10 @@
19
19
  "viem": "^2.45.2"
20
20
  },
21
21
  "dependencies": {
22
- "@0xmonaco/contracts": "1.0.55",
23
- "@0xmonaco/types": "1.0.55",
22
+ "@0xmonaco/contracts": "1.0.57",
23
+ "@0xmonaco/types": "1.0.57",
24
24
  "@noble/curves": "^1.9.1",
25
- "@noble/hashes": "^1.8.0",
25
+ "@noble/hashes": "^2.4.0",
26
26
  "http-status-codes": "^2.3.0"
27
27
  },
28
28
  "devDependencies": {