pmxt-core 2.48.6 → 2.49.0

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 (63) hide show
  1. package/dist/exchanges/gemini-titan/normalizer.js +9 -3
  2. package/dist/exchanges/gemini-titan/websocket.js +17 -5
  3. package/dist/exchanges/hyperliquid/normalizer.js +2 -0
  4. package/dist/exchanges/interfaces.d.ts +2 -2
  5. package/dist/exchanges/kalshi/api.d.ts +1 -1
  6. package/dist/exchanges/kalshi/api.js +2 -2
  7. package/dist/exchanges/kalshi/config.d.ts +6 -6
  8. package/dist/exchanges/kalshi/config.js +8 -8
  9. package/dist/exchanges/kalshi/fetcher.d.ts +2 -0
  10. package/dist/exchanges/kalshi/fetcher.js +6 -7
  11. package/dist/exchanges/kalshi/normalizer.js +6 -1
  12. package/dist/exchanges/kalshi/websocket.js +21 -9
  13. package/dist/exchanges/limitless/api.d.ts +1 -1
  14. package/dist/exchanges/limitless/api.js +1 -1
  15. package/dist/exchanges/limitless/client.js +2 -2
  16. package/dist/exchanges/limitless/config.d.ts +2 -0
  17. package/dist/exchanges/limitless/config.js +3 -1
  18. package/dist/exchanges/limitless/fetcher.d.ts +10 -0
  19. package/dist/exchanges/limitless/fetcher.js +2 -1
  20. package/dist/exchanges/limitless/index.js +1 -1
  21. package/dist/exchanges/limitless/utils.js +3 -3
  22. package/dist/exchanges/limitless/websocket.js +8 -11
  23. package/dist/exchanges/metaculus/utils.js +0 -1
  24. package/dist/exchanges/myriad/api.d.ts +1 -1
  25. package/dist/exchanges/myriad/api.js +17 -2
  26. package/dist/exchanges/myriad/normalizer.js +10 -5
  27. package/dist/exchanges/myriad/utils.js +2 -0
  28. package/dist/exchanges/opinion/api.d.ts +16 -1
  29. package/dist/exchanges/opinion/api.js +20 -1
  30. package/dist/exchanges/opinion/websocket.js +21 -14
  31. package/dist/exchanges/polymarket/api-clob.d.ts +13 -1
  32. package/dist/exchanges/polymarket/api-clob.js +26 -1
  33. package/dist/exchanges/polymarket/api-data.d.ts +1 -1
  34. package/dist/exchanges/polymarket/api-data.js +1 -1
  35. package/dist/exchanges/polymarket/api-gamma.d.ts +1 -1
  36. package/dist/exchanges/polymarket/api-gamma.js +1 -1
  37. package/dist/exchanges/polymarket/auth.js +3 -3
  38. package/dist/exchanges/polymarket/config.d.ts +1 -0
  39. package/dist/exchanges/polymarket/config.js +4 -0
  40. package/dist/exchanges/polymarket/index.js +2 -1
  41. package/dist/exchanges/polymarket_us/websocket.js +3 -2
  42. package/dist/exchanges/probable/api.d.ts +1 -1
  43. package/dist/exchanges/probable/api.js +1 -1
  44. package/dist/exchanges/probable/auth.js +5 -4
  45. package/dist/exchanges/probable/config.d.ts +2 -0
  46. package/dist/exchanges/probable/config.js +5 -0
  47. package/dist/exchanges/probable/websocket.js +2 -1
  48. package/dist/exchanges/suibets/api.d.ts +1 -1
  49. package/dist/exchanges/suibets/api.js +1 -1
  50. package/dist/exchanges/suibets/config.d.ts +1 -1
  51. package/dist/exchanges/suibets/config.js +2 -2
  52. package/dist/exchanges/suibets/errors.js +9 -0
  53. package/dist/exchanges/suibets/fetcher.d.ts +22 -2
  54. package/dist/exchanges/suibets/fetcher.js +23 -5
  55. package/dist/exchanges/suibets/index.d.ts +1 -1
  56. package/dist/exchanges/suibets/index.js +1 -1
  57. package/dist/exchanges/suibets/normalizer.js +2 -22
  58. package/dist/feeds/binance/binance-feed.js +6 -3
  59. package/dist/feeds/chainlink/chainlink-feed.js +5 -2
  60. package/dist/server/openapi.yaml +2 -0
  61. package/dist/types.d.ts +1 -0
  62. package/dist/utils/throttler.js +3 -5
  63. package/package.json +4 -4
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.opinionApiSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/opinion/opinion-openapi.yaml
6
- * Generated at: 2026-06-04T11:40:47.305Z
6
+ * Generated at: 2026-06-08T19:06:11.875Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.opinionApiSpec = {
@@ -150,6 +150,25 @@ exports.opinionApiSpec = {
150
150
  ]
151
151
  }
