pmxt-core 2.44.5 → 2.45.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 (45) hide show
  1. package/dist/exchanges/kalshi/api.d.ts +1 -1
  2. package/dist/exchanges/kalshi/api.js +1 -1
  3. package/dist/exchanges/kalshi/fetcher.d.ts +11 -1
  4. package/dist/exchanges/kalshi/fetcher.js +49 -17
  5. package/dist/exchanges/kalshi/normalizer.d.ts +12 -0
  6. package/dist/exchanges/kalshi/normalizer.js +125 -1
  7. package/dist/exchanges/limitless/api.d.ts +1 -1
  8. package/dist/exchanges/limitless/api.js +1 -1
  9. package/dist/exchanges/mock/index.js +13 -2
  10. package/dist/exchanges/myriad/api.d.ts +1 -1
  11. package/dist/exchanges/myriad/api.js +1 -1
  12. package/dist/exchanges/opinion/api.d.ts +1 -1
  13. package/dist/exchanges/opinion/api.js +1 -1
  14. package/dist/exchanges/polymarket/api-clob.d.ts +1 -1
  15. package/dist/exchanges/polymarket/api-clob.js +1 -1
  16. package/dist/exchanges/polymarket/api-data.d.ts +1 -1
  17. package/dist/exchanges/polymarket/api-data.js +1 -1
  18. package/dist/exchanges/polymarket/api-gamma.d.ts +1 -1
  19. package/dist/exchanges/polymarket/api-gamma.js +1 -1
  20. package/dist/exchanges/polymarket/websocket.d.ts +12 -0
  21. package/dist/exchanges/polymarket/websocket.js +120 -14
  22. package/dist/exchanges/probable/api.d.ts +1 -1
  23. package/dist/exchanges/probable/api.js +1 -1
  24. package/dist/exchanges/suibets/api.d.ts +15 -0
  25. package/dist/exchanges/suibets/api.js +17 -0
  26. package/dist/exchanges/suibets/config.d.ts +16 -0
  27. package/dist/exchanges/suibets/config.js +34 -0
  28. package/dist/exchanges/suibets/errors.d.ts +16 -0
  29. package/dist/exchanges/suibets/errors.js +71 -0
  30. package/dist/exchanges/suibets/fetcher.d.ts +64 -0
  31. package/dist/exchanges/suibets/fetcher.js +128 -0
  32. package/dist/exchanges/suibets/index.d.ts +54 -0
  33. package/dist/exchanges/suibets/index.js +114 -0
  34. package/dist/exchanges/suibets/normalizer.d.ts +8 -0
  35. package/dist/exchanges/suibets/normalizer.js +102 -0
  36. package/dist/exchanges/suibets/utils.d.ts +63 -0
  37. package/dist/exchanges/suibets/utils.js +124 -0
  38. package/dist/index.d.ts +4 -0
  39. package/dist/index.js +5 -1
  40. package/dist/router/Router.js +12 -3
  41. package/dist/server/app.js +76 -1
  42. package/dist/server/exchange-factory.js +6 -0
  43. package/dist/server/openapi.yaml +7 -0
  44. package/dist/server/ws-handler.js +196 -23
  45. package/package.json +6 -6
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SuiBetsExchange = void 0;
4
+ const BaseExchange_1 = require("../../BaseExchange");
5
+ const errors_1 = require("../../errors");
6
+ const config_1 = require("./config");
7
+ const fetcher_1 = require("./fetcher");
8
+ const normalizer_1 = require("./normalizer");
9
+ const utils_1 = require("./utils");
10
+ /**
11
+ * SuiBets — Decentralised P2P sports betting on Sui blockchain.
12
+ *
13
+ * Maps P2P bet offers to the pmxt unified market model:
14
+ * - Market = one P2P offer (creator side vs taker side)
15
+ * - Event = a sports match (groups all offers for that match)
16
+ * - Outcome = creator's pick (YES) or opposite (NO)
17
+ * - Price = implied probability derived from the offer odds
18
+ *
19
+ * Usage:
20
+ * ```ts
21
+ * import pmxt from 'pmxtjs';
22
+ * const exchange = new pmxt.SuiBets();
23
+ * const markets = await exchange.fetchMarkets({ limit: 20 });
24
+ * ```
25
+ */
26
+ class SuiBetsExchange extends BaseExchange_1.PredictionMarketExchange {
27
+ capabilityOverrides = {
28
+ fetchOrderBook: 'emulated',
29
+ createOrder: false,
30
+ cancelOrder: false,
31
+ fetchOrder: false,
32
+ fetchOpenOrders: false,
33
+ fetchBalance: false,
34
+ fetchPositions: true,
35
+ watchOrderBook: false,
36
+ watchTrades: false,
37
+ };
38
+ config;
39
+ fetcher;
40
+ normalizer;
41
+ walletAddress;
42
+ constructor(credentials) {
43
+ super(credentials);
44
+ this.rateLimit = config_1.RATE_LIMIT_MS;
45
+ this.walletAddress = credentials?.walletAddress;
46
+ if (credentials?.baseUrl) {
47
+ (0, config_1.validateBaseUrl)(credentials.baseUrl);
48
+ }
49
+ this.config = (0, config_1.getSuibetsConfig)(credentials?.baseUrl);
50
+ const ctx = {
51
+ http: this.http,
52
+ callApi: this.callApi.bind(this),
53
+ getHeaders: () => ({}),
54
+ };
55
+ this.fetcher = new fetcher_1.SuibetsFetcher(ctx, this.config.baseUrl);
56
+ this.normalizer = new normalizer_1.SuibetsNormalizer();
57
+ }
58
+ get name() {
59
+ return 'SuiBets';
60
+ }
61
+ // SuiBets is a public API -- no request signing required
62
+ sign() {
63
+ return {};
64
+ }
65
+ // -------------------------------------------------------------------------
66
+ // Market Data
67
+ // -------------------------------------------------------------------------
68
+ async fetchMarketsImpl(params) {
69
+ const raw = await this.fetcher.fetchRawMarkets(params);
70
+ return raw
71
+ .map(r => this.normalizer.normalizeMarket(r))
72
+ .filter((m) => m !== null);
73
+ }
74
+ async fetchEventsImpl(params) {
75
+ const raw = await this.fetcher.fetchRawEvents(params);
76
+ return raw
77
+ .map(r => this.normalizer.normalizeEvent(r))
78
+ .filter((e) => e !== null);
79
+ }
80
+ /**
81
+ * Emulated order book derived from offer odds.
82
+ *
83
+ * Bid side: what buyers pay to back the creator's pick (YES price).
84
+ * Ask side: what sellers want to take the opposite side (NO price).
85
+ */
86
+ async fetchOrderBook(outcomeId) {
87
+ const { offerId } = (0, utils_1.fromOutcomeId)(outcomeId);
88
+ const markets = await this.fetchMarketsImpl({ marketId: `suibets:${offerId}` });
89
+ const market = markets[0];
90
+ if (!market)
91
+ return { bids: [], asks: [], timestamp: Date.now() };
92
+ const yes = market.outcomes[0];
93
+ const no = market.outcomes[1];
94
+ const size = market.liquidity;
95
+ return {
96
+ bids: [{ price: yes.price, size }],
97
+ asks: [{ price: no.price, size }],
98
+ timestamp: Date.now(),
99
+ };
100
+ }
101
+ // -------------------------------------------------------------------------
102
+ // Positions (read-only -- requires walletAddress)
103
+ // -------------------------------------------------------------------------
104
+ async fetchPositions() {
105
+ const wallet = this.walletAddress;
106
+ if (!wallet) {
107
+ throw new errors_1.AuthenticationError('fetchPositions() requires a walletAddress. ' +
108
+ 'Pass it via new SuiBetsExchange({ walletAddress: "0x..." }).', 'SuiBets');
109
+ }
110
+ const raw = await this.fetcher.fetchRawPositions(wallet);
111
+ return raw.map(r => this.normalizer.normalizePosition(r));
112
+ }
113
+ }
114
+ exports.SuiBetsExchange = SuiBetsExchange;
@@ -0,0 +1,8 @@
1
+ import { IExchangeNormalizer } from '../interfaces';
2
+ import { UnifiedMarket, UnifiedEvent, Position } from '../../types';
3
+ import { SuibetsRawOffer, SuibetsRawEvent } from './fetcher';
4
+ export declare class SuibetsNormalizer implements IExchangeNormalizer<SuibetsRawOffer, SuibetsRawEvent> {
5
+ normalizeMarket(raw: SuibetsRawOffer): UnifiedMarket | null;
6
+ normalizeEvent(raw: SuibetsRawEvent): UnifiedEvent | null;
7
+ normalizePosition(raw: SuibetsRawOffer): Position;
8
+ }
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SuibetsNormalizer = void 0;
4
+ const utils_1 = require("./utils");
5
+ function liquidity(offer) {
6
+ const remaining = offer.remainingStake ?? offer.creatorStake;
7
+ return (0, utils_1.mistToSui)(remaining);
8
+ }
9
+ class SuibetsNormalizer {
10
+ normalizeMarket(raw) {
11
+ if (!raw?.id)
12
+ return null;
13
+ const dateSource = raw.matchDate || raw.expiresAt;
14
+ if (!dateSource) {
15
+ throw new Error(`SuibetsNormalizer: offer ${raw.id} has neither matchDate nor expiresAt`);
16
+ }
17
+ const homeTeam = raw.homeTeam || 'Unknown Team';
18
+ const awayTeam = raw.awayTeam || 'Unknown Team';
19
+ const odds = Number(raw.creatorOdds) || 2;
20
+ const yesProb = (0, utils_1.impliedProbability)(odds);
21
+ const noProb = (0, utils_1.takerProbability)(odds);
22
+ const liq = liquidity(raw);
23
+ const volume24h = (0, utils_1.mistToSui)(raw.totalMatched ?? 0);
24
+ const marketId = (0, utils_1.toMarketId)(raw.id);
25
+ const creatorOutcome = {
26
+ outcomeId: (0, utils_1.toOutcomeId)(raw.id, 'creator'),
27
+ marketId,
28
+ label: (0, utils_1.sideLabel)(raw, 'creator'),
29
+ price: yesProb,
30
+ };
31
+ const takerOutcome = {
32
+ outcomeId: (0, utils_1.toOutcomeId)(raw.id, 'taker'),
33
+ marketId,
34
+ label: (0, utils_1.sideLabel)(raw, 'taker'),
35
+ price: noProb,
36
+ };
37
+ const market = {
38
+ marketId,
39
+ eventId: raw.matchId ? (0, utils_1.toMarketId)(raw.matchId) : undefined,
40
+ title: `${raw.matchName || `${homeTeam} vs ${awayTeam}`} \u2014 ${(0, utils_1.sideLabel)(raw, 'creator')} @ ${odds}x`,
41
+ description: [
42
+ `P2P offer on ${raw.sport || 'sports'} match.`,
43
+ `Creator bets ${(0, utils_1.sideLabel)(raw, 'creator')} at ${odds}x odds.`,
44
+ `Taker backs ${(0, utils_1.sideLabel)(raw, 'taker')} at ${(1 / noProb).toFixed(2)}x implied odds.`,
45
+ raw.leagueName ? `League: ${raw.leagueName}.` : '',
46
+ raw.isOnchain ? `On-chain escrow: ${raw.onchainOfferId ?? 'yes'}.` : 'Off-chain escrow.',
47
+ ].filter(Boolean).join(' '),
48
+ slug: raw.id,
49
+ outcomes: [creatorOutcome, takerOutcome],
50
+ resolutionDate: new Date(dateSource),
51
+ volume24h,
52
+ liquidity: liq,
53
+ url: 'https://suibets.replit.app/p2p',
54
+ status: (0, utils_1.mapStatus)(raw.status),
55
+ category: 'Sports',
56
+ tags: ['Sports', 'P2P', raw.sport, raw.leagueName].filter((t) => Boolean(t)),
57
+ contractAddress: raw.onchainOfferId,
58
+ yes: creatorOutcome,
59
+ no: takerOutcome,
60
+ };
61
+ return market;
62
+ }
63
+ normalizeEvent(raw) {
64
+ if (!raw?.id)
65
+ return null;
66
+ const homeTeam = raw.homeTeam || 'Unknown Team';
67
+ const awayTeam = raw.awayTeam || 'Unknown Team';
68
+ const markets = (raw.offers ?? [])
69
+ .map(o => this.normalizeMarket(o))
70
+ .filter((m) => m !== null);
71
+ const totalVolume = markets.reduce((s, m) => s + (m.volume24h ?? 0), 0);
72
+ return {
73
+ id: (0, utils_1.toMarketId)(raw.id),
74
+ title: raw.name || `${homeTeam} vs ${awayTeam}`,
75
+ description: [
76
+ raw.leagueName ? `${raw.leagueName} \u2014` : '',
77
+ raw.sport,
78
+ 'P2P betting on SuiBets.',
79
+ ].filter(Boolean).join(' '),
80
+ slug: raw.id,
81
+ markets,
82
+ volume24h: totalVolume,
83
+ volume: totalVolume,
84
+ url: 'https://suibets.replit.app/p2p',
85
+ category: 'Sports',
86
+ tags: ['Sports', 'P2P', 'Sui', raw.sport, raw.leagueName].filter((t) => Boolean(t)),
87
+ };
88
+ }
89
+ normalizePosition(raw) {
90
+ const odds = Number(raw.creatorOdds) || 2;
91
+ return {
92
+ marketId: (0, utils_1.toMarketId)(raw.matchId ?? raw.id),
93
+ outcomeId: (0, utils_1.toOutcomeId)(raw.id, 'creator'),
94
+ outcomeLabel: (0, utils_1.sideLabel)(raw, 'creator'),
95
+ size: (0, utils_1.mistToSui)(raw.creatorStake ?? 0),
96
+ entryPrice: (0, utils_1.impliedProbability)(odds),
97
+ currentPrice: (0, utils_1.impliedProbability)(odds),
98
+ unrealizedPnL: 0,
99
+ };
100
+ }
101
+ }
102
+ exports.SuibetsNormalizer = SuibetsNormalizer;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Build a unique market ID from a SuiBets offer ID.
3
+ * Format: "suibets:{offerId}"
4
+ */
5
+ export declare function toMarketId(offerId: string): string;
6
+ /**
7
+ * Extract the offer ID from a SuiBets market ID.
8
+ * Throws if the ID does not carry the expected prefix.
9
+ */
10
+ export declare function fromMarketId(marketId: string): string;
11
+ /**
12
+ * Build an outcome ID that encodes both the offer ID and the side.
13
+ * Format: "{offerId}:{side}"
14
+ */
15
+ export declare function toOutcomeId(offerId: string, side: 'creator' | 'taker'): string;
16
+ /**
17
+ * Decode an outcome ID back into offerId and side.
18
+ * Throws if the format is unrecognised or the side is invalid.
19
+ */
20
+ export declare function fromOutcomeId(outcomeId: string): {
21
+ offerId: string;
22
+ side: 'creator' | 'taker';
23
+ };
24
+ /**
25
+ * Map raw SuiBets offer statuses to the pmxt unified status vocabulary.
26
+ *
27
+ * OPEN -> active
28
+ * MATCHED -> matched
29
+ * SETTLED -> settled
30
+ * EXPIRED -> expired
31
+ * CANCELLED -> cancelled
32
+ * (other) -> inactive
33
+ */
34
+ export declare function mapStatus(rawStatus: string): string;
35
+ /**
36
+ * Convert decimal odds to an implied probability clamped to [0.01, 0.99].
37
+ *
38
+ * Throws if odds are zero or negative.
39
+ * Throws if odds are less than 1 (invalid — a payout below stake).
40
+ * When odds === 1 (evens), returns 0.99 (clamped maximum).
41
+ */
42
+ export declare function impliedProbability(decimalOdds: number): number;
43
+ /**
44
+ * Implied probability for the taker side: 1 - impliedProbability(odds),
45
+ * clamped to [0.01, 0.99].
46
+ */
47
+ export declare function takerProbability(decimalOdds: number): number;
48
+ /**
49
+ * Convert an amount denominated in MIST (the smallest SUI unit, 1e-9 SUI)
50
+ * to SUI by dividing by 1e9.
51
+ */
52
+ export declare function mistToSui(mist: number | string): number;
53
+ /**
54
+ * Return the human-readable team name for the given side of a P2P offer.
55
+ *
56
+ * Creator side: the team the creator bet on (creatorTeam, falling back to homeTeam).
57
+ * Taker side: the opposite team.
58
+ */
59
+ export declare function sideLabel(offer: {
60
+ creatorTeam?: string;
61
+ homeTeam?: string;
62
+ awayTeam?: string;
63
+ }, side: 'creator' | 'taker'): string;
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toMarketId = toMarketId;
4
+ exports.fromMarketId = fromMarketId;
5
+ exports.toOutcomeId = toOutcomeId;
6
+ exports.fromOutcomeId = fromOutcomeId;
7
+ exports.mapStatus = mapStatus;
8
+ exports.impliedProbability = impliedProbability;
9
+ exports.takerProbability = takerProbability;
10
+ exports.mistToSui = mistToSui;
11
+ exports.sideLabel = sideLabel;
12
+ const MARKET_PREFIX = 'suibets:';
13
+ /**
14
+ * Build a unique market ID from a SuiBets offer ID.
15
+ * Format: "suibets:{offerId}"
16
+ */
17
+ function toMarketId(offerId) {
18
+ return `${MARKET_PREFIX}${offerId}`;
19
+ }
20
+ /**
21
+ * Extract the offer ID from a SuiBets market ID.
22
+ * Throws if the ID does not carry the expected prefix.
23
+ */
24
+ function fromMarketId(marketId) {
25
+ if (!marketId.startsWith(MARKET_PREFIX)) {
26
+ throw new Error(`Invalid SuiBets market ID: ${marketId}`);
27
+ }
28
+ return marketId.slice(MARKET_PREFIX.length);
29
+ }
30
+ /**
31
+ * Build an outcome ID that encodes both the offer ID and the side.
32
+ * Format: "{offerId}:{side}"
33
+ */
34
+ function toOutcomeId(offerId, side) {
35
+ return `${offerId}:${side}`;
36
+ }
37
+ /**
38
+ * Decode an outcome ID back into offerId and side.
39
+ * Throws if the format is unrecognised or the side is invalid.
40
+ */
41
+ function fromOutcomeId(outcomeId) {
42
+ const lastColon = outcomeId.lastIndexOf(':');
43
+ if (lastColon === -1) {
44
+ throw new Error(`Invalid SuiBets outcome ID: ${outcomeId}`);
45
+ }
46
+ const offerId = outcomeId.slice(0, lastColon);
47
+ const side = outcomeId.slice(lastColon + 1);
48
+ if (side !== 'creator' && side !== 'taker') {
49
+ throw new Error(`Invalid side in SuiBets outcome ID: ${outcomeId}`);
50
+ }
51
+ if (!offerId) {
52
+ throw new Error(`Invalid SuiBets outcome ID (empty offerId): ${outcomeId}`);
53
+ }
54
+ return { offerId, side };
55
+ }
56
+ /**
57
+ * Map raw SuiBets offer statuses to the pmxt unified status vocabulary.
58
+ *
59
+ * OPEN -> active
60
+ * MATCHED -> matched
61
+ * SETTLED -> settled
62
+ * EXPIRED -> expired
63
+ * CANCELLED -> cancelled
64
+ * (other) -> inactive
65
+ */
66
+ function mapStatus(rawStatus) {
67
+ switch (rawStatus) {
68
+ case 'OPEN': return 'active';
69
+ case 'MATCHED': return 'matched';
70
+ case 'SETTLED': return 'settled';
71
+ case 'EXPIRED': return 'expired';
72
+ case 'CANCELLED': return 'cancelled';
73
+ default: return 'inactive';
74
+ }
75
+ }
76
+ /**
77
+ * Convert decimal odds to an implied probability clamped to [0.01, 0.99].
78
+ *
79
+ * Throws if odds are zero or negative.
80
+ * Throws if odds are less than 1 (invalid — a payout below stake).
81
+ * When odds === 1 (evens), returns 0.99 (clamped maximum).
82
+ */
83
+ function impliedProbability(decimalOdds) {
84
+ if (decimalOdds <= 0) {
85
+ throw new Error(`Decimal odds must be positive, got: ${decimalOdds}`);
86
+ }
87
+ if (decimalOdds < 1) {
88
+ throw new Error(`Decimal odds below 1 are invalid, got: ${decimalOdds}`);
89
+ }
90
+ const raw = 1 / decimalOdds;
91
+ return Math.min(0.99, Math.max(0.01, raw));
92
+ }
93
+ /**
94
+ * Implied probability for the taker side: 1 - impliedProbability(odds),
95
+ * clamped to [0.01, 0.99].
96
+ */
97
+ function takerProbability(decimalOdds) {
98
+ return Math.min(0.99, Math.max(0.01, 1 - impliedProbability(decimalOdds)));
99
+ }
100
+ /**
101
+ * Convert an amount denominated in MIST (the smallest SUI unit, 1e-9 SUI)
102
+ * to SUI by dividing by 1e9.
103
+ */
104
+ function mistToSui(mist) {
105
+ return Number(mist) / 1e9;
106
+ }
107
+ /**
108
+ * Return the human-readable team name for the given side of a P2P offer.
109
+ *
110
+ * Creator side: the team the creator bet on (creatorTeam, falling back to homeTeam).
111
+ * Taker side: the opposite team.
112
+ */
113
+ function sideLabel(offer, side) {
114
+ const creator = offer.creatorTeam || offer.homeTeam || 'Home';
115
+ const away = offer.awayTeam || 'Away';
116
+ if (side === 'creator')
117
+ return creator;
118
+ // Taker takes the opposite side
119
+ if (creator.toLowerCase() === offer.homeTeam?.toLowerCase())
120
+ return away;
121
+ if (creator.toLowerCase() === offer.awayTeam?.toLowerCase())
122
+ return offer.homeTeam || 'Home';
123
+ return 'Opposite';
124
+ }
package/dist/index.d.ts CHANGED
@@ -17,6 +17,7 @@ export * from './exchanges/smarkets';
17
17
  export * from './exchanges/polymarket_us';
