@0xmonaco/core 1.0.47 → 1.0.50-develop.9c1b239

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.
@@ -5,6 +5,16 @@ export declare class MarginAccountsAPIImpl extends BaseAPI implements MarginAcco
5
5
  getMarginAccountSummary(marginAccountId: string, params?: GetMarginAccountSummaryParams): Promise<MarginAccountSummary>;
6
6
  getParentMarginAccountSummary(params?: GetMarginAccountSummaryParams): Promise<MarginAccountSummary>;
7
7
  getAvailableCollateral(params?: GetAvailableCollateralParams): Promise<GetAvailableCollateralResponse>;
8
+ /**
9
+ * @deprecated This endpoint overloads two unrelated operations: with
10
+ * `tradingPairId` it allocates into that pair's isolated risk bucket
11
+ * (crediting the sole open position's stored margin), without it it
12
+ * performs a wallet-debiting deposit to the parent margin account. Use
13
+ * {@link transferCollateralToRiskBucket} for bucket funding (which now
14
+ * applies the same position-margin credit) and
15
+ * {@link transferCollateralToParentMarginAccount} for wallet-to-margin
16
+ * deposits. Kept for backward compatibility; no removal date scheduled.
17
+ */
8
18
  transferCollateralToMarginAccount(marginAccountId: string, request: TransferCollateralRequest): Promise<TransferCollateralResponse>;
9
19
  transferCollateralToParentMarginAccount(request: TransferCollateralToParentMarginAccountRequest): Promise<TransferCollateralResponse>;
10
20
  transferCollateralToRiskBucket(request: TransferCollateralToRiskBucketRequest): Promise<TransferCollateralResponse>;
@@ -27,6 +27,16 @@ export class MarginAccountsAPIImpl extends BaseAPI {
27
27
  validate(GetAvailableCollateralSchema, params);
28
28
  return await this.makeAuthenticatedRequest(perpRoutes.marginAccounts.availableCollateral(params));
29
29
  }
30
+ /**
31
+ * @deprecated This endpoint overloads two unrelated operations: with
32
+ * `tradingPairId` it allocates into that pair's isolated risk bucket
33
+ * (crediting the sole open position's stored margin), without it it
34
+ * performs a wallet-debiting deposit to the parent margin account. Use
35
+ * {@link transferCollateralToRiskBucket} for bucket funding (which now
36
+ * applies the same position-margin credit) and
37
+ * {@link transferCollateralToParentMarginAccount} for wallet-to-margin
38
+ * deposits. Kept for backward compatibility; no removal date scheduled.
39
+ */
30
40
  async transferCollateralToMarginAccount(marginAccountId, request) {
31
41
  validate(TransferCollateralSchema, { marginAccountId, request });
32
42
  return await this.makeAuthenticatedRequest(perpRoutes.marginAccounts.transferIn(marginAccountId), {
@@ -60,6 +70,10 @@ export class MarginAccountsAPIImpl extends BaseAPI {
60
70
  strategyKey: request.strategyKey,
61
71
  marginMode: request.marginMode,
62
72
  selectedTradingPairIds: request.selectedTradingPairIds,
73
+ // JSON.stringify drops it when omitted; an explicit false MUST reach
74
+ // the wire or the server applies its Adjust Margin default and every
75
+ // funding-loop top-up converts into initial margin requirement.
76
+ applyToPositionMargin: request.applyToPositionMargin,
63
77
  }),
64
78
  });
65
79
  }