152
152
  },
153
+ "/market/slug/{slug}": {
154
+ "get": {
155
+ "tags": [
156
+ "Market"
157
+ ],
158
+ "summary": "Get market detail by slug",
159
+ "operationId": "getMarketDetailBySlug",
160
+ "parameters": [
161
+ {
162
+ "name": "slug",
163
+ "in": "path",
164
+ "required": true,
165
+ "schema": {
166
+ "type": "string"
167
+ }
168
+ }
169
+ ]
170
+ }
171
+ },
153
172
  "/market/categorical/{marketId}": {
154
173
  "get": {
155
174
  "tags": [
@@ -200,10 +200,7 @@ class OpinionWebSocket {
200
200
  if (isNaN(price)) {
201
201
  return;
202
202
  }
203
- const existing = this.orderBooks.get(marketId);
204
- const book = existing
205
- ? { ...existing }
206
- : { bids: [], asks: [], timestamp: Date.now() };
203
+ const book = this.orderBooks.get(marketId) ?? { bids: [], asks: [], timestamp: Date.now() };
207
204
  if (side === "bids") {
208
205
  book.bids = applyLevelUpdate(book.bids, price, size, "desc");
209
206
  }
@@ -280,8 +277,11 @@ class OpinionWebSocket {
280
277
  this.subscribedDepthMarketIds.add(marketId);
281
278
  }
282
279
  if (!this.isConnected) {
283
- this.connect().catch(() => {
284
- // Swallow scheduleReconnect will retry. Resolvers stay pending.
280
+ this.connect().catch((err) => {
281
+ logger_1.logger.warn("Opinion WebSocket connect failed during watchOrderBook", { error: String(err) });
282
+ if (!this.isTerminated) {
283
+ this.scheduleReconnect();
284
+ }
285
285
  });
286
286
  }
287
287
  else {
@@ -310,8 +310,11 @@ class OpinionWebSocket {
310
310
  this.subscribedTradeMarketIds.add(marketId);
311
311
  }
312
312
  if (!this.isConnected) {
313
- this.connect().catch(() => {
314
- // Swallow scheduleReconnect will retry. Resolvers stay pending.
313
+ this.connect().catch((err) => {
314
+ logger_1.logger.warn("Opinion WebSocket connect failed during watchTrades", { error: String(err) });
315
+ if (!this.isTerminated) {
316
+ this.scheduleReconnect();
317
+ }
315
318
  });
316
319
  }
317
320
  else {
@@ -378,20 +381,24 @@ exports.OpinionWebSocket = OpinionWebSocket;
378
381
  // ---------------------------------------------------------------------------
379
382
  /**
380
383
  * Apply an absolute-size level update to one side of the book.
381
- * A size of 0 (or NaN) removes the level. Returns a new sorted array.
384
+ * A size of 0 (or NaN) removes the level. Mutates the provided array in place
385
+ * and keeps it sorted.
382
386
  */
383
387
  function applyLevelUpdate(levels, price, size, sortOrder) {
384
- const updated = levels.filter((l) => Math.abs(l.price - price) >= 0.0001);
388
+ const idx = levels.findIndex((l) => l.price === price);
389
+ if (idx !== -1) {
390
+ levels.splice(idx, 1);
391
+ }
385
392
  if (!isNaN(size) && size > 0) {
386
- updated.push({ price, size });
393
+ levels.push({ price, size });
387
394
  }
388
395
  if (sortOrder === "desc") {
389
- updated.sort((a, b) => b.price - a.price);
396
+ levels.sort((a, b) => b.price - a.price);
390
397
  }
391
398
  else {
392
- updated.sort((a, b) => a.price - b.price);
399
+ levels.sort((a, b) => a.price - b.price);
393
400
  }
394
- return updated;
401
+ return levels;
395
402
  }
396
403
  /**
397
404
  * Map Opinion's trade side string to our unified type.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/PolymarketClobAPI.yaml
3
- * Generated at: 2026-06-04T11:40:47.258Z
3
+ * Generated at: 2026-06-08T19:06:11.828Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const polymarketClobSpec: {
@@ -243,6 +243,18 @@ export declare const polymarketClobSpec: {
243
243
  }[];
244
244
  };
245
245
  };
246
+ "/v1/heartbeats": {
247
+ post: {
248
+ summary: string;
249
+ tags: string[];
250
+ security: {
251
+ L2Auth: never[];
252
+ }[];
253
+ parameters: {
254
+ $ref: string;
255
+ }[];
256
+ };
257
+ };
246
258
  "/data/order/{id}": {
247
259
  get: {
248
260
  summary: string;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.polymarketClobSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/PolymarketClobAPI.yaml
6
- * Generated at: 2026-06-04T11:40:47.258Z
6
+ * Generated at: 2026-06-08T19:06:11.828Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.polymarketClobSpec = {
@@ -349,6 +349,24 @@ exports.polymarketClobSpec = {
349
349
  ]
350
350
  }
351
351
  },
352
+ "/v1/heartbeats": {
353
+ "post": {
354
+ "summary": "Refresh authenticated session heartbeat",
355
+ "tags": [
356
+ "Orders"
357
+ ],
358
+ "security": [
359
+ {
360
+ "L2Auth": []
361
+ }
362
+ ],
363
+ "parameters": [
364
+ {
365
+ "$ref": "#/components/parameters/L2Headers"
366
+ }
367
+ ]
368
+ }
369
+ },
352
370
  "/data/order/{id}": {
353
371
  "get": {
354
372
  "summary": "Get Order",
@@ -410,6 +428,13 @@ exports.polymarketClobSpec = {
410
428
  "schema": {
411
429
  "type": "string"
412
430
  }
431
+ },
432
+ {
433
+ "name": "next_cursor",
434
+ "in": "query",
435
+ "schema": {
436
+ "type": "string"
437
+ }
413
438
  }
414
439
  ]
415
440
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/Polymarket_Data_API.yaml
3
- * Generated at: 2026-06-04T11:40:47.271Z
3
+ * Generated at: 2026-06-08T19:06:11.840Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const polymarketDataSpec: {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.polymarketDataSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/Polymarket_Data_API.yaml
6
- * Generated at: 2026-06-04T11:40:47.271Z
6
+ * Generated at: 2026-06-08T19:06:11.840Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.polymarketDataSpec = {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/PolymarketGammaAPI.yaml
3
- * Generated at: 2026-06-04T11:40:47.268Z
3
+ * Generated at: 2026-06-08T19:06:11.836Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const polymarketGammaSpec: {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.polymarketGammaSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/PolymarketGammaAPI.yaml
6
- * Generated at: 2026-06-04T11:40:47.268Z
6
+ * Generated at: 2026-06-08T19:06:11.836Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.polymarketGammaSpec = {
@@ -10,9 +10,9 @@ const accounts_1 = require("viem/accounts");
10
10
  const chains_1 = require("viem/chains");
11
11
  const axios_1 = __importDefault(require("axios"));
12
12
  const logger_1 = require("../../utils/logger");
13
+ const config_1 = require("./config");
13
14
  const errors_1 = require("./errors");
14
15
  const DEFAULT_POLYMARKET_HOST = process.env.POLYMARKET_CLOB_URL || 'https://clob.polymarket.com';
15
- const POLYGON_CHAIN_ID = 137;
16
16
  // Polymarket CLOB signature types — determines how the CLOB API
17
17
  // resolves the on-chain address holding the user's funds.
18
18
  const SIG_TYPE_EOA = 0;
@@ -83,7 +83,7 @@ class PolymarketAuth {
83
83
  // Otherwise, derive/create them using L1 auth
84
84
  const l1Client = new clob_client_v2_1.ClobClient({
85
85
  host: this.host,
86
- chain: POLYGON_CHAIN_ID,
86
+ chain: config_1.POLYMARKET_CHAIN_ID,
87
87
  signer: this.signer,
88
88
  });
89
89
  // Robust derivation strategy:
@@ -253,7 +253,7 @@ class PolymarketAuth {
253
253
  // console.log(`[PolymarketAuth] Initializing ClobClient | Signer: ${signerAddress} | Funder: ${finalProxyAddress} | SigType: ${finalSignatureType}`);
254
254
  this.clobClient = new clob_client_v2_1.ClobClient({
255
255
  host: this.host,
256
- chain: POLYGON_CHAIN_ID,
256
+ chain: config_1.POLYMARKET_CHAIN_ID,
257
257
  signer: this.signer,
258
258
  creds: apiCreds,
259
259
  signatureType: finalSignatureType,
@@ -0,0 +1 @@
1
+ export declare const POLYMARKET_CHAIN_ID = 137;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.POLYMARKET_CHAIN_ID = void 0;
4
+ exports.POLYMARKET_CHAIN_ID = 137; // Polygon mainnet
@@ -14,6 +14,7 @@ const api_clob_1 = require("./api-clob");
14
14
  const api_data_1 = require("./api-data");
15
15
  const api_gamma_1 = require("./api-gamma");
16
16
  const auth_1 = require("./auth");
17
+ const config_1 = require("./config");
17
18
  const logger_1 = require("../../utils/logger");
18
19
  const errors_2 = require("./errors");
19
20
  const fetcher_1 = require("./fetcher");
@@ -629,7 +630,7 @@ class PolymarketExchange extends BaseExchange_1.PredictionMarketExchange {
629
630
  // Static network avoids ethers v5 auto-detect (eth_chainId), which can throw
630
631
  // noNetwork / NETWORK_ERROR on flaky public RPCs (#92).
631
632
  const provider = new ethers.providers.StaticJsonRpcProvider('https://polygon-rpc.com', {
632
- chainId: 137,
633
+ chainId: config_1.POLYMARKET_CHAIN_ID,
633
634
  name: 'matic',
634
635
  });
635
636
  const pusdAddress = '0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB'; // pUSD (Polymarket USD)
@@ -16,6 +16,7 @@
16
16
  */
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.PolymarketUSWebSocket = void 0;
19
+ const logger_1 = require("../../utils/logger");
19
20
  const watch_timeout_1 = require("../../utils/watch-timeout");
20
21
  const price_1 = require("./price");
21
22
  function sideFromOrderSide(raw) {
@@ -96,8 +97,8 @@ class PolymarketUSWebSocket {
96
97
  try {
97
98
  this.socket.close();
98
99
  }
99
- catch {
100
- // Ignore close errors.
100
+ catch (error) {
101
+ logger_1.logger.debug('[polymarket_us] error during socket close', { error: String(error) });
101
102
  }
102
103
  this.socket = null;
103
104
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/probable/probable.yaml
3
- * Generated at: 2026-06-04T11:40:47.295Z
3
+ * Generated at: 2026-06-08T19:06:11.866Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const probableApiSpec: {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.probableApiSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/probable/probable.yaml
6
- * Generated at: 2026-06-04T11:40:47.295Z
6
+ * Generated at: 2026-06-08T19:06:11.866Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.probableApiSpec = {
@@ -5,6 +5,7 @@ const clob_1 = require("@prob/clob");
5
5
  const accounts_1 = require("viem/accounts");
6
6
  const viem_1 = require("viem");
7
7
  const chains_1 = require("viem/chains");
8
+ const config_1 = require("./config");
8
9
  /**
9
10
  * Manages Probable authentication and CLOB client initialization.
10
11
  * Requires a privateKey and pre-generated API key triplet (apiKey, apiSecret, passphrase).
@@ -29,8 +30,8 @@ class ProbableAuth {
29
30
  if (this.clobClient) {
30
31
  return this.clobClient;
31
32
  }
32
- const chainId = parseInt(process.env.PROBABLE_CHAIN_ID || '56', 10);
33
- const chain = chainId === 97 ? chains_1.bscTestnet : chains_1.bsc;
33
+ const chainId = parseInt(process.env.PROBABLE_CHAIN_ID || String(config_1.PROBABLE_CHAIN_ID), 10);
34
+ const chain = chainId === config_1.PROBABLE_TESTNET_CHAIN_ID ? chains_1.bscTestnet : chains_1.bsc;
34
35
  const account = (0, accounts_1.privateKeyToAccount)(this.credentials.privateKey);
35
36
  const wallet = (0, viem_1.createWalletClient)({
36
37
  account,
@@ -45,9 +46,9 @@ class ProbableAuth {
45
46
  // @prob/clob may resolve a different viem copy than this package; types then
46
47
  // disagree on WalletClient. Runtime shape is identical.
47
48
  const walletForClob = wallet;
48
- if (chainId === 56) {
49
+ if (chainId === config_1.PROBABLE_CHAIN_ID) {
49
50
  this.clobClient = (0, clob_1.createClobClient)({
50
- chainId: 56,
51
+ chainId: config_1.PROBABLE_CHAIN_ID,
51
52
  wallet: walletForClob,
52
53
  credential,
53
54
  });
@@ -0,0 +1,2 @@
1
+ export declare const PROBABLE_CHAIN_ID = 56;
2
+ export declare const PROBABLE_TESTNET_CHAIN_ID = 97;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PROBABLE_TESTNET_CHAIN_ID = exports.PROBABLE_CHAIN_ID = void 0;
4
+ exports.PROBABLE_CHAIN_ID = 56; // BSC mainnet
5
+ exports.PROBABLE_TESTNET_CHAIN_ID = 97; // BSC testnet
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ProbableWebSocket = void 0;
4
4
  const watch_timeout_1 = require("../../utils/watch-timeout");
5
+ const config_1 = require("./config");
5
6
  /**
6
7
  * Probable WebSocket implementation for real-time order book streaming.
7
8
  * Uses the @prob/clob SDK's subscribePublicStream (no auth required).
@@ -19,7 +20,7 @@ class ProbableWebSocket {
19
20
  async ensureClient() {
20
21
  if (this.client)
21
22
  return this.client;
22
- const chainId = this.config.chainId || parseInt(process.env.PROBABLE_CHAIN_ID || '56', 10);
23
+ const chainId = this.config.chainId || parseInt(process.env.PROBABLE_CHAIN_ID || String(config_1.PROBABLE_CHAIN_ID), 10);
23
24
  const wsUrl = this.config.wsUrl || process.env.PROBABLE_WS_URL || 'wss://ws.probable.markets/public/api/v1';
24
25
  const baseUrl = this.config.baseUrl || process.env.PROBABLE_BASE_URL || 'https://api.probable.markets/public/api/v1';
25
26
  // Dynamically import @prob/clob using eval to bypass TS compilation to require()
@@ -5,7 +5,7 @@
5
5
  * It is NOT wired into defineImplicitApi — the fetcher calls these endpoints
6
6
  * directly via FetcherContext.http (the rate-limited HTTP client).
7
7
  *
8
- * Base URL: https://suibets.replit.app
8
+ * Base URL: https://www.suibets.com
9
9
  *
10
10
  * Endpoints:
11
11
  * GET /api/p2p/offers - List open P2P offers (status, matchId, sport, limit, offset)
@@ -6,7 +6,7 @@
6
6
  * It is NOT wired into defineImplicitApi — the fetcher calls these endpoints
7
7
  * directly via FetcherContext.http (the rate-limited HTTP client).
8
8
  *
9
- * Base URL: https://suibets.replit.app
9
+ * Base URL: https://www.suibets.com
10
10
  *
11
11
  * Endpoints:
12
12
  * GET /api/p2p/offers - List open P2P offers (status, matchId, sport, limit, offset)
@@ -1,4 +1,4 @@
1
- export declare const SUIBETS_BASE_URL = "https://suibets.replit.app";
1
+ export declare const SUIBETS_BASE_URL = "https://www.suibets.com";
2
2
  export declare const SUIBETS_PLATFORM_FEE = 0.02;
3
3
  export declare const MIST_PER_SUI = 1000000000;
4
4
  export declare const MIN_PRICE = 0.01;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ALLOWED_HOSTS = exports.RATE_LIMIT_MS = exports.MAX_PRICE = exports.MIN_PRICE = exports.MIST_PER_SUI = exports.SUIBETS_PLATFORM_FEE = exports.SUIBETS_BASE_URL = void 0;
4
4
  exports.validateBaseUrl = validateBaseUrl;
5
5
  exports.getSuibetsConfig = getSuibetsConfig;
6
- exports.SUIBETS_BASE_URL = 'https://suibets.replit.app';
6
+ exports.SUIBETS_BASE_URL = 'https://www.suibets.com';
7
7
  // SuiBets is a P2P sports betting platform on Sui blockchain.
8
8
  // Platform takes a 2% fee on settled markets.
9
9
  exports.SUIBETS_PLATFORM_FEE = 0.02;
@@ -15,7 +15,7 @@ exports.MAX_PRICE = 0.99;
15
15
  // Minimum delay between outbound requests (milliseconds)
16
16
  exports.RATE_LIMIT_MS = 300;
17
17
  // Allowlist of permitted hostnames for SSRF protection
18
- exports.ALLOWED_HOSTS = ['suibets.replit.app'];
18
+ exports.ALLOWED_HOSTS = ['www.suibets.com'];
19
19
  /**
20
20
  * Validates that the given URL's hostname is in the ALLOWED_HOSTS allowlist.
21
21
  * Throws if the hostname is not permitted, to prevent SSRF.
@@ -41,6 +41,15 @@ class SuibetsErrorMapper extends error_mapper_1.ErrorMapper {
41
41
  mapError(error) {
42
42
  if (axios_1.default.isAxiosError(error)) {
43
43
  const status = error.response?.status;
44
+ // HTML body = hosting/gateway outage — not a missing resource.
45
+ // Return ExchangeNotAvailable so callers can distinguish
46
+ // "offer not found" from "upstream server is down".
47
+ const responseData = error.response?.data;
48
+ const isHtml = typeof responseData === 'string' &&
49
+ responseData.trimStart().startsWith('<');
50
+ if (isHtml) {
51
+ return new errors_1.ExchangeNotAvailable('SuiBets API is unavailable. Check https://www.suibets.com for service status.', this.exchangeName);
52
+ }
44
53
  if (status === 429) {
45
54
  const retryAfter = error.response?.headers?.['retry-after'];
46
55
  const retryAfterSeconds = retryAfter ? parseInt(retryAfter, 10) : undefined;
@@ -33,7 +33,24 @@ export interface SuibetsRawEvent {
33
33
  status: string;
34
34
  offers?: SuibetsRawOffer[];
35
35
  }
36
- export declare class SuibetsFetcher implements IExchangeFetcher<SuibetsRawOffer, SuibetsRawEvent> {
36
+ /**
37
+ * Structured return type for fetchRawPositions.
38
+ * Keeps the three position-array types separate so each is normalised
39
+ * with the correct shape instead of being cast from unknown[].
40
+ */
41
+ export interface SuibetsRawPositions {
42
+ createdOffers: SuibetsRawOffer[];
43
+ matchedBets: unknown[];
44
+ parlays: unknown[];
45
+ }
46
+ /**
47
+ * Type guard: true only when the value has the core fields that
48
+ * SuibetsNormalizer.normalizePosition() reads (id, creatorOdds, creatorStake).
49
+ * Guards against silent garbage output when matchedBets or parlays
50
+ * are accidentally passed in as SuibetsRawOffer.
51
+ */
52
+ export declare function isSuibetsRawOffer(value: unknown): value is SuibetsRawOffer;
53
+ export declare class SuibetsFetcher implements IExchangeFetcher<SuibetsRawOffer, SuibetsRawEvent, SuibetsRawPositions> {
37
54
  private readonly ctx;
38
55
  private readonly baseUrl;
39
56
  constructor(ctx: FetcherContext, baseUrl: string);
@@ -59,6 +76,9 @@ export declare class SuibetsFetcher implements IExchangeFetcher<SuibetsRawOffer,
59
76
  /**
60
77
  * Fetches raw positions (created offers, matched bets, parlays) for a
61
78
  * given Sui wallet address.
79
+ *
80
+ * Returns each array separately and typed so that normalisation uses
81
+ * the correct shape per position type rather than casting from unknown[].
62
82
  */
63
- fetchRawPositions(walletAddress: string): Promise<unknown[]>;
83
+ fetchRawPositions(walletAddress: string): Promise<SuibetsRawPositions>;
64
84
  }
@@ -1,7 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SuibetsFetcher = void 0;
4
+ exports.isSuibetsRawOffer = isSuibetsRawOffer;
4
5
  const errors_1 = require("./errors");
6
+ /**
7
+ * Type guard: true only when the value has the core fields that
8
+ * SuibetsNormalizer.normalizePosition() reads (id, creatorOdds, creatorStake).
9
+ * Guards against silent garbage output when matchedBets or parlays
10
+ * are accidentally passed in as SuibetsRawOffer.
11
+ */
12
+ function isSuibetsRawOffer(value) {
13
+ if (typeof value !== 'object' || value === null)
14
+ return false;
15
+ const v = value;
16
+ return ((typeof v['id'] === 'string' || typeof v['id'] === 'number') &&
17
+ typeof v['creatorOdds'] === 'number' &&
18
+ typeof v['creatorStake'] === 'number');
19
+ }
5
20
  class SuibetsFetcher {
6
21
  ctx;
7
22
  baseUrl;
@@ -115,14 +130,17 @@ class SuibetsFetcher {
115
130
  /**
116
131
  * Fetches raw positions (created offers, matched bets, parlays) for a
117
132
  * given Sui wallet address.
133
+ *
134
+ * Returns each array separately and typed so that normalisation uses
135
+ * the correct shape per position type rather than casting from unknown[].
118
136
  */
119
137
  async fetchRawPositions(walletAddress) {
120
138
  const data = await this.get('/api/p2p/my', { wallet: walletAddress });
121
- return [
122
- ...(data.createdOffers ?? []),
123
- ...(data.matchedBets ?? []),
124
- ...(data.parlays ?? []),
125
- ];
139
+ return {
140
+ createdOffers: (data.createdOffers ?? []).filter(isSuibetsRawOffer),
141
+ matchedBets: data.matchedBets ?? [],
142
+ parlays: data.parlays ?? [],
143
+ };
126
144
  }
127
145
  }
128
146
  exports.SuibetsFetcher = SuibetsFetcher;
@@ -3,7 +3,7 @@ import { UnifiedMarket, UnifiedEvent, OrderBook, Position } from '../../types';
3
3
  export interface SuibetsCredentials extends ExchangeCredentials {
4
4
  /** Sui wallet address for fetching personal positions */
5
5
  walletAddress?: string;
6
- /** Override API base URL (default: https://suibets.replit.app) */
6
+ /** Override API base URL (default: https://www.suibets.com) */
7
7
  baseUrl?: string;
8
8
  }
9
9
  /**
@@ -113,7 +113,7 @@ class SuiBetsExchange extends BaseExchange_1.PredictionMarketExchange {
113
113
  'Pass it via new SuiBetsExchange({ walletAddress: "0x..." }).', 'SuiBets');
114
114
  }
115
115
  const raw = await this.fetcher.fetchRawPositions(wallet);
116
- return raw.map(r => this.normalizer.normalizePosition(r));
116
+ return raw.createdOffers.map(r => this.normalizer.normalizePosition(r));
117
117
  }
118
118
  }
119
119
  exports.SuiBetsExchange = SuiBetsExchange;
@@ -1,21 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SuibetsNormalizer = void 0;
4
- const metadata_1 = require("../../utils/metadata");
5
4
  const utils_1 = require("./utils");
6
- // Raw SuiBets offer fields already promoted to first-class UnifiedMarket columns.
7
- // Omit these from sourceMetadata to capture only vendor-specific data not
8
- // represented by the unified shape.
9
- const SUIBETS_PROMOTED_OFFER_KEYS = [
10
- 'id', 'matchId', 'matchName', 'homeTeam', 'awayTeam',
11
- 'creatorOdds', 'creatorStake', 'remainingStake', 'totalMatched',
12
- 'matchDate', 'expiresAt', 'status', 'onchainOfferId',
13
- 'leagueName', 'sport', 'isOnchain',
14
- ];
15
- // Raw SuiBets event fields already promoted to first-class UnifiedEvent columns.
16
- const SUIBETS_PROMOTED_EVENT_KEYS = [
17
- 'id', 'name', 'homeTeam', 'awayTeam', 'sport', 'leagueName', 'offers',
18
- ];
19
5
  function liquidity(offer) {
20
6
  const remaining = offer.remainingStake ?? offer.creatorStake;
21
7
  return (0, utils_1.mistToSui)(remaining);
@@ -64,16 +50,13 @@ class SuibetsNormalizer {
64
50
  resolutionDate: new Date(dateSource),
65
51
  volume24h,
66
52
  liquidity: liq,
67
- url: 'https://suibets.replit.app/p2p',
53
+ url: 'https://www.suibets.com/p2p',
68
54
  status: (0, utils_1.mapStatus)(raw.status),
69
55
  category: 'Sports',
70
56
  tags: ['Sports', 'P2P', raw.sport, raw.leagueName].filter((t) => Boolean(t)),
71
57
  contractAddress: raw.onchainOfferId,
72
58
  yes: creatorOutcome,
73
59
  no: takerOutcome,
74
- // Retains creatorWallet, creatorTeam, takerStake, currency \u2014 fields
75
- // that are vendor-specific and not promoted to any unified column.
76
- sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw, SUIBETS_PROMOTED_OFFER_KEYS),
77
60
  };
78
61
  return market;
79
62
  }
@@ -98,12 +81,9 @@ class SuibetsNormalizer {
98
81
  markets,
99
82
  volume24h: totalVolume,
100
83
  volume: totalVolume,
101
- url: 'https://suibets.replit.app/p2p',
84
+ url: 'https://www.suibets.com/p2p',
102
85
  category: 'Sports',
103
86
  tags: ['Sports', 'P2P', 'Sui', raw.sport, raw.leagueName].filter((t) => Boolean(t)),
104
- // Retains matchDate and status \u2014 event-level fields not promoted to
105
- // any first-class UnifiedEvent column.
106
- sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw, SUIBETS_PROMOTED_EVENT_KEYS),
107
87
  };
108
88
  }
109
89
  normalizePosition(raw) {
@@ -115,7 +115,7 @@ class BinanceFeed extends base_feed_1.BaseDataFeed {
115
115
  }
116
116
  watchTickerImpl(symbol, callback) {
117
117
  const sub = { symbol, callback };
118
- this.subscriptions = [...this.subscriptions, sub];
118
+ this.subscriptions.push(sub);
119
119
  this.ensureConnected().catch((err) => {
120
120
  logger_1.logger.error('[BinanceFeed] initial connect failed in watchTickerImpl', {
121
121
  error: err instanceof Error ? err.message : String(err),
@@ -185,14 +185,17 @@ class BinanceFeed extends base_feed_1.BaseDataFeed {
185
185
  try {
186
186
  msg = JSON.parse(text);
187
187
  }
188
- catch {
188
+ catch (error) {
189
+ logger_1.logger.debug('[BinanceFeed] failed to parse relay message', {
190
+ error: error instanceof Error ? error.message : String(error),
191
+ });
189
192
  return;
190
193
  }
191
194
  if (msg.op !== 'event')
192
195
  return;
193
196
  const event = msg;
194
197
  const ticker = (0, normalizer_1.normalizeTradeToTicker)(event);
195
- this.latestTickers = new Map(this.latestTickers).set(ticker.symbol, ticker);
198
+ this.latestTickers.set(ticker.symbol, ticker);
196
199
  for (const sub of this.subscriptions) {
197
200
  if (sub.symbol === ticker.symbol) {
198
201
  sub.callback(ticker);