@0xmonaco/core 1.0.90 → 1.0.92

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
@@ -50,26 +50,12 @@ npm install viem@^2.31.7
50
50
 
51
51
  ## Network Support
52
52
 
53
- The SDK supports the following documented preset networks. Configure the network by providing the `network` and `seiRpcUrl` parameters:
54
-
55
- **Preset Networks:**
56
- - `"staging"` - Staging environment (https://staging.apimonaco.xyz)
57
- - `"mainnet"` - Production environment (https://apimonaco.xyz)
58
-
59
- WebSocket URLs are automatically resolved per network.
53
+ Configure the network by providing the `network` and `seiRpcUrl` parameters. Use the `"mainnet"` preset, which connects to Sei mainnet and the production API (https://apimonaco.xyz). WebSocket URLs are resolved automatically.
60
54
 
61
55
  ```typescript
62
56
  import { MonacoSDK } from "@0xmonaco/core";
63
57
 
64
- // Staging configuration
65
- const stagingSdk = new MonacoSDK({
66
- walletClient,
67
- network: "staging",
68
- seiRpcUrl: "https://evm-rpc-testnet.sei-apis.com",
69
- });
70
-
71
- // Mainnet configuration
72
- const mainnetSdk = new MonacoSDK({
58
+ const sdk = new MonacoSDK({
73
59
  walletClient,
74
60
  network: "mainnet",
75
61
  seiRpcUrl: "https://evm-rpc.sei-apis.com",
@@ -82,19 +68,20 @@ const mainnetSdk = new MonacoSDK({
82
68
  import { MonacoSDK } from "@0xmonaco/core";
83
69
  import { createWalletClient, http } from "viem";
84
70
  import { privateKeyToAccount } from "viem/accounts";
71
+ import { sei } from "viem/chains";
85
72
 
86
73
  // Initialize the SDK with wallet client
87
74
  const account = privateKeyToAccount("0x...");
88
75
  const walletClient = createWalletClient({
89
76
  account,
90
- chain: sei, // or seiTestnet
91
- transport: http("https://evm-rpc.sei-apis.com") // or https://evm-rpc-testnet.sei-apis.com for testnet
77
+ chain: sei,
78
+ transport: http("https://evm-rpc.sei-apis.com")
92
79
  });
93
80
 
94
81
  const monaco = new MonacoSDK({
95
82
  walletClient,
96
- network: "staging", // or "mainnet"
97
- seiRpcUrl: "https://evm-rpc-testnet.sei-apis.com", // or https://evm-rpc.sei-apis.com for mainnet
83
+ network: "mainnet",
84
+ seiRpcUrl: "https://evm-rpc.sei-apis.com", // public RPC; use a dedicated provider in production
98
85
  });
99
86
 
100
87
  // Authentication
@@ -230,7 +217,7 @@ interface SDKConfig {
230
217
  /** Wallet client for signing operations (optional - can be set later via setWalletClient) */
231
218
  walletClient?: WalletClient;
232
219
 
233
- /** Use "staging" for public testnet or "mainnet" for production. */
220
+ /** Use "mainnet" (Sei mainnet, production API). */
234
221
  network: Network;
235
222
 
236
223
  /** RPC URL for Sei blockchain interactions */
@@ -82,6 +82,7 @@ export declare abstract class BaseAPI {
82
82
  *
83
83
  * @param endpoint - The API endpoint to call (should start with /)
84
84
  * @param options - Request options (method, body, headers, etc.)
85
+ * @param resendDeadline - Epoch ms after which a clock-skew 401 is returned instead of re-signed
85
86
  * @returns Promise resolving to the response data
86
87
  * @throws {APIError} When access token is not set or API request fails
87
88
  *
@@ -100,7 +101,25 @@ export declare abstract class BaseAPI {
100
101
  * );
101
102
  * ```
102
103
  */
103
- protected makeAuthenticatedRequest<T>(endpoint: string, options?: RequestInit): Promise<T>;
104
+ protected makeAuthenticatedRequest<T>(endpoint: string, options?: RequestInit, resendDeadline?: number): Promise<T>;
105
+ /**
106
+ * Sends a keyed submission, re-sending the identical body while its outcome
107
+ * is unknown (network failure or a 5xx), for at most `maxRetries` extra
108
+ * attempts with exponential backoff.
109
+ *
110
+ * Only safe for a body that carries an `idempotencyKey`: the server replays
111
+ * the original result for a repeated key instead of executing again. A
112
+ * maintenance `OPERATIONS_BLOCKED` refusal and every 4xx are definitive and
113
+ * returned at once. A `Retry-After` beyond {@link MAX_SUBMISSION_RETRY_DELAY_MS},
114
+ * a resend later than {@link MAX_SUBMISSION_RETRY_WINDOW_MS} after the first
115
+ * send, or a session change during backoff also stops the loop: keys are
116
+ * scoped per account, so a resend signed by another session would be a new order.
117
+ *
118
+ * @throws {APIError} The last failure, naming the key to reuse, when every attempt is ambiguous
119
+ */
120
+ protected makeKeyedSubmission<T>(endpoint: string, options: RequestInit & {
121
+ body: string;
122
+ }): Promise<T>;
104
123
  /**
105
124
  * Compute the per-request ed25519 signature headers.
106
125
  *
package/dist/api/base.js CHANGED
@@ -37,6 +37,56 @@ import { CLOCK_CORRECTION_THRESHOLD_MS, ServerClock } from "../utils/server-cloc
37
37
  * only takes effect in server runtimes (Node/undici, Bun, Deno).
38
38
  */
39
39
  const SDK_USER_AGENT = "@0xmonaco/core";
40
+ /** Longest server-requested wait a keyed submission retry will honour. */
41
+ const MAX_SUBMISSION_RETRY_DELAY_MS = 5000;
42
+ /**
43
+ * Keyed submission retries stop this long after the first send, far inside
44
+ * the server's 15-minute key window even if a suspended tab stretches a wait.
45
+ */
46
+ const MAX_SUBMISSION_RETRY_WINDOW_MS = 60_000;
47
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
48
+ /**
49
+ * True when a submission may or may not have been applied: no response came
50
+ * back at all, a 2xx body could not be read, or the server answered 5xx.
51
+ * Maintenance (`OPERATIONS_BLOCKED`) is a deliberate refusal before the book,
52
+ * not an unknown outcome.
53
+ */
54
+ function isUnknownSubmissionOutcome(error) {
55
+ if (!(error instanceof APIError))
56
+ return false;
57
+ if (error.statusCode === undefined)
58
+ return error.cause !== undefined;
59
+ if (error.statusCode >= StatusCodes.OK && error.statusCode < StatusCodes.MULTIPLE_CHOICES)
60
+ return error.cause !== undefined;
61
+ if (error.statusCode < StatusCodes.INTERNAL_SERVER_ERROR)
62
+ return false;
63
+ const body = error.responseBody;
64
+ return !(body && typeof body === "object" && body.error === "OPERATIONS_BLOCKED");
65
+ }
66
+ function hasIdempotencyKey(body) {
67
+ try {
68
+ const key = JSON.parse(body)?.idempotencyKey;
69
+ return typeof key === "string" && key.length > 0;
70
+ }
71
+ catch {
72
+ return false;
73
+ }
74
+ }
75
+ function unresolvedSubmissionError(last, attempts, reason, cause = last) {
76
+ const key = last.requestBody?.idempotencyKey;
77
+ const stopped = reason ? ` (stopped: ${reason})` : "";
78
+ return new APIError(`Submission outcome unknown after ${attempts} attempts${stopped}: ${last.message}. Retry with idempotencyKey ${String(key)} and the same payload within 15 minutes of the first attempt, under the same account; a new key can execute again.`, {
79
+ endpoint: last.endpoint,
80
+ // A 2xx whose body never arrived is no answer at all: report no status
81
+ // so callers branching on it treat the outcome as unknown.
82
+ statusCode: last.statusCode !== undefined && last.statusCode < StatusCodes.MULTIPLE_CHOICES ? undefined : last.statusCode,
83
+ responseBody: last.responseBody,
84
+ cause,
85
+ requestBody: last.requestBody,
86
+ requestId: last.requestId,
87
+ retryAfter: last.retryAfter,
88
+ });
89
+ }
40
90
  export class BaseAPI {
41
91
  apiUrl;
42
92
  /** Active session keypair (raw bytes) used to sign authenticated requests. */
@@ -260,6 +310,7 @@ export class BaseAPI {
260
310
  *
261
311
  * @param endpoint - The API endpoint to call (should start with /)
262
312
  * @param options - Request options (method, body, headers, etc.)
313
+ * @param resendDeadline - Epoch ms after which a clock-skew 401 is returned instead of re-signed
263
314
  * @returns Promise resolving to the response data
264
315
  * @throws {APIError} When access token is not set or API request fails
265
316
  *
@@ -278,7 +329,7 @@ export class BaseAPI {
278
329
  * );
279
330
  * ```
280
331
  */
281
- async makeAuthenticatedRequest(endpoint, options = {}) {
332
+ async makeAuthenticatedRequest(endpoint, options = {}, resendDeadline) {
282
333
  if (!this.sessionKeypair) {
283
334
  throw new APIError("Session keypair not set. Authenticate (login) first.", {
284
335
  endpoint: `${this.apiUrl}${endpoint}`,
@@ -305,19 +356,80 @@ export class BaseAPI {
305
356
  },
306
357
  }, requestBody);
307
358
  // A 401 whose response moved the server clock was most likely a
308
- // timestamp-skew rejection: re-sign once with the corrected time.
359
+ // timestamp-skew rejection: re-sign once with the corrected time, but
360
+ // only under the session that signed the rejected request and before the
361
+ // caller's resend deadline.
309
362
  const offsetBefore = this.serverClock.offsetMs;
363
+ const signer = this.sessionKeypair;
310
364
  try {
311
365
  return await send();
312
366
  }
313
367
  catch (error) {
314
368
  const clockMoved = Math.abs(this.serverClock.offsetMs - offsetBefore) >= CLOCK_CORRECTION_THRESHOLD_MS;
315
- if (error instanceof APIError && error.statusCode === StatusCodes.UNAUTHORIZED && clockMoved) {
369
+ if (error instanceof APIError &&
370
+ error.statusCode === StatusCodes.UNAUTHORIZED &&
371
+ clockMoved &&
372
+ this.sessionKeypair === signer &&
373
+ (resendDeadline === undefined || Date.now() <= resendDeadline)) {
316
374
  return send();
317
375
  }
318
376
  throw error;
319
377
  }
320
378
  }
379
+ /**
380
+ * Sends a keyed submission, re-sending the identical body while its outcome
381
+ * is unknown (network failure or a 5xx), for at most `maxRetries` extra
382
+ * attempts with exponential backoff.
383
+ *
384
+ * Only safe for a body that carries an `idempotencyKey`: the server replays
385
+ * the original result for a repeated key instead of executing again. A
386
+ * maintenance `OPERATIONS_BLOCKED` refusal and every 4xx are definitive and
387
+ * returned at once. A `Retry-After` beyond {@link MAX_SUBMISSION_RETRY_DELAY_MS},
388
+ * a resend later than {@link MAX_SUBMISSION_RETRY_WINDOW_MS} after the first
389
+ * send, or a session change during backoff also stops the loop: keys are
390
+ * scoped per account, so a resend signed by another session would be a new order.
391
+ *
392
+ * @throws {APIError} The last failure, naming the key to reuse, when every attempt is ambiguous
393
+ */
394
+ async makeKeyedSubmission(endpoint, options) {
395
+ if (!hasIdempotencyKey(options.body))
396
+ throw new Error("Keyed submission requires a JSON body with an idempotencyKey");
397
+ const { maxRetries, baseDelayMs } = this.retryOptions;
398
+ const session = this.sessionKeypair;
399
+ const startedAt = Date.now();
400
+ let unknownOutcome;
401
+ for (let attempt = 0;; attempt++) {
402
+ try {
403
+ return await this.makeAuthenticatedRequest(endpoint, options, startedAt + MAX_SUBMISSION_RETRY_WINDOW_MS);
404
+ }
405
+ catch (error) {
406
+ const attempts = attempt + 1;
407
+ if (!isUnknownSubmissionOutcome(error)) {
408
+ // A refusal of a resend says nothing about the earlier attempt,
409
+ // which may have executed: report that outcome as still unknown.
410
+ if (!unknownOutcome)
411
+ throw error;
412
+ const refusal = error instanceof Error ? error : new Error(String(error));
413
+ throw unresolvedSubmissionError(unknownOutcome, attempts, `retry refused: ${refusal.message}`, refusal);
414
+ }
415
+ unknownOutcome = error;
416
+ const retryAfterMs = (error.retryAfter ?? 0) * 1000;
417
+ if (attempt >= maxRetries || retryAfterMs > MAX_SUBMISSION_RETRY_DELAY_MS) {
418
+ throw unresolvedSubmissionError(error, attempts);
419
+ }
420
+ const delayMs = Math.max(baseDelayMs * 2 ** attempt, retryAfterMs);
421
+ if (Date.now() + delayMs - startedAt > MAX_SUBMISSION_RETRY_WINDOW_MS) {
422
+ throw unresolvedSubmissionError(error, attempts, "retry window passed");
423
+ }
424
+ await sleep(delayMs);
425
+ if (Date.now() - startedAt > MAX_SUBMISSION_RETRY_WINDOW_MS) {
426
+ throw unresolvedSubmissionError(error, attempts, "retry window passed");
427
+ }
428
+ if (this.sessionKeypair !== session)
429
+ throw unresolvedSubmissionError(error, attempts, "session changed");
430
+ }
431
+ }
432
+ }
321
433
  /**
322
434
  * Compute the per-request ed25519 signature headers.
323
435
  *
@@ -61,7 +61,8 @@ export declare class BuildercodesAPIImpl extends BaseAPI implements Buildercodes
61
61
  * Read the authenticated wallet's available BuilderCodes reward balances.
62
62
  *
63
63
  * Current enrollment is not required: a former payout wallet can still see
64
- * and claim the balance it accrued before an address rotation.
64
+ * and claim whatever remains in its bucket. Shortly after a payout-address
65
+ * change the old wallet's unclaimed balance moves to the new wallet.
65
66
  */
66
67
  getRewardsBalance(): Promise<GetBuildercodeRewardsBalanceResponse>;
67
68
  /**
@@ -173,7 +173,8 @@ export class BuildercodesAPIImpl extends BaseAPI {
173
173
  * Read the authenticated wallet's available BuilderCodes reward balances.
174
174
  *
175
175
  * Current enrollment is not required: a former payout wallet can still see
176
- * and claim the balance it accrued before an address rotation.
176
+ * and claim whatever remains in its bucket. Shortly after a payout-address
177
+ * change the old wallet's unclaimed balance moves to the new wallet.
177
178
  */
178
179
  async getRewardsBalance() {
179
180
  return this.makeAuthenticatedRequest("/api/v1/buildercodes/rewards/balance", { method: "GET" });
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Compliance API Implementation
3
+ *
4
+ * Reports whether Monaco is available in the caller's region. Every other
5
+ * endpoint refuses callers from a restricted region with HTTP 451
6
+ * `REGION_RESTRICTED`; this one always answers. Public — no authentication
7
+ * required.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * const { restricted } = await sdk.compliance.getRegionStatus();
12
+ * if (restricted) showRegionNotice();
13
+ * ```
14
+ */
15
+ import type { ComplianceAPI, GetRegionStatusResponse } from "@0xmonaco/types";
16
+ import { BaseAPI } from "../base";
17
+ export declare class ComplianceAPIImpl extends BaseAPI implements ComplianceAPI {
18
+ /** Get the caller's region status (public, no auth headers). */
19
+ getRegionStatus(): Promise<GetRegionStatusResponse>;
20
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Compliance API Implementation
3
+ *
4
+ * Reports whether Monaco is available in the caller's region. Every other
5
+ * endpoint refuses callers from a restricted region with HTTP 451
6
+ * `REGION_RESTRICTED`; this one always answers. Public — no authentication
7
+ * required.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * const { restricted } = await sdk.compliance.getRegionStatus();
12
+ * if (restricted) showRegionNotice();
13
+ * ```
14
+ */
15
+ import { BaseAPI } from "../base";
16
+ export class ComplianceAPIImpl extends BaseAPI {
17
+ /** Get the caller's region status (public, no auth headers). */
18
+ async getRegionStatus() {
19
+ return await this.makePublicRequest("/api/v1/compliance/region", {
20
+ method: "GET",
21
+ });
22
+ }
23
+ }
@@ -0,0 +1 @@
1
+ export { ComplianceAPIImpl } from "./api";
@@ -0,0 +1 @@
1
+ export { ComplianceAPIImpl } from "./api";
@@ -6,6 +6,7 @@
6
6
  export * from "./applications/index";
7
7
  export * from "./base";
8
8
  export * from "./buildercodes";
9
+ export * from "./compliance";
9
10
  export * from "./copy-trading";
10
11
  export * from "./delegated-agents";
11
12
  export * from "./faucet";
package/dist/api/index.js CHANGED
@@ -6,6 +6,7 @@
6
6
  export * from "./applications/index";
7
7
  export * from "./base";
8
8
  export * from "./buildercodes";
9
+ export * from "./compliance";
9
10
  export * from "./copy-trading";
10
11
  export * from "./delegated-agents";
11
12
  export * from "./faucet";
@@ -5,8 +5,12 @@ export declare class MarginAccountsAPIImpl extends BaseAPI implements MarginAcco
5
5
  closeMarginSubAccount(marginAccountId: string): Promise<CloseMarginSubAccountResponse>;
6
6
  getMarginAccountPnl(marginAccountId: string): Promise<MarginAccountPnl>;
7
7
  transferMarginCollateral(request: TransferMarginCollateralRequest): Promise<TransferMarginCollateralResponse>;
8
- transferCollateralToSubAccount(marginAccountId: string, amount: string): Promise<TransferMarginCollateralResponse>;
9
- transferCollateralFromSubAccount(marginAccountId: string, amount: string): Promise<TransferMarginCollateralResponse>;
8
+ transferCollateralToSubAccount(marginAccountId: string, amount: string, options?: {
9
+ idempotencyKey?: string;
10
+ }): Promise<TransferMarginCollateralResponse>;
11
+ transferCollateralFromSubAccount(marginAccountId: string, amount: string, options?: {
12
+ idempotencyKey?: string;
13
+ }): Promise<TransferMarginCollateralResponse>;
10
14
  listMarginAccounts(params?: ListMarginAccountsParams): Promise<ListMarginAccountsResponse>;
11
15
  getMarginAccountSummary(marginAccountId: string, params?: GetMarginAccountSummaryParams): Promise<MarginAccountSummary>;
12
16
  getParentMarginAccountSummary(params?: GetMarginAccountSummaryParams): Promise<MarginAccountSummary>;
@@ -29,21 +29,24 @@ export class MarginAccountsAPIImpl extends BaseAPI {
29
29
  amount: request.amount,
30
30
  from: request.from,
31
31
  to: request.to,
32
+ idempotencyKey: request.idempotencyKey,
32
33
  }),
33
34
  });
34
35
  }
35
- async transferCollateralToSubAccount(marginAccountId, amount) {
36
- validate(SubAccountCollateralTransferSchema, { marginAccountId, amount });
36
+ async transferCollateralToSubAccount(marginAccountId, amount, options) {
37
+ const idempotencyKey = options?.idempotencyKey;
38
+ validate(SubAccountCollateralTransferSchema, { marginAccountId, amount, idempotencyKey });
37
39
  return await this.makeAuthenticatedRequest(perpRoutes.marginAccounts.subAccountTransferIn(marginAccountId), {
38
40
  method: "POST",
39
- body: JSON.stringify({ amount }),
41
+ body: JSON.stringify({ amount, idempotencyKey }),
40
42
  });
41
43
  }
42
- async transferCollateralFromSubAccount(marginAccountId, amount) {
43
- validate(SubAccountCollateralTransferSchema, { marginAccountId, amount });
44
+ async transferCollateralFromSubAccount(marginAccountId, amount, options) {
45
+ const idempotencyKey = options?.idempotencyKey;
46
+ validate(SubAccountCollateralTransferSchema, { marginAccountId, amount, idempotencyKey });
44
47
  return await this.makeAuthenticatedRequest(perpRoutes.marginAccounts.subAccountTransferOut(marginAccountId), {
45
48
  method: "POST",
46
- body: JSON.stringify({ amount }),
49
+ body: JSON.stringify({ amount, idempotencyKey }),
47
50
  });
48
51
  }
49
52
  async listMarginAccounts(params) {
@@ -90,6 +93,7 @@ export class MarginAccountsAPIImpl extends BaseAPI {
90
93
  amount: request.amount,
91
94
  tradingPairId: request.tradingPairId,
92
95
  strategyKey: request.strategyKey,
96
+ idempotencyKey: request.idempotencyKey,
93
97
  }),
94
98
  });
95
99
  }
@@ -100,6 +104,7 @@ export class MarginAccountsAPIImpl extends BaseAPI {
100
104
  body: JSON.stringify({
101
105
  asset: request.asset,
102
106
  amount: request.amount,
107
+ idempotencyKey: request.idempotencyKey,
103
108
  }),
104
109
  });
105
110
  }
@@ -121,6 +126,7 @@ export class MarginAccountsAPIImpl extends BaseAPI {
121
126
  // the wire or the server applies its Adjust Margin default and every
122
127
  // funding-loop top-up converts into initial margin requirement.
123
128
  applyToPositionMargin: request.applyToPositionMargin,
129
+ idempotencyKey: request.idempotencyKey,
124
130
  }),
125
131
  });
126
132
  }
@@ -134,6 +140,7 @@ export class MarginAccountsAPIImpl extends BaseAPI {
134
140
  tradingPairId: request.tradingPairId,
135
141
  strategyKey: request.strategyKey,
136
142
  marginMode: request.marginMode,
143
+ idempotencyKey: request.idempotencyKey,
137
144
  }),
138
145
  });
139
146
  }
@@ -144,6 +151,7 @@ export class MarginAccountsAPIImpl extends BaseAPI {
144
151
  body: JSON.stringify({
145
152
  asset: request.asset,
146
153
  amount: request.amount,
154
+ idempotencyKey: request.idempotencyKey,
147
155
  }),
148
156
  });
149
157
  }
@@ -24,6 +24,12 @@
24
24
  import type { BatchCancelOrdersResponse, BatchCreateOrderParams, BatchCreateOrdersResponse, BatchReplaceOrderParams, BatchReplaceOrdersResponse, CancelConditionalOrderResponse, CancelOrderResponse, CancelTwapOrderResponse, ConditionalOrder, CreateOrderResponse, CreateTwapOrderOptions, CreateTwapOrderResponse, GetOrderResponse, GetPaginatedOrdersParams, GetPaginatedOrdersResponse, GetTwapOrderTcaResponse, ListConditionalOrdersParams, ListConditionalOrdersResponse, ListTwapOrderSlicesParams, ListTwapOrderSlicesResponse, ListTwapOrdersParams, ListTwapOrdersResponse, MarginMode, OrderSide, ParentTpSlLegParams, ParentTrailingStopLegParams, PositionSide, ReplaceOrderResponse, SelfTradePreventionMode, TimeInForce, TradingAPI, TradingMode, TwapOrder } from "@0xmonaco/types";
25
25
  import { BaseAPI } from "../base";
26
26
  export declare class TradingAPIImpl extends BaseAPI implements TradingAPI {
27
+ /**
28
+ * Sends a single create. A key the SDK generated for this call is resent
29
+ * after a lost response; a caller-supplied key is sent once, because it may
30
+ * be a recovery of an earlier submission whose 15-minute window is nearly over.
31
+ */
32
+ private submitCreate;
27
33
  /**
28
34
  * Places a limit order on the order book.
29
35
  *
@@ -90,7 +96,7 @@ export declare class TradingAPIImpl extends BaseAPI implements TradingAPI {
90
96
  postOnly?: boolean;
91
97
  selfTradePreventionMode?: SelfTradePreventionMode;
92
98
  clientOrderId?: string;
93
- /** Generated once per call when omitted. Supply the same key to retry a logical order within 15 minutes. */
99
+ /** Generated once per call when omitted, and then re-sent inside the call after a lost response. A supplied key is sent once: reuse it with the same order to recover across calls within 15 minutes. */
94
100
  idempotencyKey?: string;
95
101
  takeProfit?: ParentTpSlLegParams;
96
102
  stopLoss?: ParentTpSlLegParams;
@@ -157,7 +163,7 @@ export declare class TradingAPIImpl extends BaseAPI implements TradingAPI {
157
163
  /** Trailing stop that arms when this order fully fills; margin entry orders only. */
158
164
  trailingStop?: ParentTrailingStopLegParams;
159
165
  clientOrderId?: string;
160
- /** Generated once per call when omitted. Supply the same key to retry a logical order within 15 minutes. */
166
+ /** Generated once per call when omitted, and then re-sent inside the call after a lost response. A supplied key is sent once: reuse it with the same order to recover across calls within 15 minutes. */
161
167
  idempotencyKey?: string;
162
168
  }): Promise<CreateOrderResponse>;
163
169
  /**
@@ -55,6 +55,17 @@ function parentTrailingStopLegToRequest(leg) {
55
55
  };
56
56
  }
57
57
  export class TradingAPIImpl extends BaseAPI {
58
+ /**
59
+ * Sends a single create. A key the SDK generated for this call is resent
60
+ * after a lost response; a caller-supplied key is sent once, because it may
61
+ * be a recovery of an earlier submission whose 15-minute window is nearly over.
62
+ */
63
+ submitCreate(requestBody, keyGenerated) {
64
+ const request = { method: "POST", body: JSON.stringify(requestBody) };
65
+ return keyGenerated
66
+ ? this.makeKeyedSubmission(perpRoutes.orders.create(), request)
67
+ : this.makeAuthenticatedRequest(perpRoutes.orders.create(), request);
68
+ }
58
69
  /**
59
70
  * Places a limit order on the order book.
60
71
  *
@@ -140,10 +151,7 @@ export class TradingAPIImpl extends BaseAPI {
140
151
  stopLoss: parentTpSlLegToRequest(options?.stopLoss),
141
152
  trailingStop: parentTrailingStopLegToRequest(options?.trailingStop),
142
153
  };
143
- return await this.makeAuthenticatedRequest(perpRoutes.orders.create(), {
144
- method: "POST",
145
- body: JSON.stringify(requestBody),
146
- });
154
+ return await this.submitCreate(requestBody, options?.idempotencyKey === undefined);
147
155
  }
148
156
  /**
149
157
  * Places a market order for immediate execution.
@@ -219,10 +227,7 @@ export class TradingAPIImpl extends BaseAPI {
219
227
  clientOrderId: options?.clientOrderId,
220
228
  idempotencyKey: options?.idempotencyKey === undefined ? crypto.randomUUID() : options.idempotencyKey,
221
229
  };
222
- return await this.makeAuthenticatedRequest(perpRoutes.orders.create(), {
223
- method: "POST",
224
- body: JSON.stringify(requestBody),
225
- });
230
+ return await this.submitCreate(requestBody, options?.idempotencyKey === undefined);
226
231
  }
227
232
  /**
228
233
  * Cancels an existing order.
@@ -1301,7 +1301,7 @@ export function createMonacoWebSocket(baseUrl, options = {}, serverClock = new S
1301
1301
  if (msg.code === "AUTH_FAILED") {
1302
1302
  lastAuthError = { code: msg.code, message: msg.message, session: pendingAuthSessions.shift() };
1303
1303
  }
1304
- else if (msg.code === "ACCOUNT_DEACTIVATED") {
1304
+ else if (msg.code === "ACCOUNT_DEACTIVATED" || msg.code === "ACCOUNT_RESTRICTED") {
1305
1305
  lastAuthError = { code: msg.code, message: msg.message, session: pendingAuthSessions.shift() ?? confirmedSession };
1306
1306
  }
1307
1307
  else if (msg.code === "SESSION_INVALID") {
@@ -1489,7 +1489,7 @@ export function createMonacoWebSocket(baseUrl, options = {}, serverClock = new S
1489
1489
  const authLoss = !skewRetry &&
1490
1490
  authError !== null &&
1491
1491
  authError.session !== undefined &&
1492
- (authError.code === "AUTH_FAILED" || authError.code === "ACCOUNT_DEACTIVATED" || event.code === 1008);
1492
+ (authError.code === "AUTH_FAILED" || authError.code === "ACCOUNT_DEACTIVATED" || authError.code === "ACCOUNT_RESTRICTED" || event.code === 1008);
1493
1493
  const terminalAuthLoss = authLoss && session === authError.session;
1494
1494
  if (terminalAuthLoss) {
1495
1495
  session = undefined;
@@ -57,6 +57,7 @@ export declare const COVERED: {
57
57
  get_market_stats: string;
58
58
  get_my_fee_tier: string;
59
59
  get_pulse: string;
60
+ get_region_status: string;
60
61
  list_managed_market_assignments: string;
61
62
  claim_managed_market_lease: string;
62
63
  refresh_managed_market_quotes: string;
package/dist/coverage.js CHANGED
@@ -57,6 +57,7 @@ export const COVERED = {
57
57
  get_market_stats: "getMarketStats",
58
58
  get_my_fee_tier: "getMyFeeTier",
59
59
  get_pulse: "pulse.getPulse",
60
+ get_region_status: "compliance.getRegionStatus",
60
61
  list_managed_market_assignments: "managedMarkets.listAssignments",
61
62
  claim_managed_market_lease: "managedMarkets.claimLease",
62
63
  refresh_managed_market_quotes: "managedMarkets.refreshQuotes",
package/dist/sdk.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ApplicationsAPI, AuthAPI, AuthState, BuildercodesAPI, CopyTradingAPI, DelegatedAgentsAPI, FaucetAPI, FeesAPI, ManagedMarketsAPI, MarginAccountsAPI, MarketAPI, MonacoSDK, Network, PortfolioAPI, PositionsAPI, ProfileAPI, PulseAPI, SDKConfig, SubAccountsAPI, SweeperAPI, TraderCodesAPI, TradingAPI, VaultAPI, WhitelistAPI, WithdrawalsAPI } from "@0xmonaco/types";
1
+ import type { ApplicationsAPI, AuthAPI, AuthState, BuildercodesAPI, ComplianceAPI, CopyTradingAPI, DelegatedAgentsAPI, FaucetAPI, FeesAPI, ManagedMarketsAPI, MarginAccountsAPI, MarketAPI, MonacoSDK, Network, PortfolioAPI, PositionsAPI, ProfileAPI, PulseAPI, SDKConfig, SubAccountsAPI, SweeperAPI, TraderCodesAPI, TradingAPI, VaultAPI, WhitelistAPI, WithdrawalsAPI } from "@0xmonaco/types";
2
2
  import { type Address, type PublicClient, type TransactionReceipt, type WalletClient } from "viem";
3
3
  import { type MonacoWebSocket, OrderbookAPIImpl, TradesAPIImpl } from "./api";
4
4
  export declare class MonacoSDKImpl implements MonacoSDK {
@@ -23,6 +23,7 @@ export declare class MonacoSDKImpl implements MonacoSDK {
23
23
  whitelist: WhitelistAPI;
24
24
  profile: ProfileAPI;
25
25
  pulse: PulseAPI;
26
+ compliance: ComplianceAPI;
26
27
  orderbook: OrderbookAPIImpl;
27
28
  trades: TradesAPIImpl;
28
29
  ws: MonacoWebSocket;
package/dist/sdk.js CHANGED
@@ -4,6 +4,7 @@ import { sei, seiTestnet } from "viem/chains";
4
4
  import { ApplicationsAPIImpl, createMonacoWebSocket, OrderbookAPIImpl, TradesAPIImpl } from "./api";
5
5
  import { AuthAPIImpl } from "./api/auth";
6
6
  import { BuildercodesAPIImpl } from "./api/buildercodes";
7
+ import { ComplianceAPIImpl } from "./api/compliance";
7
8
  import { CopyTradingAPIImpl } from "./api/copy-trading";
8
9
  import { DelegatedAgentsAPIImpl } from "./api/delegated-agents";
9
10
  import { FaucetAPIImpl } from "./api/faucet";
@@ -64,6 +65,7 @@ export class MonacoSDKImpl {
64
65
  whitelist;
65
66
  profile;
66
67
  pulse;
68
+ compliance;
67
69
  orderbook;
68
70
  trades;
69
71
  ws;
@@ -97,6 +99,7 @@ export class MonacoSDKImpl {
97
99
  this.managedMarkets.setSessionKeypair(credentials);
98
100
  this.whitelist.setSessionKeypair(credentials);
99
101
  this.pulse.setSessionKeypair(credentials);
102
+ this.compliance.setSessionKeypair(credentials);
100
103
  this.profile.setSessionKeypair(credentials);
101
104
  this.orderbook.setSessionKeypair(credentials);
102
105
  this.trades.setSessionKeypair(credentials);
@@ -168,6 +171,7 @@ export class MonacoSDKImpl {
168
171
  this.managedMarkets = configureSigning(new ManagedMarketsAPIImpl(apiUrl));
169
172
  this.whitelist = configureSigning(new WhitelistAPIImpl(apiUrl));
170
173
  this.pulse = configureSigning(new PulseAPIImpl(apiUrl));
174
+ this.compliance = configureSigning(new ComplianceAPIImpl(apiUrl));
171
175
  this.auth = configureSigning(new AuthAPIImpl(this.walletClient, this.chain, apiUrl));
172
176
  this.delegatedAgents = configureSigning(new DelegatedAgentsAPIImpl(apiUrl));
173
177
  this.fees = configureSigning(new FeesAPIImpl(apiUrl));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0xmonaco/core",
3
- "version": "1.0.90",
3
+ "version": "1.0.92",
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.90",
23
- "@0xmonaco/types": "1.0.90",
22
+ "@0xmonaco/contracts": "1.0.92",
23
+ "@0xmonaco/types": "1.0.92",
24
24
  "@noble/curves": "^1.9.1",
25
25
  "@noble/hashes": "^2.4.0",
26
26
  "http-status-codes": "^2.3.0"