@@ -1,10 +1,14 @@
1
1
  /**
2
2
  * Sweeper API Implementation
3
3
  *
4
- * Public (unauthenticated) deposit-address registration. Every (application,
5
- * user) pair has a deterministic deposit address; registering the pair makes
6
- * the backend monitor that address and sweep every ERC20 deposit into the
7
- * rollup automatically, crediting the user.
4
+ * Authenticated deposit-address registration. Every (application, user,
5
+ * deposit target) triple has a deterministic deposit address; registering the
6
+ * triple makes the backend monitor that address and sweep every ERC20 deposit
7
+ * into the rollup automatically, crediting the user.
8
+ *
9
+ * The credited address is the calling session's own — it is not a request
10
+ * field, so this can only ever register an address the caller holds the
11
+ * session key for.
8
12
  *
9
13
  * The reply returns immediately with the derived address; the one-time
10
14
  * initial balance check (sweeping anything already sitting on the address)
@@ -12,27 +16,36 @@
12
16
  *
13
17
  * @example
14
18
  * ```typescript
15
- * const { status, sweeperAddress, initialSweepCheck } = await sdk.sweeper.register({
16
- * clientId: "60eb377aab9f4ea18dcdf4747204c792",
17
- * userAddress: "0x...",
18
- * });
19
+ * const { status, sweeperAddress, initialSweepCheck } = await sdk.sweeper.register();
20
+ *
21
+ * // Or ask for the margin address instead — a different address.
22
+ * const margin = await sdk.sweeper.register({ depositTarget: "margin" });
19
23
  * ```
20
24
  */
21
25
  import type { RegisterSweeperRequest, RegisterSweeperResponse, SweeperAPI } from "@0xmonaco/types";
22
26
  import { BaseAPI } from "../base";
23
27
  export declare class SweeperAPIImpl extends BaseAPI implements SweeperAPI {
24
28
  /**
25
- * Registers an (application, user) deposit address for monitoring. Public —
26
- * no auth required.
29
+ * Registers an (application, user, deposit target) deposit address for
30
+ * monitoring. Requires authentication; the user is the session's own
31
+ * address.
32
+ *
33
+ * `registered` means the triple was added by this call,
34
+ * `already_registered` that it was known before; both carry the same derived
35
+ * deposit address. Idempotent and safe to retry — a repeat never re-triggers
36
+ * the initial balance check.
27
37
  *
28
- * `registered` means the pair was added by this call, `already_registered`
29
- * that it was known before; both carry the same derived deposit address.
30
- * Idempotent and safe to retry — a repeat never re-triggers the initial
31
- * balance check.
38
+ * `depositTarget` defaults to `"spot"`. Because it is part of the address
39
+ * derivation, `"spot"` and `"margin"` return two different addresses for the
40
+ * same application and user, each carrying its own routing request. A
41
+ * `"spot"` address is never credited to margin; `"margin"` is a request
42
+ * rather than a guarantee, since a deposit the backend cannot route to
43
+ * collateral is credited to the spot wallet instead.
32
44
  *
33
- * @param body - The application and user identifying the pair
45
+ * @param body - Optionally the destination ledger; everything else comes
46
+ * from the session
34
47
  * @returns Promise resolving to the deposit address and the initial-check
35
48
  * state
36
49
  */
37
- register(body: RegisterSweeperRequest): Promise<RegisterSweeperResponse>;
50
+ register(body?: RegisterSweeperRequest): Promise<RegisterSweeperResponse>;
38
51
  }
@@ -1,10 +1,14 @@
1
1
  /**
2
2
  * Sweeper API Implementation
3
3
  *
4
- * Public (unauthenticated) deposit-address registration. Every (application,
5
- * user) pair has a deterministic deposit address; registering the pair makes
6
- * the backend monitor that address and sweep every ERC20 deposit into the
7
- * rollup automatically, crediting the user.
4
+ * Authenticated deposit-address registration. Every (application, user,
5
+ * deposit target) triple has a deterministic deposit address; registering the
6
+ * triple makes the backend monitor that address and sweep every ERC20 deposit
7
+ * into the rollup automatically, crediting the user.
8
+ *
9
+ * The credited address is the calling session's own — it is not a request
10
+ * field, so this can only ever register an address the caller holds the
11
+ * session key for.
8
12
  *
9
13
  * The reply returns immediately with the derived address; the one-time
10
14
  * initial balance check (sweeping anything already sitting on the address)
@@ -12,32 +16,70 @@
12
16
  *
13
17
  * @example
14
18
  * ```typescript
15
- * const { status, sweeperAddress, initialSweepCheck } = await sdk.sweeper.register({
16
- * clientId: "60eb377aab9f4ea18dcdf4747204c792",
17
- * userAddress: "0x...",
18
- * });
19
+ * const { status, sweeperAddress, initialSweepCheck } = await sdk.sweeper.register();
20
+ *
21
+ * // Or ask for the margin address instead — a different address.
22
+ * const margin = await sdk.sweeper.register({ depositTarget: "margin" });
19
23
  * ```
20
24
  */
