@0xmonaco/core 0.7.7 → 0.7.9

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.
Files changed (77) hide show
  1. package/dist/api/applications/api.d.ts +43 -0
  2. package/dist/api/applications/api.js +54 -0
  3. package/dist/api/applications/index.d.ts +4 -0
  4. package/dist/api/applications/index.js +4 -0
  5. package/dist/api/auth/api.d.ts +206 -0
  6. package/dist/api/auth/api.js +305 -0
  7. package/dist/api/auth/index.d.ts +4 -0
  8. package/dist/api/auth/index.js +4 -0
  9. package/dist/api/base.d.ts +123 -0
  10. package/dist/api/base.js +286 -0
  11. package/dist/api/fees/api.d.ts +72 -0
  12. package/dist/api/fees/api.js +90 -0
  13. package/dist/api/fees/index.d.ts +6 -0
  14. package/dist/api/fees/index.js +6 -0
  15. package/dist/api/index.d.ts +18 -0
  16. package/dist/api/index.js +18 -0
  17. package/dist/api/margin-accounts/api.d.ts +12 -0
  18. package/dist/api/margin-accounts/api.js +69 -0
  19. package/dist/api/margin-accounts/index.d.ts +1 -0
  20. package/dist/api/margin-accounts/index.js +1 -0
  21. package/dist/api/market/api.d.ts +20 -0
  22. package/dist/api/market/api.js +97 -0
  23. package/dist/api/market/index.d.ts +1 -0
  24. package/dist/api/market/index.js +1 -0
  25. package/dist/api/orderbook/api.d.ts +15 -0
  26. package/dist/api/orderbook/api.js +37 -0
  27. package/dist/api/orderbook/index.d.ts +1 -0
  28. package/dist/api/orderbook/index.js +1 -0
  29. package/dist/api/perp/index.d.ts +1 -0
  30. package/dist/api/perp/index.js +1 -0
  31. package/dist/api/perp/routes.d.ts +133 -0
  32. package/dist/api/perp/routes.js +85 -0
  33. package/dist/api/positions/api.d.ts +12 -0
  34. package/dist/api/positions/api.js +86 -0
  35. package/dist/api/positions/index.d.ts +1 -0
  36. package/dist/api/positions/index.js +1 -0
  37. package/dist/api/profile/api.d.ts +191 -0
  38. package/dist/api/profile/api.js +259 -0
  39. package/dist/api/profile/index.d.ts +6 -0
  40. package/dist/api/profile/index.js +6 -0
  41. package/dist/api/trades/api.d.ts +44 -0
  42. package/dist/api/trades/api.js +42 -0
  43. package/dist/api/trades/index.d.ts +1 -0
  44. package/dist/api/trades/index.js +1 -0
  45. package/dist/api/trading/api.d.ts +301 -0
  46. package/dist/api/trading/api.js +497 -0
  47. package/dist/api/trading/index.d.ts +4 -0
  48. package/dist/api/trading/index.js +4 -0
  49. package/dist/api/vault/api.d.ts +261 -0
  50. package/dist/api/vault/api.js +506 -0
  51. package/dist/api/vault/index.d.ts +4 -0
  52. package/dist/api/vault/index.js +4 -0
  53. package/dist/api/websocket/index.d.ts +3 -0
  54. package/dist/api/websocket/index.js +3 -0
  55. package/dist/api/websocket/types.d.ts +41 -0
  56. package/dist/api/websocket/types.js +0 -0
  57. package/dist/api/websocket/utils.d.ts +8 -0
  58. package/dist/api/websocket/utils.js +22 -0
  59. package/dist/api/websocket/websocket.d.ts +5 -0
  60. package/dist/api/websocket/websocket.js +560 -0
  61. package/dist/errors/errors.d.ts +381 -0
  62. package/dist/errors/errors.js +815 -0
  63. package/dist/errors/index.d.ts +1 -0
  64. package/dist/errors/index.js +1 -0
  65. package/dist/index.d.ts +5 -0
  66. package/dist/index.js +5 -0
  67. package/dist/networks/index.d.ts +1 -0
  68. package/dist/networks/index.js +1 -0
  69. package/dist/networks/networks.d.ts +21 -0
  70. package/dist/networks/networks.js +46 -0
  71. package/dist/sdk.d.ts +134 -0
  72. package/dist/sdk.js +294 -0
  73. package/dist/utils/index.d.ts +1 -0
  74. package/dist/utils/index.js +1 -0
  75. package/dist/utils/magnitude.d.ts +26 -0
  76. package/dist/utils/magnitude.js +31 -0
  77. package/package.json +3 -3
