@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.
- package/dist/api/margin-accounts/api.d.ts +10 -0
- package/dist/api/margin-accounts/api.js +14 -0
- package/dist/api/sweeper/api.d.ts +29 -16
- package/dist/api/sweeper/api.js +60 -18
- package/dist/api/vault/api.d.ts +6 -8
- package/dist/api/vault/api.js +6 -11
- package/dist/api/websocket/websocket.js +78 -28
- package/package.json +3 -3
|
@@ -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
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* the backend monitor that address and sweep every ERC20 deposit
|
|
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
|
-
*
|
|
17
|
-
*
|
|
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
|
|
26
|
-
*
|
|
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
|
-
* `
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
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 -
|
|
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
|
|
50
|
+
register(body?: RegisterSweeperRequest): Promise<RegisterSweeperResponse>;
|
|
38
51
|
}
|
package/dist/api/sweeper/api.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Sweeper API Implementation
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* the backend monitor that address and sweep every ERC20 deposit
|
|
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
|
-
*
|
|
17
|
-
*
|
|
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
|
|
26
|
-
*
|
|
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
|
-
* `
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
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 -
|
|
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
|
-
|
|
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(
|
|
82
|
+
body: JSON.stringify(wireBody),
|
|
41
83
|
});
|
|
42
84
|
}
|
|
43
85
|
}
|
package/dist/api/vault/api.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
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
|
|
38
|
+
export { encodeDepositApplicationData };
|
|
41
39
|
export declare class VaultAPIImpl extends BaseAPI implements VaultAPI {
|
|
42
40
|
private readonly publicClient;
|
|
43
41
|
private readonly chain;
|
package/dist/api/vault/api.js
CHANGED
|
@@ -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
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
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
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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:
|
|
111
|
+
associationType: readOptionalEnumValue(data, "associationType"),
|
|
106
112
|
linkedGroupId: readOptionalString(data, "linkedGroupId"),
|
|
107
|
-
conditionType:
|
|
108
|
-
triggerSource:
|
|
113
|
+
conditionType: readString(data, "conditionType"),
|
|
114
|
+
triggerSource: readString(data, "triggerSource"),
|
|
109
115
|
triggerPrice: readString(data, "triggerPrice"),
|
|
110
|
-
side:
|
|
111
|
-
positionSide:
|
|
112
|
-
orderType:
|
|
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:
|
|
118
|
-
state:
|
|
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
|
-
|
|
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
|
-
|
|
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" ||
|
|
368
|
-
throw new Error(
|
|
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
|
-
|
|
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.
|
|
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.
|
|
23
|
-
"@0xmonaco/types": "1.0.
|
|
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"
|