25
+ import { InvalidConfigError } from "../../errors";
21
26
  import { BaseAPI } from "../base";
22
27
  import { perpRoutes } from "../perp/routes";
28
+ /**
29
+ * Ergonomic target to its wire form.
30
+ *
31
+ * The public surface is lowercase; the contract's enum is `SPOT`/`MARGIN`, and
32
+ * the generated wire type is `"SPOT" | "MARGIN" | null`. Serializing the
33
+ * ergonomic value verbatim would put the official SDK outside its own OpenAPI
34
+ * schema — it happens to be accepted today only because the gateway uppercases
35
+ * before matching, so a server tightened to the published enum would break the
36
+ * SDK rather than the caller.
37
+ *
38
+ * Deliberately a comparison rather than an object lookup. A lookup resolves
39
+ * inherited keys, so an untyped `depositTarget: "toString"` would return
40
+ * `Object.prototype.toString` — not `undefined`, so it passes a
41
+ * validity check — and `JSON.stringify` then drops the function-valued field
42
+ * entirely, leaving the server to apply its SPOT default and hand back a
43
+ * fundable address for a target that was never valid.
44
+ */
45
+ function toWireDepositTarget(target) {
46
+ if (target === "spot") {
47
+ return "SPOT";
48
+ }
49
+ if (target === "margin") {
50
+ return "MARGIN";
51
+ }
52
+ throw new InvalidConfigError(`Invalid deposit target: ${JSON.stringify(target)}. Expected "spot" or "margin".`);
53
+ }
23
54
  export class SweeperAPIImpl extends BaseAPI {
24
55
  /**
25
- * Registers an (application, user) deposit address for monitoring. Public —
26
- * no auth required.
56
+ * Registers an (application, user, deposit target) deposit address for
57
+ * monitoring. Requires authentication; the user is the session's own
58
+ * address.
59
+ *
60
+ * `registered` means the triple was added by this call,
61
+ * `already_registered` that it was known before; both carry the same derived
62
+ * deposit address. Idempotent and safe to retry — a repeat never re-triggers
63
+ * the initial balance check.
27
64
  *
28
- * `registered` means the pair was added by this call, `already_registered`
29
- * that it was known before; both carry the same derived deposit address.
30
- * Idempotent and safe to retry — a repeat never re-triggers the initial
31
- * balance check.
65
+ * `depositTarget` defaults to `"spot"`. Because it is part of the address
66
+ * derivation, `"spot"` and `"margin"` return two different addresses for the
67
+ * same application and user, each carrying its own routing request. A
68
+ * `"spot"` address is never credited to margin; `"margin"` is a request
69
+ * rather than a guarantee, since a deposit the backend cannot route to
70
+ * collateral is credited to the spot wallet instead.
32
71
  *
33
- * @param body - The application and user identifying the pair
72
+ * @param body - Optionally the destination ledger; everything else comes
73
+ * from the session
34
74
  * @returns Promise resolving to the deposit address and the initial-check
35
75
  * state
36
76
  */
37
- async register(body) {
38
- return await this.makePublicRequest(perpRoutes.sweeper.register(), {
77
+ async register(body = {}) {
78
+ const { depositTarget, ...rest } = body;
79
+ const wireBody = depositTarget === undefined ? rest : { ...rest, depositTarget: toWireDepositTarget(depositTarget) };
80
+ return await this.makeAuthenticatedRequest(perpRoutes.sweeper.register(), {
39
81
  method: "POST",
40
- body: JSON.stringify(body),
82
+ body: JSON.stringify(wireBody),
41
83
  });
42
84
  }
43
85
  }
@@ -23,21 +23,19 @@
23
23
  * console.log(`Deposit transaction: ${result.hash}`);
24
24
  * ```
25
25
  */
26
+ import { encodeDepositApplicationData } from "@0xmonaco/contracts";
26
27
  import type { ApplicationsAPI, Balance, DepositOptions, DepositTarget, ProfileAPI, TransactionResult, VaultAPI, WithdrawalRetryOptions, WithdrawalSource, WithdrawResult } from "@0xmonaco/types";
27
28
  import { type Address, type Chain, type PublicClient, type WalletClient } from "viem";
28
29
  import { BaseAPI } from "../base";
29
30
  /**
30
31
  * Encode the on-chain `applicationData` for a deposit.
31
32
  *
32
- * For `"spot"` (the default) this stays the bare `clientId` string — byte-for-byte
33
- * identical to the legacy encoding, so existing deposits are unchanged. For
34
- * `"margin"` it emits a small JSON routing hint the indexer decodes to credit the
35
- * deposit to margin collateral.
36
- *
37
- * MUST stay in sync with the indexer decoder in
38
- * `indexer/src/listeners/deposit_routing.rs`.
33
+ * Re-exported from `@0xmonaco/contracts` rather than defined here: the same
34
+ * encoding also derives sweeper deposit addresses, and two copies of it could
35
+ * disagree about the bytes a deposit address is a hash of. Its Rust
36
+ * counterpart is `utils::deposit_routing::encode_application_data`.
39
37
  */
40
- export declare function encodeDepositApplicationData(clientId: string, target: DepositTarget): string;
38
+ export { encodeDepositApplicationData };
41
39
  export declare class VaultAPIImpl extends BaseAPI implements VaultAPI {
42
40
  private readonly publicClient;
43
41
  private readonly chain;
@@ -23,7 +23,7 @@
23
23
  * console.log(`Deposit transaction: ${result.hash}`);
24
24
  * ```
25
25
  */
26
- import { CONTRACT_ABIS } from "@0xmonaco/contracts";
26
+ import { CONTRACT_ABIS, encodeDepositApplicationData } from "@0xmonaco/contracts";
27
27
  import { ApproveTokenSchema, DepositSchema, validate, WithdrawSchema } from "@0xmonaco/types";
28
28
  import { erc20Abi } from "viem";
29
29
  import { APIError, ContractError, InvalidConfigError } from "../../errors";
@@ -42,17 +42,12 @@ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
42
42
  /**
43
43
  * Encode the on-chain `applicationData` for a deposit.
44
44
  *
45
- * For `"spot"` (the default) this stays the bare `clientId` string — byte-for-byte
46
- * identical to the legacy encoding, so existing deposits are unchanged. For
47
- * `"margin"` it emits a small JSON routing hint the indexer decodes to credit the
48
- * deposit to margin collateral.
49
- *
50
- * MUST stay in sync with the indexer decoder in
51
- * `indexer/src/listeners/deposit_routing.rs`.
45
+ * Re-exported from `@0xmonaco/contracts` rather than defined here: the same
46
+ * encoding also derives sweeper deposit addresses, and two copies of it could
47
+ * disagree about the bytes a deposit address is a hash of. Its Rust
48
+ * counterpart is `utils::deposit_routing::encode_application_data`.
52
49
  */
53
- export function encodeDepositApplicationData(clientId, target) {
54
- return target === "margin" ? JSON.stringify({ clientId, depositTarget: "MARGIN" }) : clientId;
55
- }
50
+ export { encodeDepositApplicationData };
56
51
  export class VaultAPIImpl extends BaseAPI {
57
52
  publicClient;
58
53
  chain;
@@ -7,13 +7,6 @@ const CONNECTION_TIMEOUT = 10000;
7
7
  const HEARTBEAT_INTERVAL = 15000;
8
8
  const MAX_RECONNECT_DELAY = 30000;
9
9
  const RECONNECT_BASE_DELAY = 1000;
10
- const CONDITIONAL_ORDER_REASONS = ["created", "activated", "cancelled", "parent_cancelled", "triggered", "failed", "oco_cancelled"];
11
- const CONDITIONAL_ORDER_CONDITION_TYPES = ["STOP_LOSS", "TAKE_PROFIT"];
12
- const CONDITIONAL_ORDER_TRIGGER_SOURCES = ["MARK_PRICE"];
13
- const CONDITIONAL_ORDER_STATES = ["PENDING_PARENT", "ACTIVE", "TRIGGERING", "TRIGGERED", "CANCELLED", "EXPIRED", "FAILED"];
14
- const CONDITIONAL_ORDER_ASSOCIATION_TYPES = ["POSITION", "PARENT_ORDER"];
15
- const TWAP_ORDER_STATES = ["PENDING", "ACTIVE", "COMPLETED", "CANCELLED"];
16
- const INSTRUMENT_CHANGE_KINDS = ["listing", "delisting", "halt", "unhalt", "config_change"];
17
10
  function isRecord(value) {
18
11
  return typeof value === "object" && value !== null && !Array.isArray(value);
19
12
  }
@@ -76,14 +69,20 @@ function readEnum(source, field, allowed) {
76
69
  }
77
70
  return value;
78
71
  }
79
- function readOptionalEnum(source, field, allowed) {
80
- const value = source[field];
81
- if (value === undefined || value === null)
82
- return undefined;
83
- if (typeof value !== "string" || !allowed.includes(value)) {
84
- throw new Error(`Invalid WebSocket event: ${field} must be one of ${allowed.join(", ")}`);
85
- }
86
- return value;
72
+ /**
73
+ * Read an optional wire enum leniently.
74
+ *
75
+ * Absent, `null`, and a present-but-empty string all mean "not set": server
76
+ * side these fields are `Option<..>` over closed enums, so `""` is a malformed
77
+ * value rather than a variant this SDK version predates, and letting it through
78
+ * would put a string the union does not contain onto the typed surface. It is
79
+ * mapped to absent rather than thrown on, because throwing would drop the whole
80
+ * frame over one optional field. A non-empty value is passed through untouched,
81
+ * whether or not the SDK recognizes it.
82
+ */
83
+ function readOptionalEnumValue(source, field) {
84
+ const value = readOptionalString(source, field);
85
+ return value === undefined || value.length === 0 ? undefined : value;
87
86
  }
88
87
  function parseConditionalOrderEvent(rawData) {
89
88
  if (!isRecord(rawData)) {
@@ -94,6 +93,13 @@ function parseConditionalOrderEvent(rawData) {
94
93
  }
95
94
  const data = keysToCamelCase(rawData.data);
96
95
  return {
96
+ // event_type is the event-class discriminator, validated strictly so a
97
+ // mis-routed frame is rejected rather than parsed as a conditional order.
98
+ // The value fields below stay lenient — read as strings and cast, never
99
+ // checked against an allow-list — so an unexpected *value* never drops the
100
+ // whole update (0XM-2627). The parse throw is swallowed by the
101
+ // message-handler catch, so a strict enum here means a silently lost frame
102
+ // with nothing surfaced to the consumer.
97
103
  eventType: readEnum(rawData, "event_type", ["conditional_order_update"]),
98
104
  userId: readString(rawData, "user_id"),
99
105
  data: {
@@ -102,27 +108,29 @@ function parseConditionalOrderEvent(rawData) {
102
108
  marginAccountId: readString(data, "marginAccountId"),
103
109
  positionId: readOptionalString(data, "positionId"),
104
110
  parentOrderId: readOptionalString(data, "parentOrderId"),
105
- associationType: readOptionalEnum(data, "associationType", CONDITIONAL_ORDER_ASSOCIATION_TYPES),
111
+ associationType: readOptionalEnumValue(data, "associationType"),
106
112
  linkedGroupId: readOptionalString(data, "linkedGroupId"),
107
- conditionType: readEnum(data, "conditionType", CONDITIONAL_ORDER_CONDITION_TYPES),
108
- triggerSource: readEnum(data, "triggerSource", CONDITIONAL_ORDER_TRIGGER_SOURCES),
113
+ conditionType: readString(data, "conditionType"),
114
+ triggerSource: readString(data, "triggerSource"),
109
115
  triggerPrice: readString(data, "triggerPrice"),
110
- side: readEnum(data, "side", ["BUY", "SELL"]),
111
- positionSide: readEnum(data, "positionSide", ["LONG", "SHORT", "NONE"]),
112
- orderType: readEnum(data, "orderType", ["LIMIT", "MARKET"]),
116
+ side: readString(data, "side"),
117
+ positionSide: readString(data, "positionSide"),
118
+ orderType: readString(data, "orderType"),
113
119
  limitPrice: readOptionalString(data, "limitPrice"),
114
120
  quantity: readOptionalString(data, "quantity"),
115
121
  slippageToleranceBps: readOptionalNumber(data, "slippageToleranceBps"),
116
122
  reduceOnly: readBoolean(data, "reduceOnly"),
117
- timeInForce: readOptionalEnum(data, "timeInForce", ["GTC", "IOC"]),
118
- state: readEnum(data, "state", CONDITIONAL_ORDER_STATES),
123
+ timeInForce: readOptionalEnumValue(data, "timeInForce"),
124
+ state: readString(data, "state"),
119
125
  triggeredOrderId: readOptionalString(data, "triggeredOrderId"),
120
126
  triggeredAt: readOptionalString(data, "triggeredAt"),
121
127
  activatedAt: readOptionalString(data, "activatedAt"),
122
128
  cancelledAt: readOptionalString(data, "cancelledAt"),
123
129
  expiresAt: readOptionalString(data, "expiresAt"),
124
130
  failureReason: readOptionalString(data, "failureReason"),
125
- reason: readEnum(data, "reason", CONDITIONAL_ORDER_REASONS),
131
+ // Free-form on the wire (the server field is a plain string), so it is
132
+ // passed through untouched rather than matched against a value list.
133
+ reason: readString(data, "reason"),
126
134
  updatedAt: readString(data, "updatedAt"),
127
135
  },
128
136
  };
@@ -300,7 +308,14 @@ function parseInstrumentEventData(raw) {
300
308
  }
301
309
  const data = keysToCamelCase(raw);
302
310
  return {
303
- change: readEnum(data, "change", INSTRUMENT_CHANGE_KINDS),
311
+ // Read as a plain non-empty string, not matched against a value list: an
312
+ // unrecognized change kind must not throw, because the throw is swallowed
313
+ // by the message-handler catch and takes the whole frame with it — and
314
+ // every instrument frame carries the market's FULL current config, so a
315
+ // dropped frame can hide a market going inactive behind an unfamiliar
316
+ // label. The declared `InstrumentChangeKind` union documents the values
317
+ // the server sends today, not a runtime guarantee.
318
+ change: readString(data, "change"),
304
319
  tradingPairId: readString(data, "tradingPairId"),
305
320
  symbol: readString(data, "symbol"),
306
321
  isActive: readBoolean(data, "isActive"),
@@ -363,9 +378,16 @@ function parseTwapOrderEvent(rawData) {
363
378
  if (typeof twapOrderId !== "string" || twapOrderId.length === 0) {
364
379
  throw new Error("Invalid TWAP order event: twapOrderId must be a non-empty string");
365
380
  }
381
+ // `state` is required, so an absent/non-string/empty one is a malformed
382
+ // frame — but an unrecognized VALUE is passed through untouched. Matching it
383
+ // against a value list would throw, the message-handler catch would swallow
384
+ // the throw, and the whole update would be lost: a TWAP order whose state
385
+ // this SDK version predates would stop reporting its fills entirely
386
+ // (0XM-2627's failure mode). The declared `TwapOrderState` union documents
387
+ // the values the server sends today, not a runtime guarantee.
366
388
  const state = data.state;
367
- if (typeof state !== "string" || !TWAP_ORDER_STATES.includes(state)) {
368
- throw new Error(`Invalid TWAP order event: state must be one of ${TWAP_ORDER_STATES.join(", ")}`);
389
+ if (typeof state !== "string" || state.length === 0) {
390
+ throw new Error("Invalid TWAP order event: state must be a non-empty string");
369
391
  }
370
392
  return {
371
393
  eventType: "twap_order_update",
@@ -543,6 +565,21 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
543
565
  return frameChannel === subscriptionChannel || frameChannel.startsWith(`${subscriptionChannel}:`);
544
566
  };
545
567
  const routeMessage = (msg) => {
568
+ if (msg.type === "Error") {
569
+ // Connection-scoped, not channel-scoped: an Error frame matches no
570
+ // subscription, so without this it reached no consumer at all and a
571
+ // failed subscribe / invalidated session / unreadable subscribe-time
572
+ // snapshot was silent. `code` is passed through whatever its value —
573
+ // a code added server-side after this SDK shipped must still reach the
574
+ // app rather than be dropped for being unrecognized (0XM-2627's
575
+ // leniency contract) — but a frame missing either field is malformed,
576
+ // and synthesizing the missing half would send the app down a branch on
577
+ // data the server never sent.
578
+ if (typeof msg.code === "string" && typeof msg.message === "string") {
579
+ options.onError?.({ code: msg.code, message: msg.message });
580
+ }
581
+ return;
582
+ }
546
583
  if (!msg.channel)
547
584
  return;
548
585
  if (msg.type === "Snapshot") {
@@ -806,7 +843,15 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
806
843
  // Concrete price series of this candle (`trade`/`mark`). Present on
807
844
  // every current frame; a wildcard (`*`) subscriber reads it to tell the
808
845
  // two streams apart. Omitted when a legacy frame carries no `price_type`.
809
- const priceType = data.price_type === "trade" || data.price_type === "mark" ? data.price_type : undefined;
846
+ //
847
+ // Passed through rather than matched against the two known values: a
848
+ // wildcard subscription accepts any suffix, so a series added
849
+ // server-side after this version reaches this parser, and reporting it
850
+ // as `undefined` would leave a wildcard subscriber unable to do the one
851
+ // thing the field exists for. Only a missing or non-string value is
852
+ // treated as absent.
853
+ const rawPriceType = data.price_type;
854
+ const priceType = typeof rawPriceType === "string" && rawPriceType !== "" ? rawPriceType : undefined;
810
855
  const event = {
811
856
  // `trading_pair_id` is the pair UUID; `symbol` is the display string.
812
857
  // `tradingPairId` must carry the UUID (the symbol lives in `candlestick.s`).
@@ -877,12 +922,17 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
877
922
  token: (movementData.token ?? movementData.token_address),
878
923
  tokenAddress: (movementData.token ?? movementData.token_address),
879
924
  assetId: movementData.asset_id,
925
+ symbol: movementData.symbol,
880
926
  amount: String(movementData.amount),
881
927
  amountRaw: movementData.amount_raw,
882
928
  balanceBefore: movementData.balance_before != null ? String(movementData.balance_before) : undefined,
929
+ balanceBeforeRaw: movementData.balance_before_raw,
883
930
  balanceAfter: movementData.balance_after != null ? String(movementData.balance_after) : undefined,
931
+ balanceAfterRaw: movementData.balance_after_raw,
884
932
  lockedBefore: movementData.locked_before != null ? String(movementData.locked_before) : undefined,
933
+ lockedBeforeRaw: movementData.locked_before_raw,
885
934
  lockedAfter: movementData.locked_after != null ? String(movementData.locked_after) : undefined,
935
+ lockedAfterRaw: movementData.locked_after_raw,
886
936
  referenceId: movementData.reference_id,
887
937
  referenceType: movementData.reference_type,
888
938
  description: movementData.description,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0xmonaco/core",
3
- "version": "1.0.47",
3
+ "version": "1.0.50-develop.9c1b239",
4
4
  "type": "module",
5
5
  "homepage": "https://docs.0xmonaco.com/sdk/typescript",
6
6
  "main": "./dist/index.js",
@@ -19,8 +19,8 @@
19
19
  "viem": "^2.45.2"
20
20
  },
21
21
  "dependencies": {
22
- "@0xmonaco/contracts": "1.0.47",
23
- "@0xmonaco/types": "1.0.47",
22
+ "@0xmonaco/contracts": "1.0.50-develop.9c1b239",
23
+ "@0xmonaco/types": "1.0.50-develop.9c1b239",
24
24
  "@noble/curves": "^1.9.1",
25
25
  "@noble/hashes": "^1.8.0",
26
26
  "http-status-codes": "^2.3.0"