@@ -0,0 +1,815 @@
1
+ /**
2
+ * Monaco SDK Error Types
3
+ *
4
+ * Standardized error classes for SDK operations with built-in sensitive data sanitization.
5
+ * All error classes inherit from MonacoCoreError and provide structured error information
6
+ * with automatic suggestions for common failure scenarios.
7
+ *
8
+ * @module errors
9
+ */
10
+ import { StatusCodes } from "http-status-codes";
11
+ /**
12
+ * Sensitive field names that should be redacted in error logs.
13
+ *
14
+ * When errors are serialized (via toJSON()) and sent to external monitoring services
15
+ * like Sentry, Datadog, or CloudWatch, these fields will be replaced with "[REDACTED]"
16
+ * to prevent exposure of sensitive information.
17
+ *
18
+ * Categories:
19
+ * - Authentication & Authorization: tokens, API keys, secrets
20
+ * - User PII: emails, phone numbers, addresses
21
+ * - Security credentials: private keys, mnemonics, signatures
22
+ * - Session data: cookies, session IDs
23
+ *
24
+ * @constant
25
+ */
26
+ const SENSITIVE_FIELDS = [
27
+ // Authentication & Authorization
28
+ "token",
29
+ "accessToken",
30
+ "access_token",
31
+ "refreshToken",
32
+ "refresh_token",
33
+ "authToken",
34
+ "auth_token",
35
+ "bearerToken",
36
+ "bearer_token",
37
+ "jwtToken",
38
+ "jwt_token",
39
+ "apiKey",
40
+ "api_key",
41
+ "apikey",
42
+ "clientSecret",
43
+ "client_secret",
44
+ "clientId",
45
+ "client_id",
46
+ "secret",
47
+ "password",
48
+ "authorization",
49
+ "bearer",
50
+ "auth",
51
+ "credentials",
52
+ "credential",
53
+ // User PII
54
+ "email",
55
+ "phone",
56
+ "phoneNumber",
57
+ "phone_number",
58
+ "ssn",
59
+ "socialSecurity",
60
+ "social_security",
61
+ "address",
62
+ "passport",
63
+ "passportNumber",
64
+ "passport_number",
65
+ "driversLicense",
66
+ "drivers_license",
67
+ // Financial
68
+ "cardNumber",
69
+ "card_number",
70
+ "creditCard",
71
+ "credit_card",
72
+ "cvv",
73
+ "pin",
74
+ // Security & Crypto
75
+ "signature",
76
+ "privateKey",
77
+ "private_key",
78
+ "publicKey",
79
+ "public_key",
80
+ "mnemonic",
81
+ "seed",
82
+ "salt",
83
+ "hash",
84
+ "certificate",
85
+ "cert",
86
+ "fingerprint",
87
+ // Session & Cookies
88
+ "cookie",
89
+ "session",
90
+ "sessionId",
91
+ "session_id",
92
+ "sessionToken",
93
+ "session_token",
94
+ // Network & Device
95
+ "ipAddress",
96
+ "ip_address",
97
+ "deviceId",
98
+ "device_id",
99
+ "userId",
100
+ "user_id",
101
+ "accountId",
102
+ "account_id",
103
+ ];
104
+ /**
105
+ * Sanitize data by redacting sensitive fields and limiting size.
106
+ *
107
+ * Recursively processes data structures to protect sensitive information while
108
+ * preserving debugging utility. Used by error classes when serializing to JSON.
109
+ *
110
+ * **Protections applied:**
111
+ * - Redacts sensitive fields (tokens, passwords, PII, etc.)
112
+ * - Truncates long strings (500 chars for object values, 1000 for standalone)
113
+ * - Limits array size (10 items max, then "... N more items")
114
+ * - Prevents infinite recursion (max depth: 5)
115
+ *
116
+ * **Examples:**
117
+ * ```typescript
118
+ * sanitizeData({ token: "secret", name: "John" })
119
+ * // → { token: "[REDACTED]", name: "John" }
120
+ *
121
+ * sanitizeData(["a".repeat(1000)])
122
+ * // → ["aaa... [truncated]"]
123
+ *
124
+ * sanitizeData({ nested: { deep: { ... } } }, 2)
125
+ * // → { nested: { deep: "[Max depth reached]" } }
126
+ * ```
127
+ *
128
+ * @param data - Data to sanitize (any type)
129
+ * @param maxDepth - Maximum recursion depth to prevent infinite loops (default: 5)
130
+ * @param currentDepth - Internal parameter tracking current recursion level
131
+ * @returns Sanitized data safe for logging to external services
132
+ */
133
+ function sanitizeData(data, maxDepth = 5, currentDepth = 0) {
134
+ // Prevent infinite recursion
135
+ if (currentDepth >= maxDepth) {
136
+ return "[Max depth reached]";
137
+ }
138
+ // Handle null/undefined
139
+ if (data === null || data === undefined) {
140
+ return data;
141
+ }
142
+ // Handle arrays
143
+ if (Array.isArray(data)) {
144
+ // Limit array size to prevent huge logs
145
+ const limit = 10;
146
+ const sanitized = data.slice(0, limit).map((item) => sanitizeData(item, maxDepth, currentDepth + 1));
147
+ if (data.length > limit) {
148
+ sanitized.push(`[... ${data.length - limit} more items]`);
149
+ }
150
+ return sanitized;
151
+ }
152
+ // Handle objects
153
+ if (typeof data === "object") {
154
+ const sanitized = {};
155
+ for (const [key, value] of Object.entries(data)) {
156
+ const lowerKey = key.toLowerCase();
157
+ // Check if field is sensitive
158
+ const isSensitive = SENSITIVE_FIELDS.some((field) => lowerKey.includes(field.toLowerCase()));
159
+ if (isSensitive) {
160
+ sanitized[key] = "[REDACTED]";
161
+ }
162
+ else if (typeof value === "string" && value.length > 500) {
163
+ // Truncate long strings to prevent huge logs
164
+ sanitized[key] = `${value.substring(0, 500)}... [truncated]`;
165
+ }
166
+ else {
167
+ sanitized[key] = sanitizeData(value, maxDepth, currentDepth + 1);
168
+ }
169
+ }
170
+ return sanitized;
171
+ }
172
+ // Handle strings
173
+ if (typeof data === "string") {
174
+ // Truncate long strings
175
+ if (data.length > 1000) {
176
+ return `${data.substring(0, 1000)}... [truncated]`;
177
+ }
178
+ return data;
179
+ }
180
+ // Primitives (numbers, booleans) pass through
181
+ return data;
182
+ }
183
+ /**
184
+ * Monaco Core SDK error codes.
185
+ *
186
+ * Categorized error codes for better error handling and debugging.
187
+ * Each error class uses one of these codes to identify the error type.
188
+ *
189
+ * **Categories:**
190
+ *
191
+ * **Initialization Errors** (Configuration and setup):
192
+ * - `INITIALIZATION_ERROR`: General initialization failure
193
+ * - `INVALID_CONFIG`: Invalid SDK configuration (missing/wrong parameters)
194
+ * - `INVALID_STATE`: Invalid SDK state (operation called at wrong time)
195
+ * - `UNSUPPORTED_NETWORK`: Network not supported by SDK
196
+ *
197
+ * **API Errors** (External communication):
198
+ * - `API_ERROR`: API request failed (HTTP errors, network issues)
199
+ * - `CONTRACT_ERROR`: Smart contract interaction failure
200
+ * - `TRANSACTION_ERROR`: Blockchain transaction failure
201
+ *
202
+ * **Order Errors** (Trading operations):
203
+ * - `ORDER_ERROR`: General order operation failure
204
+ * - `INVALID_ORDER`: Order validation failure (invalid parameters)
205
+ * - `ORDER_NOT_FOUND`: Order does not exist
206
+ * - `INSUFFICIENT_BALANCE`: Insufficient token balance for operation
207
+ *
208
+ * **Event Errors** (WebSocket and subscriptions):
209
+ * - `EVENT_ERROR`: General event handling failure
210
+ * - `SUBSCRIPTION_ERROR`: Event subscription failure
211
+ *
212
+ * @example
213
+ * ```typescript
214
+ * try {
215
+ * await sdk.someOperation();
216
+ * } catch (error) {
217
+ * if (error.code === ERROR_CODES.INVALID_CONFIG) {
218
+ * // Handle configuration error
219
+ * } else if (error.code === ERROR_CODES.API_ERROR) {
220
+ * // Handle API error
221
+ * }
222
+ * }
223
+ * ```
224
+ *
225
+ * @constant
226
+ */
227
+ export const ERROR_CODES = {
228
+ /** Initialization errors */
229
+ INITIALIZATION_ERROR: "INITIALIZATION_ERROR",
230
+ INVALID_CONFIG: "INVALID_CONFIG",
231
+ INVALID_STATE: "INVALID_STATE",
232
+ UNSUPPORTED_NETWORK: "UNSUPPORTED_NETWORK",
233
+ /** API errors */
234
+ API_ERROR: "API_ERROR",
235
+ CONTRACT_ERROR: "CONTRACT_ERROR",
236
+ TRANSACTION_ERROR: "TRANSACTION_ERROR",
237
+ /** Order errors */
238
+ ORDER_ERROR: "ORDER_ERROR",
239
+ INVALID_ORDER: "INVALID_ORDER",
240
+ ORDER_NOT_FOUND: "ORDER_NOT_FOUND",
241
+ INSUFFICIENT_BALANCE: "INSUFFICIENT_BALANCE",
242
+ /** Event errors */
243
+ EVENT_ERROR: "EVENT_ERROR",
244
+ SUBSCRIPTION_ERROR: "SUBSCRIPTION_ERROR",
245
+ };
246
+ /**
247
+ * Base class for all Monaco SDK errors.
248
+ *
249
+ * Provides common error functionality including:
250
+ * - Structured error codes for programmatic handling
251
+ * - Automatic recovery suggestions based on error context
252
+ * - Retry-ability indication for automatic retry logic
253
+ * - Timestamps for error tracking
254
+ * - JSON serialization for logging to external services
255
+ * - Cause chain support for error wrapping
256
+ *
257
+ * All SDK errors inherit from this class and provide specific error details.
258
+ *
259
+ * **Common Properties:**
260
+ * - `code`: MonacoErrorCode - Categorized error identifier
261
+ * - `message`: string - Human-readable error description
262
+ * - `suggestion`: string | undefined - Actionable recovery steps
263
+ * - `retryable`: boolean - Whether the operation can be retried
264
+ * - `timestamp`: number - Unix timestamp when error occurred
265
+ * - `cause`: unknown - Original error that caused this error (if wrapped)
266
+ *
267
+ * @example
268
+ * ```typescript
269
+ * try {
270
+ * await sdk.someOperation();
271
+ * } catch (error) {
272
+ * if (error instanceof MonacoCoreError) {
273
+ * console.log(`Error: ${error.message}`);
274
+ * console.log(`Code: ${error.code}`);
275
+ * console.log(`Suggestion: ${error.suggestion}`);
276
+ * console.log(`Can retry: ${error.retryable}`);
277
+ *
278
+ * // Send to monitoring service
279
+ * logger.error(error.toJSON());
280
+ * }
281
+ * }
282
+ * ```
283
+ *
284
+ * @abstract
285
+ */
286
+ export class MonacoCoreError extends Error {
287
+ cause;
288
+ suggestion;
289
+ retryable = false;
290
+ timestamp;
291
+ constructor(message, options) {
292
+ super(message);
293
+ this.name = this.constructor.name;
294
+ this.timestamp = Date.now();
295
+ if (options?.cause) {
296
+ this.cause = options.cause;
297
+ }
298
+ if (options?.suggestion) {
299
+ this.suggestion = options.suggestion;
300
+ }
301
+ if (options?.retryable !== undefined) {
302
+ this.retryable = options.retryable;
303
+ }
304
+ }
305
+ /**
306
+ * Serialize error to JSON for logging/reporting
307
+ */
308
+ toJSON() {
309
+ return {
310
+ name: this.name,
311
+ code: this.code,
312
+ message: this.message,
313
+ suggestion: this.suggestion,
314
+ retryable: this.retryable,
315
+ timestamp: this.timestamp,
316
+ cause: this.cause instanceof Error ? this.cause.message : this.cause,
317
+ };
318
+ }
319
+ }
320
+ /**
321
+ * Error thrown when SDK is initialized or configured incorrectly.
322
+ *
323
+ * Indicates invalid or missing configuration parameters that prevent
324
+ * the SDK from initializing or operating correctly. Includes automatic
325
+ * suggestions for common configuration fields.
326
+ *
327
+ * **Additional Properties:**
328
+ * - `field`: string | undefined - The configuration field that's invalid
329
+ * - `value`: unknown - The invalid value provided (sanitized in toJSON)
330
+ *
331
+ * **Common Causes:**
332
+ * - Missing required parameters (walletClient, seiRpcUrl)
333
+ * - Invalid URL formats
334
+ * - Unsupported network values
335
+ * - Wallet client chain mismatch
336
+ *
337
+ * @example
338
+ * ```typescript
339
+ * // Missing required field
340
+ * throw new InvalidConfigError(
341
+ * "Wallet client is required",
342
+ * "walletClient"
343
+ * );
344
+ *
345
+ * // Invalid value
346
+ * throw new InvalidConfigError(
347
+ * "Invalid URL format",
348
+ * "seiRpcUrl",
349
+ * "not-a-url"
350
+ * );
351
+ *
352
+ * // With cause
353
+ * try {
354
+ * new URL(config.wsUrl);
355
+ * } catch (error) {
356
+ * throw new InvalidConfigError(
357
+ * "wsUrl must be a valid URL",
358
+ * "wsUrl",
359
+ * config.wsUrl,
360
+ * error
361
+ * );
362
+ * }
363
+ * ```
364
+ */
365
+ export class InvalidConfigError extends MonacoCoreError {
366
+ code = "INVALID_CONFIG";
367
+ field;
368
+ value;
369
+ constructor(message, field, value, cause) {
370
+ const suggestion = field
371
+ ? `Check the '${field}' configuration parameter. ${getSuggestionForConfigField(field)}`
372
+ : "Review your SDK configuration and ensure all required fields are provided correctly.";
373
+ super(message, { cause, suggestion, retryable: false });
374
+ this.field = field;
375
+ this.value = value;
376
+ }
377
+ toJSON() {
378
+ // Sanitize value based on field name - if the field is sensitive, redact the value
379
+ let sanitizedValue;
380
+ if (this.field) {
381
+ const lowerField = this.field.toLowerCase();
382
+ const isSensitive = SENSITIVE_FIELDS.some((sensitive) => lowerField.includes(sensitive.toLowerCase()));
383
+ if (isSensitive) {
384
+ sanitizedValue = "[REDACTED]";
385
+ }
386
+ else {
387
+ // Still sanitize the value in case it's an object with sensitive fields
388
+ sanitizedValue = sanitizeData(this.value);
389
+ }
390
+ }
391
+ else {
392
+ sanitizedValue = sanitizeData(this.value);
393
+ }
394
+ return {
395
+ ...super.toJSON(),
396
+ field: this.field,
397
+ value: sanitizedValue,
398
+ };
399
+ }
400
+ }
401
+ /**
402
+ * Get field-specific suggestions for common configuration parameters.
403
+ *
404
+ * Provides actionable guidance for fixing invalid configuration values.
405
+ * Used by InvalidConfigError to generate context-aware suggestions.
406
+ *
407
+ * **Supported Fields:**
408
+ * - `walletClient`: Wallet creation and account configuration
409
+ * - `seiRpcUrl`: RPC endpoint URLs for testnet/mainnet
410
+ * - `network`: Valid network presets
411
+ * - `*` (default): Generic configuration documentation link
412
+ *
413
+ * @param field - Configuration field name (case-insensitive)
414
+ * @returns Specific suggestion string for the field
415
+ * @internal
416
+ */
417
+ function getSuggestionForConfigField(field) {
418
+ switch (field.toLowerCase()) {
419
+ case "walletclient":
420
+ return "Ensure you're creating the wallet client with a valid account and chain configuration.";
421
+ case "seirpcurl":
422
+ return "Use 'https://evm-rpc-testnet.sei-apis.com' for testnet or 'https://evm-rpc.sei-apis.com' for mainnet.";
423
+ case "wsurl":
424
+ return "Use 'wss://development.apimonaco.xyz/ws' for testnet or 'wss://api.monaco.xyz/ws' for mainnet.";
425
+ case "network":
426
+ return "Valid networks are: 'mainnet', 'development', 'staging', 'local'.";
427
+ default:
428
+ return "Refer to the SDK documentation for correct configuration format.";
429
+ }
430
+ }
431
+ /**
432
+ * Error thrown when an operation is called in an invalid SDK state.
433
+ *
434
+ * Indicates that a method was called at the wrong time or when prerequisites
435
+ * weren't met. Includes state transition information when available.
436
+ *
437
+ * **Additional Properties:**
438
+ * - `currentState`: string | undefined - The current state of the SDK
439
+ * - `expectedState`: string | undefined - The state required for this operation
440
+ *
441
+ * **Common Causes:**
442
+ * - Calling authenticated methods before login
443
+ * - Calling methods after logout/disconnect
444
+ * - Operation sequence violations
445
+ *
446
+ * @example
447
+ * ```typescript
448
+ * // With state information
449
+ * throw new InvalidStateError(
450
+ * "Cannot place orders while disconnected",
451
+ * "disconnected",
452
+ * "connected"
453
+ * );
454
+ * // Suggestion: "Current state is 'disconnected', Expected state is 'connected'. Ensure operations are performed in the correct order."
455
+ *
456
+ * // Without state information
457
+ * throw new InvalidStateError(
458
+ * "Prerequisites not met"
459
+ * );
460
+ * // Suggestion: "Check that prerequisites are met before calling this method."
461
+ * ```
462
+ */
463
+ export class InvalidStateError extends MonacoCoreError {
464
+ code = "INVALID_STATE";
465
+ currentState;
466
+ expectedState;
467
+ constructor(message, currentState, expectedState, cause) {
468
+ const suggestion = expectedState
469
+ ? `${currentState ? `Current state is '${currentState}', ` : ""}Expected state is '${expectedState}'. Ensure operations are performed in the correct order.`
470
+ : "Check that prerequisites are met before calling this method.";
471
+ super(message, { cause, suggestion, retryable: false });
472
+ this.currentState = currentState;
473
+ this.expectedState = expectedState;
474
+ }
475
+ toJSON() {
476
+ return {
477
+ ...super.toJSON(),
478
+ currentState: this.currentState,
479
+ expectedState: this.expectedState,
480
+ };
481
+ }
482
+ }
483
+ /**
484
+ * Error thrown when an API request fails.
485
+ *
486
+ * Represents failures in HTTP/API communication including network errors,
487
+ * server errors, validation failures, and rate limiting. Automatically
488
+ * determines retry-ability and provides context-aware recovery suggestions.
489
+ *
490
+ * **Additional Properties:**
491
+ * - `endpoint`: string | undefined - The API endpoint that failed
492
+ * - `statusCode`: number | undefined - HTTP status code (undefined for network errors)
493
+ * - `responseBody`: unknown - Response body from the API (sanitized in toJSON)
494
+ * - `requestBody`: unknown - Request body sent to the API (sanitized in toJSON)
495
+ * - `requestId`: string | undefined - Request ID from X-Request-ID header
496
+ * - `retryAfter`: number | undefined - Retry-After header value in seconds
497
+ *
498
+ * **Common Status Codes:**
499
+ * - `undefined`: Network error (connection failed, timeout, DNS error)
500
+ * - `400`: Bad request (validation failed, invalid parameters)
501
+ * - `401`: Unauthorized (missing or expired token)
502
+ * - `403`: Forbidden (insufficient permissions)
503
+ * - `404`: Not found (resource doesn't exist)
504
+ * - `429`: Rate limit exceeded
505
+ * - `500+`: Server error
506
+ *
507
+ * **Retry-ability:**
508
+ * - ✅ Retryable: Network errors, 429, 500+, 409
509
+ * - ❌ Not retryable: 400, 401, 403, 404, and most client errors
510
+ *
511
+ * @example
512
+ * ```typescript
513
+ * // Network error (no response)
514
+ * throw new APIError("Network request failed", {
515
+ * endpoint: "/api/orders",
516
+ * cause: new Error("ECONNREFUSED")
517
+ * });
518
+ *
519
+ * // API validation error
520
+ * throw new APIError("Invalid order parameters", {
521
+ * endpoint: "/api/orders",
522
+ * statusCode: 400,
523
+ * responseBody: { error: "Price must be positive" },
524
+ * requestBody: { price: -10 }
525
+ * });
526
+ *
527
+ * // Rate limit with retry-after
528
+ * throw new APIError("Rate limit exceeded", {
529
+ * endpoint: "/api/orders",
530
+ * statusCode: 429,
531
+ * requestId: "req_abc123",
532
+ * retryAfter: 60
533
+ * });
534
+ *
535
+ * // Check if retryable
536
+ * try {
537
+ * await sdk.someOperation();
538
+ * } catch (error) {
539
+ * if (error instanceof APIError && error.retryable) {
540
+ * // Implement retry logic
541
+ * await delay(error.retryAfter || 5);
542
+ * await sdk.someOperation();
543
+ * }
544
+ * }
545
+ * ```
546
+ */
547
+ export class APIError extends MonacoCoreError {
548
+ code = "API_ERROR";
549
+ endpoint;
550
+ statusCode;
551
+ responseBody;
552
+ requestBody;
553
+ requestId;
554
+ retryAfter;
555
+ constructor(message, options) {
556
+ // Determine suggestion and retry-ability based on status code
557
+ const { suggestion, retryable } = getErrorSuggestion(options?.statusCode, message, options?.responseBody);
558
+ super(message, { cause: options?.cause, suggestion, retryable });
559
+ this.endpoint = options?.endpoint;
560
+ this.statusCode = options?.statusCode;
561
+ this.responseBody = options?.responseBody;
562
+ this.requestBody = options?.requestBody;
563
+ this.requestId = options?.requestId;
564
+ this.retryAfter = options?.retryAfter;
565
+ }
566
+ /**
567
+ * Override toJSON to include API-specific fields with sanitization
568
+ *
569
+ * Sanitizes requestBody and responseBody to prevent sensitive data exposure
570
+ * when errors are logged to external monitoring services.
571
+ */
572
+ toJSON() {
573
+ return {
574
+ ...super.toJSON(),
575
+ endpoint: this.endpoint,
576
+ statusCode: this.statusCode,
577
+ responseBody: sanitizeData(this.responseBody),
578
+ requestBody: sanitizeData(this.requestBody),
579
+ requestId: this.requestId,
580
+ retryAfter: this.retryAfter,
581
+ };
582
+ }
583
+ }
584
+ /**
585
+ * Generate helpful error suggestions and determine retry-ability based on HTTP status codes.
586
+ *
587
+ * Analyzes API errors to provide actionable recovery suggestions and indicate whether
588
+ * the operation can be safely retried. Used automatically by APIError constructor.
589
+ *
590
+ * **Status Code Precedence** (checked in this order):
591
+ * 1. No status code (network errors) → retryable
592
+ * 2. 401 Unauthorized → check for token expiration, not retryable
593
+ * 3. 403 Forbidden → permission issue, not retryable
594
+ * 4. 404 Not Found → resource missing, not retryable
595
+ * 5. 409 Conflict → resource conflict, retryable
596
+ * 6. 400 Bad Request → check for specific issues (balance, validation), not retryable
597
+ * 7. 429 Too Many Requests → rate limit, retryable with backoff
598
+ * 8. 500-599 Server Errors → retryable (explicit range check)
599
+ * 9. Default → not retryable
600
+ *
601
+ * **Examples:**
602
+ * ```typescript
603
+ * getErrorSuggestion(401, "Token expired")
604
+ * // → {
605
+ * // suggestion: "Access token has expired. Call sdk.refreshAuth()...",
606
+ * // retryable: false
607
+ * // }
608
+ *
609
+ * getErrorSuggestion(429, "Rate limit", { retry_after: 60 })
610
+ * // → {
611
+ * // suggestion: "Rate limit exceeded. Wait 60 seconds before retrying...",
612
+ * // retryable: true
613
+ * // }
614
+ *
615
+ * getErrorSuggestion(undefined, "Network failed")
616
+ * // → {
617
+ * // suggestion: "Network request failed. Check your internet connection...",
618
+ * // retryable: true
619
+ * // }
620
+ * ```
621
+ *
622
+ * @param statusCode - HTTP status code from the failed request (undefined for network errors)
623
+ * @param message - Error message from the API or network layer
624
+ * @param responseBody - Response body from the API (may contain retry_after, etc.)
625
+ * @returns Object with suggestion string and retryable boolean
626
+ * @internal
627
+ */
628
+ function getErrorSuggestion(statusCode, message, responseBody) {
629
+ // Network/timeout errors - always retryable
630
+ if (!statusCode) {
631
+ return {
632
+ suggestion: "Network request failed. Check your internet connection and try again.",
633
+ retryable: true,
634
+ };
635
+ }
636
+ // Authentication errors (401 Unauthorized)
637
+ if (statusCode === StatusCodes.UNAUTHORIZED) {
638
+ if (message?.toLowerCase().includes("expired")) {
639
+ return {
640
+ suggestion: "Access token has expired. Call sdk.refreshAuth() to get a new token, or call sdk.login() to re-authenticate.",
641
+ retryable: false,
642
+ };
643
+ }
644
+ return {
645
+ suggestion: "Authentication required. Call sdk.login(clientId) to authenticate before making this request.",
646
+ retryable: false,
647
+ };
648
+ }
649
+ // Forbidden (403)
650
+ if (statusCode === StatusCodes.FORBIDDEN) {
651
+ return {
652
+ suggestion: "Access denied. Ensure your application has the required permissions and your client ID is valid.",
653
+ retryable: false,
654
+ };
655
+ }
656
+ // Not found (404)
657
+ if (statusCode === StatusCodes.NOT_FOUND) {
658
+ return {
659
+ suggestion: "Resource not found. Check that the ID or identifier is correct and the resource exists.",
660
+ retryable: false,
661
+ };
662
+ }
663
+ // Conflict (409) - check BEFORE 500+ to avoid being shadowed
664
+ if (statusCode === StatusCodes.CONFLICT) {
665
+ return {
666
+ suggestion: "Resource conflict. The resource may have been modified. Refresh and try again.",
667
+ retryable: true,
668
+ };
669
+ }
670
+ // Validation errors (400 Bad Request)
671
+ if (statusCode === StatusCodes.BAD_REQUEST) {
672
+ if (message?.toLowerCase().includes("insufficient balance")) {
673
+ return {
674
+ suggestion: "Insufficient balance. Deposit more funds using sdk.vault.deposit() or reduce the order size.",
675
+ retryable: false,
676
+ };
677
+ }
678
+ if (message?.toLowerCase().includes("invalid")) {
679
+ return {
680
+ suggestion: "Invalid request parameters. Check the API documentation for correct parameter format and values.",
681
+ retryable: false,
682
+ };
683
+ }
684
+ return {
685
+ suggestion: "Bad request. Verify that all required parameters are provided and correctly formatted.",
686
+ retryable: false,
687
+ };
688
+ }
689
+ // Rate limiting (429 Too Many Requests)
690
+ if (statusCode === StatusCodes.TOO_MANY_REQUESTS) {
691
+ let retryText;
692
+ if (typeof responseBody === "object" && responseBody !== null && "retry_after" in responseBody && responseBody.retry_after) {
693
+ const retryAfterValue = responseBody.retry_after;
694
+ retryText = ` Wait ${String(retryAfterValue)} seconds before retrying.`;
695
+ }
696
+ else {
697
+ retryText = " Wait a moment before retrying.";
698
+ }
699
+ return {
700
+ suggestion: `Rate limit exceeded.${retryText} Consider implementing exponential backoff for retries.`,
701
+ retryable: true,
702
+ };
703
+ }
704
+ // Server errors (500-599) - retryable
705
+ // Use explicit range check to avoid shadowing other status codes
706
+ if (statusCode >= StatusCodes.INTERNAL_SERVER_ERROR && statusCode < 600) {
707
+ return {
708
+ suggestion: "Server error occurred. This is likely a temporary issue. Try again in a few moments.",
709
+ retryable: true,
710
+ };
711
+ }
712
+ // Default - not retryable
713
+ return {
714
+ suggestion: undefined,
715
+ retryable: false,
716
+ };
717
+ }
718
+ /**
719
+ * Error thrown when a smart contract interaction fails.
720
+ *
721
+ * Represents failures in blockchain contract interactions including transaction
722
+ * reverts, gas estimation failures, and contract execution errors. Includes
723
+ * revert reasons and transaction hashes when available for debugging.
724
+ *
725
+ * **Additional Properties:**
726
+ * - `revertReason`: string | undefined - The reason the contract reverted
727
+ * - `transactionHash`: string | undefined - Transaction hash (if transaction was submitted)
728
+ * - `contractAddress`: string | undefined - Address of the contract that failed
729
+ *
730
+ * **Common Causes:**
731
+ * - Contract revert (require/assert failed)
732
+ * - Gas estimation failure (transaction would fail)
733
+ * - Insufficient gas provided
734
+ * - Contract state restrictions (e.g., paused, unauthorized)
735
+ * - Transaction reverted after submission
736
+ *
737
+ * @example
738
+ * ```typescript
739
+ * // Contract revert during simulation
740
+ * throw new ContractError(
741
+ * "Insufficient balance for withdrawal",
742
+ * {
743
+ * revertReason: "Insufficient balance",
744
+ * contractAddress: "0x123...",
745
+ * }
746
+ * );
747
+ *
748
+ * // Transaction reverted on-chain
749
+ * throw new ContractError(
750
+ * "Transaction reverted",
751
+ * {
752
+ * transactionHash: "0xabc...",
753
+ * revertReason: "Slippage tolerance exceeded",
754
+ * contractAddress: "0x456...",
755
+ * }
756
+ * );
757
+ *
758
+ * // Gas estimation failure
759
+ * try {
760
+ * await contract.estimateGas.someMethod(...args);
761
+ * } catch (error) {
762
+ * throw new ContractError(
763
+ * "Gas estimation failed",
764
+ * {
765
+ * revertReason: error.reason,
766
+ * contractAddress: contract.address,
767
+ * cause: error,
768
+ * }
769
+ * );
770
+ * }
771
+ * ```
772
+ */
773
+ export class ContractError extends MonacoCoreError {
774
+ code = "CONTRACT_ERROR";
775
+ revertReason;
776
+ transactionHash;
777
+ contractAddress;
778
+ constructor(message, options) {
779
+ const suggestion = getContractErrorSuggestion(options?.revertReason);
780
+ super(message, { cause: options?.cause, suggestion, retryable: false });
781
+ this.revertReason = options?.revertReason;
782
+ this.transactionHash = options?.transactionHash;
783
+ this.contractAddress = options?.contractAddress;
784
+ }
785
+ toJSON() {
786
+ return {
787
+ ...super.toJSON(),
788
+ revertReason: this.revertReason,
789
+ transactionHash: this.transactionHash,
790
+ contractAddress: this.contractAddress,
791
+ };
792
+ }
793
+ }
794
+ /**
795
+ * Get suggestions for common contract errors
796
+ */
797
+ function getContractErrorSuggestion(revertReason) {
798
+ if (!revertReason) {
799
+ return "Smart contract execution failed. Check transaction parameters and try again.";
800
+ }
801
+ const reason = revertReason.toLowerCase();
802
+ if (reason.includes("insufficient allowance")) {
803
+ return "Token allowance is insufficient. Call sdk.vault.approve() to increase the token allowance for the vault contract.";
804
+ }
805
+ if (reason.includes("insufficient balance")) {
806
+ return "Wallet balance is insufficient for this transaction. Ensure you have enough tokens and native currency for gas fees.";
807
+ }
808
+ if (reason.includes("nonce")) {
809
+ return "Transaction nonce error. This may be due to a pending transaction. Wait for pending transactions to complete or reset your account nonce.";
810
+ }
811
+ if (reason.includes("gas")) {
812
+ return "Transaction ran out of gas. Try increasing the gas limit for this transaction.";
813
+ }
814
+ return undefined;
815
+ }