18
18
  export * from './exchanges/hyperliquid';
19
19
  export * from './exchanges/gemini-titan';
20
+ export * from './exchanges/suibets';
20
21
  export * from './router';
21
22
  export * from './feeds';
22
23
  export * from './server/app';
@@ -36,6 +37,7 @@ import { SmarketsExchange } from './exchanges/smarkets';
36
37
  import { PolymarketUSExchange } from './exchanges/polymarket_us';
37
38
  import { HyperliquidExchange } from './exchanges/hyperliquid';
38
39
  import { GeminiTitanExchange } from './exchanges/gemini-titan';
40
+ import { SuiBetsExchange } from './exchanges/suibets';
39
41
  import { Router } from './router';
40
42
  declare const pmxt: {
41
43
  Mock: typeof MockExchange;
@@ -52,6 +54,7 @@ declare const pmxt: {
52
54
  PolymarketUS: typeof PolymarketUSExchange;
53
55
  Hyperliquid: typeof HyperliquidExchange;
54
56
  GeminiTitan: typeof GeminiTitanExchange;
57
+ SuiBets: typeof SuiBetsExchange;
55
58
  Router: typeof Router;
56
59
  };
57
60
  export declare const Mock: typeof MockExchange;
@@ -68,4 +71,5 @@ export declare const Smarkets: typeof SmarketsExchange;
68
71
  export declare const PolymarketUS: typeof PolymarketUSExchange;
69
72
  export declare const Hyperliquid: typeof HyperliquidExchange;
70
73
  export declare const GeminiTitan: typeof GeminiTitanExchange;
74
+ export declare const SuiBets: typeof SuiBetsExchange;
71
75
  export default pmxt;
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.GeminiTitan = exports.Hyperliquid = exports.PolymarketUS = exports.Smarkets = exports.Metaculus = exports.Opinion = exports.Myriad = exports.Baozi = exports.Probable = exports.KalshiDemo = exports.Kalshi = exports.Limitless = exports.Polymarket = exports.Mock = exports.parseOpenApiSpec = void 0;
17
+ exports.SuiBets = exports.GeminiTitan = exports.Hyperliquid = exports.PolymarketUS = exports.Smarkets = exports.Metaculus = exports.Opinion = exports.Myriad = exports.Baozi = exports.Probable = exports.KalshiDemo = exports.Kalshi = exports.Limitless = exports.Polymarket = exports.Mock = exports.parseOpenApiSpec = void 0;
18
18
  __exportStar(require("./BaseExchange"), exports);
19
19
  __exportStar(require("./types"), exports);
20
20
  __exportStar(require("./utils/math"), exports);
@@ -35,6 +35,7 @@ __exportStar(require("./exchanges/smarkets"), exports);
35
35
  __exportStar(require("./exchanges/polymarket_us"), exports);
36
36
  __exportStar(require("./exchanges/hyperliquid"), exports);
37
37
  __exportStar(require("./exchanges/gemini-titan"), exports);
38
+ __exportStar(require("./exchanges/suibets"), exports);
38
39
  __exportStar(require("./router"), exports);
39
40
  __exportStar(require("./feeds"), exports);
40
41
  __exportStar(require("./server/app"), exports);
@@ -54,6 +55,7 @@ const smarkets_1 = require("./exchanges/smarkets");
54
55
  const polymarket_us_1 = require("./exchanges/polymarket_us");
55
56
  const hyperliquid_1 = require("./exchanges/hyperliquid");
56
57
  const gemini_titan_1 = require("./exchanges/gemini-titan");
58
+ const suibets_1 = require("./exchanges/suibets");
57
59
  const router_1 = require("./router");
58
60
  const pmxt = {
59
61
  Mock: mock_1.MockExchange,
@@ -70,6 +72,7 @@ const pmxt = {
70
72
  PolymarketUS: polymarket_us_1.PolymarketUSExchange,
71
73
  Hyperliquid: hyperliquid_1.HyperliquidExchange,
72
74
  GeminiTitan: gemini_titan_1.GeminiTitanExchange,
75
+ SuiBets: suibets_1.SuiBetsExchange,
73
76
  Router: router_1.Router,
74
77
  };
75
78
  exports.Mock = mock_1.MockExchange;
@@ -86,4 +89,5 @@ exports.Smarkets = smarkets_1.SmarketsExchange;
86
89
  exports.PolymarketUS = polymarket_us_1.PolymarketUSExchange;
87
90
  exports.Hyperliquid = hyperliquid_1.HyperliquidExchange;
88
91
  exports.GeminiTitan = gemini_titan_1.GeminiTitanExchange;
92
+ exports.SuiBets = suibets_1.SuiBetsExchange;
89
93
  exports.default = pmxt;
@@ -217,7 +217,10 @@ class Router extends BaseExchange_1.PredictionMarketExchange {
217
217
  }
218
218
  // Lookup mode: find matches for a specific market.
219
219
  const response = await this.client.getMarketMatches(params);
220
- const matches = response.matches ?? [];
220
+ if (!response || !Array.isArray(response.matches)) {
221
+ throw new Error('fetchMarketMatches returned an unexpected response shape: missing matches array');
222
+ }
223
+ const matches = response.matches;
221
224
  return matches.map((m) => ({
222
225
  market: m.market,
223
226
  relation: m.relation,
@@ -262,14 +265,20 @@ class Router extends BaseExchange_1.PredictionMarketExchange {
262
265
  const hasIdentifier = params.eventId || params.slug;
263
266
  if (!hasIdentifier) {
264
267
  const results = await this.client.browseEventMatches(params);
265
- return Array.isArray(results) ? results : [];
268
+ if (!Array.isArray(results)) {
269
+ throw new Error(`browseEventMatches returned unexpected type '${typeof results}'`);
270
+ }
271
+ return results;
266
272
  }
267
273
  if (await this.resolveLocalMockEventLookup(params)) {
268
274
  return [];
269
275
  }
270
276
  // Lookup mode: find matches for a specific event.
271
277
  const response = await this.client.getEventMatches(params);
272
- return response.matches ?? [];
278
+ if (!response || !Array.isArray(response.matches)) {
279
+ throw new Error('fetchEventMatches returned an unexpected response shape: missing matches array');
280
+ }
281
+ return response.matches;
273
282
  }
274
283
  // -----------------------------------------------------------------------
275
284
  // Price comparison: identity matches with live prices
@@ -134,6 +134,80 @@ function queryToArgs(query, spec) {
134
134
  }
135
135
  return args;
136
136
  }
137
+ function isRecord(value) {
138
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
139
+ }
140
+ function toDateParam(value, field, exchange) {
141
+ if (value instanceof Date) {
142
+ if (Number.isNaN(value.getTime())) {
143
+ throw new errors_1.ValidationError(`${field} must be a valid date-time value.`, field, exchange);
144
+ }
145
+ return value;
146
+ }
147
+ if (typeof value === "string" || typeof value === "number") {
148
+ const parsed = new Date(value);
149
+ if (!Number.isNaN(parsed.getTime())) {
150
+ return parsed;
151
+ }
152
+ }
153
+ throw new errors_1.ValidationError(`${field} must be a valid date-time string or timestamp.`, field, exchange);
154
+ }
155
+ function normalizeDateFields(params, fields, exchange) {
156
+ const normalized = { ...params };
157
+ for (const field of fields) {
158
+ const value = normalized[field];
159
+ if (value === undefined || value === null || value === "")
160
+ continue;
161
+ normalized[field] = toDateParam(value, field, exchange);
162
+ }
163
+ return normalized;
164
+ }
165
+ function isMissing(value) {
166
+ return value === undefined || value === null || value === "";
167
+ }
168
+ function assertFiniteNumber(params, field, exchange) {
169
+ const value = params[field];
170
+ if (typeof value !== "number" || !Number.isFinite(value)) {
171
+ throw new errors_1.ValidationError(`createOrder params.${field} must be a finite number.`, field, exchange);
172
+ }
173
+ }
174
+ function validateCreateOrderParams(params, exchange) {
175
+ if (!isRecord(params)) {
176
+ throw new errors_1.ValidationError("createOrder requires an order parameter object.", "params", exchange);
177
+ }
178
+ const required = ["marketId", "outcomeId", "side", "type", "amount"];
179
+ const missing = required.filter((field) => isMissing(params[field]));
180
+ if (missing.length > 0) {
181
+ throw new errors_1.ValidationError(`createOrder requires ${missing.map((field) => `params.${field}`).join(", ")}.`, missing[0], exchange);
182
+ }
183
+ if (params.side !== "buy" && params.side !== "sell") {
184
+ throw new errors_1.ValidationError('createOrder params.side must be "buy" or "sell".', "side", exchange);
185
+ }
186
+ if (params.type !== "market" && params.type !== "limit") {
187
+ throw new errors_1.ValidationError('createOrder params.type must be "market" or "limit".', "type", exchange);
188
+ }
189
+ assertFiniteNumber(params, "amount", exchange);
190
+ if (params.type === "limit" && isMissing(params.price)) {
191
+ throw new errors_1.ValidationError("createOrder params.price is required for limit orders.", "price", exchange);
192
+ }
193
+ if (!isMissing(params.price)) {
194
+ assertFiniteNumber(params, "price", exchange);
195
+ }
196
+ if (!isMissing(params.fee)) {
197
+ assertFiniteNumber(params, "fee", exchange);
198
+ }
199
+ return params;
200
+ }
201
+ function normalizeDispatchArgs(methodName, args, exchange) {
202
+ const normalized = [...args];
203
+ if (methodName === "fetchOHLCV" && isRecord(normalized[1])) {
204
+ normalized[1] = normalizeDateFields(normalized[1], ["start", "end"], exchange);
205
+ }
206
+ if (methodName === "createOrder") {
207
+ normalized[0] = validateCreateOrderParams(normalized[0], exchange);
208
+ }
209
+ return normalized;
210
+ }
137
211
  // Singleton instances for local usage (when no credentials provided)
138
212
  const defaultExchanges = {
139
213
  polymarket: null,
@@ -275,7 +349,8 @@ function createApp(options = {}) {
275
349
  });
276
350
  return;
277
351
  }
278
- const result = await exchange[methodName](...args);
352
+ const normalizedArgs = normalizeDispatchArgs(methodName, args, exchangeName);
353
+ const result = await exchange[methodName](...normalizedArgs);
279
354
  res.json({ success: true, data: result });
280
355
  }
281
356
  catch (error) {
@@ -14,6 +14,7 @@ const smarkets_1 = require("../exchanges/smarkets");
14
14
  const polymarket_us_1 = require("../exchanges/polymarket_us");
15
15
  const hyperliquid_1 = require("../exchanges/hyperliquid");
16
16
  const gemini_titan_1 = require("../exchanges/gemini-titan");
17
+ const suibets_1 = require("../exchanges/suibets");
17
18
  const mock_1 = require("../exchanges/mock");
18
19
  const router_1 = require("../router");
19
20
  function createExchange(name, credentials, bearerToken) {
@@ -103,6 +104,11 @@ function createExchange(name, credentials, bearerToken) {
103
104
  apiKey: credentials?.apiKey || process.env.GEMINI_API_KEY,
104
105
  apiSecret: credentials?.apiSecret || process.env.GEMINI_API_SECRET,
105
106
  });
107
+ case "suibets":
108
+ return new suibets_1.SuiBetsExchange({
109
+ walletAddress: credentials?.walletAddress || process.env.SUIBETS_WALLET_ADDRESS,
110
+ baseUrl: credentials?.baseUrl || process.env.SUIBETS_BASE_URL,
111
+ });
106
112
  case "mock":
107
113
  return new mock_1.MockExchange();
108
114
  case "router":
@@ -4159,6 +4159,13 @@ x-sdk-constructors:
4159
4159
  tsName: apiSecret
4160
4160
  type: string
4161
4161
  description: API secret for authentication
4162
+ suibets:
4163
+ className: Suibets
4164
+ params:
4165
+ - name: pmxt_api_key
4166
+ tsName: pmxtApiKey
4167
+ type: string
4168
+ description: PMXT API key for hosted access
4162
4169
  mock:
4163
4170
  className: Mock
4164
4171
  params: