@0xmonaco/core 1.0.82 → 1.0.85

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/README.md CHANGED
@@ -54,7 +54,7 @@ The SDK supports the following documented preset networks. Configure the network
54
54
 
55
55
  **Preset Networks:**
56
56
  - `"staging"` - Staging environment (https://staging.apimonaco.xyz)
57
- - `"mainnet"` - Production environment (https://api.monaco.xyz)
57
+ - `"mainnet"` - Production environment (https://apimonaco.xyz)
58
58
 
59
59
  WebSocket URLs are automatically resolved per network.
60
60
 
@@ -25,7 +25,7 @@
25
25
  * );
26
26
  * ```
27
27
  */
28
- import type { AuthAPI, AuthState, ChallengeResponse, SessionCredentials, SessionRefreshResponse } from "@0xmonaco/types";
28
+ import type { AuthAPI, AuthState, ChallengeResponse, InviteCodeList, InviteCodeRequestSummary, SessionCredentials, SessionRefreshResponse } from "@0xmonaco/types";
29
29
  import type { Chain, WalletClient } from "viem";
30
30
  import { BaseAPI } from "../base";
31
31
  export declare class AuthAPIImpl extends BaseAPI implements AuthAPI {
@@ -69,7 +69,7 @@ export declare class AuthAPIImpl extends BaseAPI implements AuthAPI {
69
69
  * console.log(`User ID: ${authResult.user.id}`);
70
70
  * ```
71
71
  */
72
- authenticate(clientId: string, referralCode?: string): Promise<AuthState>;
72
+ authenticate(clientId: string, referralCode?: string, inviteCode?: string): Promise<AuthState>;
73
73
  /**
74
74
  * Signs a challenge message using the wallet client.
75
75
  *
@@ -149,7 +149,11 @@ export declare class AuthAPIImpl extends BaseAPI implements AuthAPI {
149
149
  * console.log(`Session public key: ${authResult.sessionPublicKey}`);
150
150
  * ```
151
151
  */
152
- verifySignature(address: string, signature: string, nonce: string, clientId: string, session: SessionCredentials, referralCode?: string): Promise<AuthState>;
152
+ verifySignature(address: string, signature: string, nonce: string, clientId: string, session: SessionCredentials, referralCode?: string, inviteCode?: string): Promise<AuthState>;
153
+ /** Mint one code: one initial allowance plus one per 250,000 USDC of Mainnet volume. */
154
+ requestInviteCodes(): Promise<InviteCodeRequestSummary>;
155
+ /** Read the allowance and minted codes. Read-only: never consumes a slot. */
156
+ listInviteCodes(): Promise<InviteCodeList>;
153
157
  /**
154
158
  * Refreshes the current session's expiry.
155
159
  *
@@ -75,7 +75,7 @@ export class AuthAPIImpl extends BaseAPI {
75
75
  * console.log(`User ID: ${authResult.user.id}`);
76
76
  * ```
77
77
  */
78
- async authenticate(clientId, referralCode) {
78
+ async authenticate(clientId, referralCode, inviteCode) {
79
79
  if (!this.walletClient) {
80
80
  throw new InvalidConfigError("Wallet client not set. Connect a wallet first.", "walletClient");
81
81
  }
@@ -95,7 +95,7 @@ export class AuthAPIImpl extends BaseAPI {
95
95
  // 3. Sign the challenge message with the wallet
96
96
  const signature = await this.signChallenge(challenge.message);
97
97
  // 4. Verify signature, registering the session public key (and optional referral)
98
- return await this.verifySignature(account.address, signature, challenge.nonce, clientId, session, referralCode);
98
+ return await this.verifySignature(account.address, signature, challenge.nonce, clientId, session, referralCode, inviteCode);
99
99
  }
100
100
  /**
101
101
  * Signs a challenge message using the wallet client.
@@ -204,7 +204,7 @@ export class AuthAPIImpl extends BaseAPI {
204
204
  * console.log(`Session public key: ${authResult.sessionPublicKey}`);
205
205
  * ```
206
206
  */
207
- async verifySignature(address, signature, nonce, clientId, session, referralCode) {
207
+ async verifySignature(address, signature, nonce, clientId, session, referralCode, inviteCode) {
208
208
  const responseBody = await this.makePublicRequest("/api/v1/auth/verify", {
209
209
  method: "POST",
210
210
  body: JSON.stringify({
@@ -215,6 +215,7 @@ export class AuthAPIImpl extends BaseAPI {
215
215
  chainId: this.chain.id,
216
216
  sessionPublicKey: session.publicKey,
217
217
  ...(referralCode ? { referralCode } : {}),
218
+ ...(inviteCode ? { inviteCode } : {}),
218
219
  }),
219
220
  });
220
221
  return {
@@ -234,6 +235,19 @@ export class AuthAPIImpl extends BaseAPI {
234
235
  ...(responseBody.referralApplied !== undefined ? { referralApplied: responseBody.referralApplied } : {}),
235
236
  };
236
237
  }
238
+ /** Mint one code: one initial allowance plus one per 250,000 USDC of Mainnet volume. */
239
+ async requestInviteCodes() {
240
+ return await this.makeAuthenticatedRequest("/api/v1/invite-codes/requests", {
241
+ method: "POST",
242
+ body: JSON.stringify({}),
243
+ });
244
+ }
245
+ /** Read the allowance and minted codes. Read-only: never consumes a slot. */
246
+ async listInviteCodes() {
247
+ return await this.makeAuthenticatedRequest("/api/v1/invite-codes", {
248
+ method: "GET",
249
+ });
250
+ }
237
251
  /**
238
252
  * Refreshes the current session's expiry.
239
253
  *
@@ -318,7 +318,8 @@ export declare class TradingAPIImpl extends BaseAPI implements TradingAPI {
318
318
  * Every row is a BOOK order (`LIMIT` or `MARKET`); filter `status` to
319
319
  * `["SUBMITTED", "PARTIALLY_FILLED"]` for the resting working set. TWAP parents and
320
320
  * conditional (TP/SL) orders are not rows here — list them with `listTwapOrders`
321
- * and `listConditionalOrders`.
321
+ * and `listConditionalOrders`. Nor are a TWAP's slices, the child orders that carry
322
+ * `parentOrderId`: read them with `listTwapOrderSlices`.
322
323
  * When neither `page` nor `pageToken` is passed, the SDK drives cursor (pageToken)
323
324
  * pagination: it starts a cursor walk from the newest (or oldest, for
324
325
  * `orderBy: "ASC"`) row and the response's `nextPageToken` resumes it. Both modes
@@ -498,7 +498,8 @@ export class TradingAPIImpl extends BaseAPI {
498
498
  * Every row is a BOOK order (`LIMIT` or `MARKET`); filter `status` to
499
499
  * `["SUBMITTED", "PARTIALLY_FILLED"]` for the resting working set. TWAP parents and
500
500
  * conditional (TP/SL) orders are not rows here — list them with `listTwapOrders`
501
- * and `listConditionalOrders`.
501
+ * and `listConditionalOrders`. Nor are a TWAP's slices, the child orders that carry
502
+ * `parentOrderId`: read them with `listTwapOrderSlices`.
502
503
  * When neither `page` nor `pageToken` is passed, the SDK drives cursor (pageToken)
503
504
  * pagination: it starts a cursor walk from the newest (or oldest, for
504
505
  * `orderBy: "ASC"`) row and the response's `nextPageToken` resumes it. Both modes
@@ -40,6 +40,19 @@ function readOptionalString(source, field) {
40
40
  }
41
41
  return value;
42
42
  }
43
+ /**
44
+ * Read an optional decimal-string field: absent (or `null`) is `undefined`,
45
+ * and a present value must be a non-empty string. An empty string is
46
+ * rejected rather than surfaced, because a client converting it with
47
+ * `Number("")` would read it as zero.
48
+ */
49
+ function readOptionalDecimalString(source, field) {
50
+ const value = readOptionalString(source, field);
51
+ if (value === "") {
52
+ throw new Error(`Invalid WebSocket event: ${field} must not be empty`);
53
+ }
54
+ return value;
55
+ }
43
56
  /**
44
57
  * Read a field the server serializes as an explicit `null` when unset.
45
58
  *
@@ -377,6 +390,11 @@ function parseAccountEventData(raw) {
377
390
  freeCollateral: readString(data, "freeCollateral"),
378
391
  marginRatio: readString(data, "marginRatio"),
379
392
  maintenanceRequirement: readString(data, "maintenanceRequirement"),
393
+ // Optional so a frame from an older server parses; a present value must
394
+ // still be a non-empty decimal string, or the frame is rejected.
395
+ initialMarginRequired: readOptionalDecimalString(data, "initialMarginRequired"),
396
+ withdrawableCollateral: readOptionalDecimalString(data, "withdrawableCollateral"),
397
+ realizedPnl: readOptionalDecimalString(data, "realizedPnl"),
380
398
  updatedAt: readString(data, "updatedAt"),
381
399
  };
382
400
  }
@@ -553,6 +571,8 @@ function parseOHLCVSnapshotCandle(raw, interval) {
553
571
  v: readString(raw, "volume"),
554
572
  i: interval,
555
573
  n: readSequence(raw, "trades_count"),
574
+ openInterestBase: readOptionalString(raw, "open_interest_base"),
575
+ openInterestNotional: readOptionalString(raw, "open_interest_notional"),
556
576
  };
557
577
  }
558
578
  function parseInstrumentEventData(raw) {
@@ -798,8 +818,10 @@ function parseBalanceSnapshotItem(raw) {
798
818
  *
799
819
  * The optional fields are read as nullable, not optional: they are Rust
800
820
  * `Option`s serialized without `skip_serializing_if`, so the key is always
801
- * present carrying `null`. `clientOrderId` is the one field the server omits
802
- * outright, so it is the one read as optional.
821
+ * present carrying `null`. The fields the server omits outright
822
+ * (`clientOrderId`, `selfTradePreventionMode`, `parentOrderId`, `postOnly`,
823
+ * `expirationDate`, `triggerPrice`, `positionSide`, `version` and `origin`) are
824
+ * read as optional and spread in only when present.
803
825
  */
804
826
  function parseOrderSnapshotItem(raw) {
805
827
  if (!isRecord(raw)) {
@@ -817,6 +839,7 @@ function parseOrderSnapshotItem(raw) {
817
839
  const triggerPrice = readOptionalString(item, "triggerPrice");
818
840
  const positionSide = readOptionalString(item, "positionSide");
819
841
  const version = readOptionalNumber(item, "version");
842
+ const origin = readOptionalString(item, "origin");
820
843
  return {
821
844
  orderId: readString(item, "orderId"),
822
845
  userId: readString(item, "userId"),
@@ -864,6 +887,10 @@ function parseOrderSnapshotItem(raw) {
864
887
  // than becoming `version: undefined` — a resync compares this value, and
865
888
  // absent means unknown, never zero (0XM-2440).
866
889
  ...(version === undefined ? {} : { version }),
890
+ // Read as a plain string, like the value fields above: an unrecognized
891
+ // origin passes through rather than dropping the row, and absent stays
892
+ // absent.
893
+ ...(origin === undefined ? {} : { origin }),
867
894
  };
868
895
  }
869
896
  /**
@@ -1246,6 +1273,9 @@ export function createMonacoWebSocket(baseUrl, options = {}, serverClock = new S
1246
1273
  if (msg.code === "AUTH_FAILED") {
1247
1274
  lastAuthError = { code: msg.code, message: msg.message, session: pendingAuthSessions.shift() };
1248
1275
  }
1276
+ else if (msg.code === "ACCOUNT_DEACTIVATED") {
1277
+ lastAuthError = { code: msg.code, message: msg.message, session: pendingAuthSessions.shift() ?? confirmedSession };
1278
+ }
1249
1279
  else if (msg.code === "SESSION_INVALID") {
1250
1280
  lastAuthError = { code: msg.code, message: msg.message, session: confirmedSession ?? pendingAuthSessions[0] };
1251
1281
  }
@@ -1428,7 +1458,10 @@ export function createMonacoWebSocket(baseUrl, options = {}, serverClock = new S
1428
1458
  const skewRetry = autoReconnect && authError?.code === "AUTH_FAILED" && SKEW_REJECTION.test(authError.message) && skewAuthRetries < MAX_SKEW_AUTH_RETRIES;
1429
1459
  if (skewRetry)
1430
1460
  skewAuthRetries++;
1431
- const authLoss = !skewRetry && authError !== null && authError.session !== undefined && (authError.code === "AUTH_FAILED" || event.code === 1008);
1461
+ const authLoss = !skewRetry &&
1462
+ authError !== null &&
1463
+ authError.session !== undefined &&
1464
+ (authError.code === "AUTH_FAILED" || authError.code === "ACCOUNT_DEACTIVATED" || event.code === 1008);
1432
1465
  const terminalAuthLoss = authLoss && session === authError.session;
1433
1466
  if (terminalAuthLoss) {
1434
1467
  session = undefined;
@@ -1662,6 +1695,8 @@ export function createMonacoWebSocket(baseUrl, options = {}, serverClock = new S
1662
1695
  s: data.symbol,
1663
1696
  i: data.interval,
1664
1697
  n: ohlcvData.trades_count || 0,
1698
+ openInterestBase: readOptionalString(ohlcvData, "open_interest_base"),
1699
+ openInterestNotional: readOptionalString(ohlcvData, "open_interest_notional"),
1665
1700
  },
1666
1701
  };
1667
1702
  handler(event);
@@ -1906,7 +1941,8 @@ export function createMonacoWebSocket(baseUrl, options = {}, serverClock = new S
1906
1941
  /**
1907
1942
  * Subscribe to the authenticated user's account-level margin health updates
1908
1943
  * (equity, collateral, free collateral, margin ratio, maintenance
1909
- * requirement). Requires authentication - the user is identified from the
1944
+ * requirement, initial margin required, withdrawable collateral, realized
1945
+ * PnL). Requires authentication - the user is identified from the
1910
1946
  * signed session-key handshake. Detected broadcast lag closes the connection
1911
1947
  * with 1013; reconnect and reconcile missed state. Each frame is a terminal-state snapshot
1912
1948
  * per margin account, not an additive delta.
@@ -34,6 +34,8 @@ export declare const COVERED: {
34
34
  list_twap_order_slices: string;
35
35
  close_position: string;
36
36
  create_challenge: string;
37
+ request_invite_codes: string;
38
+ list_invite_codes: string;
37
39
  create_delegated_session: string;
38
40
  create_order: string;
39
41
  create_sub_account_limit: string;
package/dist/coverage.js CHANGED
@@ -34,6 +34,8 @@ export const COVERED = {
34
34
  list_twap_order_slices: "listTwapOrderSlices",
35
35
  close_position: "closePosition",
36
36
  create_challenge: "createChallenge",
37
+ request_invite_codes: "auth.requestInviteCodes",
38
+ list_invite_codes: "auth.listInviteCodes",
37
39
  create_delegated_session: "createDelegatedSession",
38
40
  create_order: "placeLimitOrder / placeMarketOrder",
39
41
  create_sub_account_limit: "subAccounts.createLimit",
@@ -303,6 +303,12 @@ export declare class APIError extends MonacoCoreError {
303
303
  requestId?: string;
304
304
  retryAfter?: number;
305
305
  });
306
+ /**
307
+ * The server's stable error code from the response envelope's `code` field
308
+ * (e.g. `INVITE_CODE_REQUIRED`, `POST_ONLY_WOULD_CROSS`), when it sent one.
309
+ * Branch on this rather than on the message text.
310
+ */
311
+ get serverCode(): string | undefined;
306
312
  /**
307
313
  * Override toJSON to include API-specific fields with sanitization
308
314
  *
@@ -563,6 +563,19 @@ export class APIError extends MonacoCoreError {
563
563
  this.requestId = options?.requestId;
564
564
  this.retryAfter = options?.retryAfter;
565
565
  }
566
+ /**
567
+ * The server's stable error code from the response envelope's `code` field
568
+ * (e.g. `INVITE_CODE_REQUIRED`, `POST_ONLY_WOULD_CROSS`), when it sent one.
569
+ * Branch on this rather than on the message text.
570
+ */
571
+ get serverCode() {
572
+ const body = this.responseBody;
573
+ if (body && typeof body === "object" && "code" in body) {
574
+ const code = body.code;
575
+ return typeof code === "string" ? code : undefined;
576
+ }
577
+ return undefined;
578
+ }
566
579
  /**
567
580
  * Override toJSON to include API-specific fields with sanitization
568
581
  *
@@ -10,7 +10,9 @@
10
10
  const LOCAL_URL = "http://localhost:8080";
11
11
  const DEVELOPMENT_URL = "https://develop.apimonaco.xyz";
12
12
  const STAGING_URL = "https://staging.apimonaco.xyz";
13
- const MAINNET_URL = "https://api.monaco.xyz";
13
+ // https://api.monaco.xyz never resolved (the monaco.xyz zone has no NS
14
+ // delegation), so this is not a behavior change for any working client.
15
+ const MAINNET_URL = "https://apimonaco.xyz";
14
16
  const DEFAULT_API_URLS = {
15
17
  local: LOCAL_URL,
16
18
  development: DEVELOPMENT_URL,
package/dist/sdk.d.ts CHANGED
@@ -62,6 +62,7 @@ export declare class MonacoSDKImpl implements MonacoSDK {
62
62
  * @param clientId - The client ID for authentication
63
63
  * @param options - Optional configuration
64
64
  * @param options.connectWebSocket - Auto-connect WebSocket after login (default: false)
65
+ * @param options.inviteCode - Optional case-sensitive Mainnet invite code
65
66
  *
66
67
  * @example
67
68
  * ```typescript
@@ -79,6 +80,7 @@ export declare class MonacoSDKImpl implements MonacoSDK {
79
80
  */
80
81
  login(clientId: string, options?: {
81
82
  connectWebSocket?: boolean;
83
+ inviteCode?: string;
82
84
  }): Promise<AuthState>;
83
85
  /**
84
86
  * Create and adopt an owner-scoped delegated session.
package/dist/sdk.js CHANGED
@@ -213,6 +213,7 @@ export class MonacoSDKImpl {
213
213
  * @param clientId - The client ID for authentication
214
214
  * @param options - Optional configuration
215
215
  * @param options.connectWebSocket - Auto-connect WebSocket after login (default: false)
216
+ * @param options.inviteCode - Optional case-sensitive Mainnet invite code
216
217
  *
217
218
  * @example
218
219
  * ```typescript
@@ -229,7 +230,7 @@ export class MonacoSDKImpl {
229
230
  * ```
230
231
  */
231
232
  async login(clientId, options) {
232
- this.authState = await this.auth.authenticate(clientId);
233
+ this.authState = await this.auth.authenticate(clientId, undefined, options?.inviteCode);
233
234
  this.propagateSession(this.sessionFromAuthState(this.authState));
234
235
  // Auto-connect WebSocket if requested
235
236
  if (options?.connectWebSocket) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0xmonaco/core",
3
- "version": "1.0.82",
3
+ "version": "1.0.85",
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.82",
23
- "@0xmonaco/types": "1.0.82",
22
+ "@0xmonaco/contracts": "1.0.85",
23
+ "@0xmonaco/types": "1.0.85",
24
24
  "@noble/curves": "^1.9.1",
25
25
  "@noble/hashes": "^2.4.0",
26
26
  "http-status-codes": "^2.3.0"