pmxt-core 2.4.0 → 2.5.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 (58) hide show
  1. package/dist/BaseExchange.d.ts +38 -0
  2. package/dist/BaseExchange.js +50 -2
  3. package/dist/errors.d.ts +6 -0
  4. package/dist/errors.js +10 -1
  5. package/dist/exchanges/baozi/auth.d.ts +14 -0
  6. package/dist/exchanges/baozi/auth.js +33 -0
  7. package/dist/exchanges/baozi/errors.d.ts +11 -0
  8. package/dist/exchanges/baozi/errors.js +70 -0
  9. package/dist/exchanges/baozi/fetchEvents.d.ts +8 -0
  10. package/dist/exchanges/baozi/fetchEvents.js +34 -0
  11. package/dist/exchanges/baozi/fetchMarkets.d.ts +5 -0
  12. package/dist/exchanges/baozi/fetchMarkets.js +160 -0
  13. package/dist/exchanges/baozi/fetchOHLCV.d.ts +6 -0
  14. package/dist/exchanges/baozi/fetchOHLCV.js +10 -0
  15. package/dist/exchanges/baozi/fetchOrderBook.d.ts +12 -0
  16. package/dist/exchanges/baozi/fetchOrderBook.js +36 -0
  17. package/dist/exchanges/baozi/fetchTrades.d.ts +6 -0
  18. package/dist/exchanges/baozi/fetchTrades.js +10 -0
  19. package/dist/exchanges/baozi/index.d.ts +28 -0
  20. package/dist/exchanges/baozi/index.js +404 -0
  21. package/dist/exchanges/baozi/utils.d.ts +112 -0
  22. package/dist/exchanges/baozi/utils.js +446 -0
  23. package/dist/exchanges/baozi/websocket.d.ts +14 -0
  24. package/dist/exchanges/baozi/websocket.js +78 -0
  25. package/dist/exchanges/kalshi/fetchEvents.js +39 -0
  26. package/dist/exchanges/kalshi/fetchMarkets.js +13 -0
  27. package/dist/exchanges/limitless/fetchEvents.js +69 -0
  28. package/dist/exchanges/limitless/fetchMarkets.js +13 -0
  29. package/dist/exchanges/myriad/auth.d.ts +8 -0
  30. package/dist/exchanges/myriad/auth.js +25 -0
  31. package/dist/exchanges/myriad/errors.d.ts +8 -0
  32. package/dist/exchanges/myriad/errors.js +33 -0
  33. package/dist/exchanges/myriad/fetchEvents.d.ts +3 -0
  34. package/dist/exchanges/myriad/fetchEvents.js +48 -0
  35. package/dist/exchanges/myriad/fetchMarkets.d.ts +3 -0
  36. package/dist/exchanges/myriad/fetchMarkets.js +102 -0
  37. package/dist/exchanges/myriad/fetchOHLCV.d.ts +3 -0
  38. package/dist/exchanges/myriad/fetchOHLCV.js +91 -0
  39. package/dist/exchanges/myriad/fetchOrderBook.d.ts +2 -0
  40. package/dist/exchanges/myriad/fetchOrderBook.js +47 -0
  41. package/dist/exchanges/myriad/fetchTrades.d.ts +3 -0
  42. package/dist/exchanges/myriad/fetchTrades.js +62 -0
  43. package/dist/exchanges/myriad/index.d.ts +24 -0
  44. package/dist/exchanges/myriad/index.js +209 -0
  45. package/dist/exchanges/myriad/utils.d.ts +15 -0
  46. package/dist/exchanges/myriad/utils.js +99 -0
  47. package/dist/exchanges/myriad/websocket.d.ts +17 -0
  48. package/dist/exchanges/myriad/websocket.js +105 -0
  49. package/dist/exchanges/polymarket/fetchEvents.js +72 -3
  50. package/dist/exchanges/polymarket/fetchMarkets.js +52 -0
  51. package/dist/exchanges/probable/fetchEvents.js +10 -0
  52. package/dist/exchanges/probable/fetchMarkets.js +33 -3
  53. package/dist/index.d.ts +8 -0
  54. package/dist/index.js +10 -2
  55. package/dist/server/app.js +14 -1
  56. package/dist/server/utils/lock-file.d.ts +1 -0
  57. package/dist/server/utils/lock-file.js +28 -8
  58. package/package.json +5 -3
@@ -0,0 +1,209 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.MyriadExchange = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ const BaseExchange_1 = require("../../BaseExchange");
9
+ const fetchMarkets_1 = require("./fetchMarkets");
10
+ const fetchEvents_1 = require("./fetchEvents");
11
+ const fetchOHLCV_1 = require("./fetchOHLCV");
12
+ const fetchOrderBook_1 = require("./fetchOrderBook");
13
+ const fetchTrades_1 = require("./fetchTrades");
14
+ const auth_1 = require("./auth");
15
+ const websocket_1 = require("./websocket");
16
+ const errors_1 = require("./errors");
17
+ const errors_2 = require("../../errors");
18
+ const utils_1 = require("./utils");
19
+ class MyriadExchange extends BaseExchange_1.PredictionMarketExchange {
20
+ auth;
21
+ ws;
22
+ constructor(credentials) {
23
+ super(credentials);
24
+ if (credentials?.apiKey) {
25
+ this.auth = new auth_1.MyriadAuth(credentials);
26
+ }
27
+ }
28
+ get name() {
29
+ return 'Myriad';
30
+ }
31
+ getHeaders() {
32
+ if (this.auth) {
33
+ return this.auth.getHeaders();
34
+ }
35
+ return { 'Content-Type': 'application/json' };
36
+ }
37
+ ensureAuth() {
38
+ if (!this.auth) {
39
+ throw new errors_2.AuthenticationError('This operation requires authentication. Initialize MyriadExchange with credentials (apiKey).', 'Myriad');
40
+ }
41
+ return this.auth;
42
+ }
43
+ // ------------------------------------------------------------------------
44
+ // Market Data
45
+ // ------------------------------------------------------------------------
46
+ async fetchMarketsImpl(params) {
47
+ return (0, fetchMarkets_1.fetchMarkets)(params, this.getHeaders());
48
+ }
49
+ async fetchEventsImpl(params) {
50
+ return (0, fetchEvents_1.fetchEvents)(params, this.getHeaders());
51
+ }
52
+ async fetchOHLCV(id, params) {
53
+ return (0, fetchOHLCV_1.fetchOHLCV)(id, params, this.getHeaders());
54
+ }
55
+ async fetchOrderBook(id) {
56
+ return (0, fetchOrderBook_1.fetchOrderBook)(id, this.getHeaders());
57
+ }
58
+ async fetchTrades(id, params) {
59
+ if ('resolution' in params && params.resolution !== undefined) {
60
+ console.warn('[pmxt] Warning: The "resolution" parameter is deprecated for fetchTrades() and will be ignored. ' +
61
+ 'It will be removed in v3.0.0. Please remove it from your code.');
62
+ }
63
+ return (0, fetchTrades_1.fetchTrades)(id, params, this.getHeaders());
64
+ }
65
+ // ------------------------------------------------------------------------
66
+ // Trading
67
+ // ------------------------------------------------------------------------
68
+ async createOrder(params) {
69
+ try {
70
+ const auth = this.ensureAuth();
71
+ const headers = auth.getHeaders();
72
+ // Parse composite marketId: {networkId}:{marketId}
73
+ const parts = params.marketId.split(':');
74
+ if (parts.length < 2) {
75
+ throw new Error(`Invalid marketId format: "${params.marketId}". Expected "{networkId}:{marketId}".`);
76
+ }
77
+ const [networkId, marketId] = parts;
78
+ // Parse outcomeId: {networkId}:{marketId}:{outcomeId}
79
+ const outcomeParts = params.outcomeId.split(':');
80
+ const outcomeId = outcomeParts.length >= 3 ? Number(outcomeParts[2]) : Number(outcomeParts[0]);
81
+ const quoteBody = {
82
+ market_id: Number(marketId),
83
+ outcome_id: outcomeId,
84
+ network_id: Number(networkId),
85
+ action: params.side,
86
+ };
87
+ if (params.side === 'buy') {
88
+ quoteBody.value = params.amount;
89
+ }
90
+ else {
91
+ quoteBody.shares = params.amount;
92
+ }
93
+ if (params.price) {
94
+ // Use price as slippage tolerance for AMM
95
+ quoteBody.slippage = 0.01;
96
+ }
97
+ const response = await axios_1.default.post(`${utils_1.BASE_URL}/markets/quote`, quoteBody, { headers });
98
+ const quote = response.data;
99
+ return {
100
+ id: `myriad-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
101
+ marketId: params.marketId,
102
+ outcomeId: params.outcomeId,
103
+ side: params.side,
104
+ type: 'market', // AMM only supports market orders
105
+ price: quote.price_average,
106
+ amount: params.side === 'buy' ? quote.value : quote.shares,
107
+ status: 'pending',
108
+ filled: 0,
109
+ remaining: params.side === 'buy' ? quote.value : quote.shares,
110
+ timestamp: Date.now(),
111
+ fee: quote.fees ? (quote.fees.fee + quote.fees.treasury + quote.fees.distributor) : undefined,
112
+ };
113
+ }
114
+ catch (error) {
115
+ throw errors_1.myriadErrorMapper.mapError(error);
116
+ }
117
+ }
118
+ async cancelOrder(_orderId) {
119
+ throw new Error('cancelOrder() is not supported by Myriad (AMM-based exchange, no open orders)');
120
+ }
121
+ async fetchOrder(_orderId) {
122
+ throw new Error('fetchOrder() is not supported by Myriad (AMM-based exchange)');
123
+ }
124
+ async fetchOpenOrders(_marketId) {
125
+ return []; // AMM: no open orders
126
+ }
127
+ async fetchPositions() {
128
+ try {
129
+ const auth = this.ensureAuth();
130
+ const headers = auth.getHeaders();
131
+ const walletAddress = auth.walletAddress;
132
+ if (!walletAddress) {
133
+ throw new errors_2.AuthenticationError('fetchPositions requires a wallet address. Pass privateKey as the wallet address in credentials.', 'Myriad');
134
+ }
135
+ const response = await axios_1.default.get(`${utils_1.BASE_URL}/users/${walletAddress}/portfolio`, {
136
+ params: { limit: 100 },
137
+ headers,
138
+ });
139
+ const items = response.data.data || response.data.items || [];
140
+ return items.map((pos) => ({
141
+ marketId: `${pos.networkId}:${pos.marketId}`,
142
+ outcomeId: `${pos.networkId}:${pos.marketId}:${pos.outcomeId}`,
143
+ outcomeLabel: pos.outcomeTitle || `Outcome ${pos.outcomeId}`,
144
+ size: Number(pos.shares || 0),
145
+ entryPrice: Number(pos.price || 0),
146
+ currentPrice: Number(pos.value || 0) / Math.max(Number(pos.shares || 1), 1),
147
+ unrealizedPnL: Number(pos.profit || 0),
148
+ }));
149
+ }
150
+ catch (error) {
151
+ throw errors_1.myriadErrorMapper.mapError(error);
152
+ }
153
+ }
154
+ async fetchBalance() {
155
+ // Myriad is on-chain; balances are per-chain token balances.
156
+ // The API doesn't expose a balance endpoint directly.
157
+ // We approximate from portfolio positions.
158
+ try {
159
+ const auth = this.ensureAuth();
160
+ const headers = auth.getHeaders();
161
+ const walletAddress = auth.walletAddress;
162
+ if (!walletAddress) {
163
+ throw new errors_2.AuthenticationError('fetchBalance requires a wallet address. Pass privateKey as the wallet address in credentials.', 'Myriad');
164
+ }
165
+ const response = await axios_1.default.get(`${utils_1.BASE_URL}/users/${walletAddress}/portfolio`, {
166
+ params: { limit: 100 },
167
+ headers,
168
+ });
169
+ const items = response.data.data || response.data.items || [];
170
+ let totalValue = 0;
171
+ for (const pos of items) {
172
+ totalValue += Number(pos.value || 0);
173
+ }
174
+ return [{
175
+ currency: 'USDC',
176
+ total: totalValue,
177
+ available: 0, // Cannot determine on-chain balance via API
178
+ locked: totalValue,
179
+ }];
180
+ }
181
+ catch (error) {
182
+ throw errors_1.myriadErrorMapper.mapError(error);
183
+ }
184
+ }
185
+ // ------------------------------------------------------------------------
186
+ // WebSocket (poll-based)
187
+ // ------------------------------------------------------------------------
188
+ async watchOrderBook(id, _limit) {
189
+ this.ensureAuth();
190
+ if (!this.ws) {
191
+ this.ws = new websocket_1.MyriadWebSocket(this.getHeaders());
192
+ }
193
+ return this.ws.watchOrderBook(id);
194
+ }
195
+ async watchTrades(id, _since, _limit) {
196
+ this.ensureAuth();
197
+ if (!this.ws) {
198
+ this.ws = new websocket_1.MyriadWebSocket(this.getHeaders());
199
+ }
200
+ return this.ws.watchTrades(id);
201
+ }
202
+ async close() {
203
+ if (this.ws) {
204
+ await this.ws.close();
205
+ this.ws = undefined;
206
+ }
207
+ }
208
+ }
209
+ exports.MyriadExchange = MyriadExchange;
@@ -0,0 +1,15 @@
1
+ import { UnifiedMarket, UnifiedEvent } from '../../types';
2
+ export declare const BASE_URL = "https://api-v2.myriadprotocol.com";
3
+ export declare const NETWORKS: {
4
+ readonly ABSTRACT: 2741;
5
+ readonly LINEA: 59144;
6
+ readonly BNB: 56;
7
+ };
8
+ export declare const CONTRACTS: Record<number, {
9
+ predictionMarket: string;
10
+ querier: string;
11
+ }>;
12
+ export declare function mapMarketState(state: string): 'active' | 'inactive' | 'closed';
13
+ export declare function mapStatusToMyriad(status?: string): string | undefined;
14
+ export declare function mapMarketToUnified(market: any): UnifiedMarket | null;
15
+ export declare function mapQuestionToEvent(question: any): UnifiedEvent | null;
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CONTRACTS = exports.NETWORKS = exports.BASE_URL = void 0;
4
+ exports.mapMarketState = mapMarketState;
5
+ exports.mapStatusToMyriad = mapStatusToMyriad;
6
+ exports.mapMarketToUnified = mapMarketToUnified;
7
+ exports.mapQuestionToEvent = mapQuestionToEvent;
8
+ const market_utils_1 = require("../../utils/market-utils");
9
+ exports.BASE_URL = 'https://api-v2.myriadprotocol.com';
10
+ // Mainnet network IDs
11
+ exports.NETWORKS = {
12
+ ABSTRACT: 2741,
13
+ LINEA: 59144,
14
+ BNB: 56,
15
+ };
16
+ // Mainnet contract addresses
17
+ exports.CONTRACTS = {
18
+ [exports.NETWORKS.ABSTRACT]: {
19
+ predictionMarket: '0x3e0F5F8F5Fb043aBFA475C0308417Bf72c463289',
20
+ querier: '0x1d5773Cd0dC74744C1F7a19afEeECfFE64f233Ff',
21
+ },
22
+ [exports.NETWORKS.LINEA]: {
23
+ predictionMarket: '0x39e66ee6b2ddaf4defded3038e0162180dbef340',
24
+ querier: '0x503c9f98398dc3433ABa819BF3eC0b97e02B8D04',
25
+ },
26
+ [exports.NETWORKS.BNB]: {
27
+ predictionMarket: '0x39E66eE6b2ddaf4DEfDEd3038E0162180dbeF340',
28
+ querier: '0xDeFb36c47754D2e37d44b8b8C647D4D643e03bAd',
29
+ },
30
+ };
31
+ function mapMarketState(state) {
32
+ switch (state) {
33
+ case 'open':
34
+ return 'active';
35
+ case 'closed':
36
+ return 'inactive';
37
+ case 'resolved':
38
+ return 'closed';
39
+ default:
40
+ return 'active';
41
+ }
42
+ }
43
+ function mapStatusToMyriad(status) {
44
+ if (!status)
45
+ return undefined;
46
+ switch (status) {
47
+ case 'active':
48
+ return 'open';
49
+ case 'inactive':
50
+ case 'closed':
51
+ return 'closed';
52
+ default:
53
+ return undefined;
54
+ }
55
+ }
56
+ function mapMarketToUnified(market) {
57
+ if (!market)
58
+ return null;
59
+ const outcomes = (market.outcomes || []).map((o) => ({
60
+ outcomeId: `${market.networkId}:${market.id}:${o.id}`,
61
+ marketId: `${market.networkId}:${market.id}`,
62
+ label: o.title || `Outcome ${o.id}`,
63
+ price: Number(o.price) || 0,
64
+ priceChange24h: o.priceChange24h != null ? Number(o.priceChange24h) : undefined,
65
+ }));
66
+ const um = {
67
+ marketId: `${market.networkId}:${market.id}`,
68
+ title: market.title || '',
69
+ description: market.description || '',
70
+ outcomes,
71
+ resolutionDate: market.expiresAt ? new Date(market.expiresAt) : new Date(0),
72
+ volume24h: Number(market.volume24h || 0),
73
+ volume: Number(market.volume || 0),
74
+ liquidity: Number(market.liquidity || 0),
75
+ url: `https://myriad.markets/markets/${market.slug || market.id}`,
76
+ image: market.imageUrl,
77
+ tags: market.topics || [],
78
+ };
79
+ (0, market_utils_1.addBinaryOutcomes)(um);
80
+ return um;
81
+ }
82
+ function mapQuestionToEvent(question) {
83
+ if (!question)
84
+ return null;
85
+ const markets = [];
86
+ for (const m of question.markets || []) {
87
+ const um = mapMarketToUnified(m);
88
+ if (um)
89
+ markets.push(um);
90
+ }
91
+ return {
92
+ id: String(question.id),
93
+ title: question.title || '',
94
+ description: '',
95
+ slug: String(question.id),
96
+ markets,
97
+ url: `https://myriad.markets`,
98
+ };
99
+ }
@@ -0,0 +1,17 @@
1
+ import { OrderBook, Trade } from '../../types';
2
+ export declare class MyriadWebSocket {
3
+ private headers;
4
+ private pollInterval;
5
+ private orderBookTimers;
6
+ private tradeTimers;
7
+ private orderBookResolvers;
8
+ private tradeResolvers;
9
+ private lastTradeTimestamp;
10
+ private closed;
11
+ constructor(headers: Record<string, string>, pollInterval?: number);
12
+ watchOrderBook(id: string): Promise<OrderBook>;
13
+ watchTrades(id: string): Promise<Trade[]>;
14
+ close(): Promise<void>;
15
+ private startOrderBookPolling;
16
+ private startTradePolling;
17
+ }
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MyriadWebSocket = void 0;
4
+ const fetchOrderBook_1 = require("./fetchOrderBook");
5
+ const fetchTrades_1 = require("./fetchTrades");
6
+ // Myriad API v2 does not expose a WebSocket endpoint.
7
+ // We implement a poll-based fallback that resolves promises
8
+ // on each polling interval, matching the CCXT Pro async pattern.
9
+ const DEFAULT_POLL_INTERVAL = 5000; // 5 seconds
10
+ class MyriadWebSocket {
11
+ headers;
12
+ pollInterval;
13
+ orderBookTimers = new Map();
14
+ tradeTimers = new Map();
15
+ orderBookResolvers = new Map();
16
+ tradeResolvers = new Map();
17
+ lastTradeTimestamp = new Map();
18
+ closed = false;
19
+ constructor(headers, pollInterval) {
20
+ this.headers = headers;
21
+ this.pollInterval = pollInterval || DEFAULT_POLL_INTERVAL;
22
+ }
23
+ async watchOrderBook(id) {
24
+ if (this.closed)
25
+ throw new Error('WebSocket connection is closed');
26
+ return new Promise((resolve) => {
27
+ if (!this.orderBookResolvers.has(id)) {
28
+ this.orderBookResolvers.set(id, []);
29
+ }
30
+ this.orderBookResolvers.get(id).push(resolve);
31
+ if (!this.orderBookTimers.has(id)) {
32
+ this.startOrderBookPolling(id);
33
+ }
34
+ });
35
+ }
36
+ async watchTrades(id) {
37
+ if (this.closed)
38
+ throw new Error('WebSocket connection is closed');
39
+ return new Promise((resolve) => {
40
+ if (!this.tradeResolvers.has(id)) {
41
+ this.tradeResolvers.set(id, []);
42
+ }
43
+ this.tradeResolvers.get(id).push(resolve);
44
+ if (!this.tradeTimers.has(id)) {
45
+ this.startTradePolling(id);
46
+ }
47
+ });
48
+ }
49
+ async close() {
50
+ this.closed = true;
51
+ for (const timer of this.orderBookTimers.values()) {
52
+ clearInterval(timer);
53
+ }
54
+ for (const timer of this.tradeTimers.values()) {
55
+ clearInterval(timer);
56
+ }
57
+ this.orderBookTimers.clear();
58
+ this.tradeTimers.clear();
59
+ this.orderBookResolvers.clear();
60
+ this.tradeResolvers.clear();
61
+ }
62
+ startOrderBookPolling(id) {
63
+ const poll = async () => {
64
+ try {
65
+ const book = await (0, fetchOrderBook_1.fetchOrderBook)(id, this.headers);
66
+ const resolvers = this.orderBookResolvers.get(id) || [];
67
+ this.orderBookResolvers.set(id, []);
68
+ for (const resolve of resolvers) {
69
+ resolve(book);
70
+ }
71
+ }
72
+ catch {
73
+ // Silently retry on next interval
74
+ }
75
+ };
76
+ // Immediate first poll
77
+ poll();
78
+ const timer = setInterval(poll, this.pollInterval);
79
+ this.orderBookTimers.set(id, timer);
80
+ }
81
+ startTradePolling(id) {
82
+ const poll = async () => {
83
+ try {
84
+ const since = this.lastTradeTimestamp.get(id);
85
+ const trades = await (0, fetchTrades_1.fetchTrades)(id, { limit: 50, start: since ? new Date(since) : undefined }, this.headers);
86
+ if (trades.length > 0) {
87
+ const maxTs = Math.max(...trades.map(t => t.timestamp));
88
+ this.lastTradeTimestamp.set(id, maxTs + 1);
89
+ }
90
+ const resolvers = this.tradeResolvers.get(id) || [];
91
+ this.tradeResolvers.set(id, []);
92
+ for (const resolve of resolvers) {
93
+ resolve(trades);
94
+ }
95
+ }
96
+ catch {
97
+ // Silently retry on next interval
98
+ }
99
+ };
100
+ poll();
101
+ const timer = setInterval(poll, this.pollInterval);
102
+ this.tradeTimers.set(id, timer);
103
+ }
104
+ }
105
+ exports.MyriadWebSocket = MyriadWebSocket;
@@ -1,14 +1,83 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.fetchEvents = fetchEvents;
7
+ const axios_1 = __importDefault(require("axios"));
4
8
  const utils_1 = require("./utils");
5
9
  const errors_1 = require("./errors");
10
+ async function fetchEventByGammaId(id) {
11
+ const response = await axios_1.default.get(utils_1.GAMMA_API_URL, {
12
+ params: { id }
13
+ });
14
+ const events = response.data;
15
+ if (!events || events.length === 0)
16
+ return [];
17
+ return events.map((event) => {
18
+ const markets = [];
19
+ if (event.markets && Array.isArray(event.markets)) {
20
+ for (const market of event.markets) {
21
+ const unifiedMarket = (0, utils_1.mapMarketToUnified)(event, market, { useQuestionAsCandidateFallback: true });
22
+ if (unifiedMarket) {
23
+ markets.push(unifiedMarket);
24
+ }
25
+ }
26
+ }
27
+ return {
28
+ id: event.id || event.slug,
29
+ title: event.title,
30
+ description: event.description || '',
31
+ slug: event.slug,
32
+ markets,
33
+ url: `https://polymarket.com/event/${event.slug}`,
34
+ image: event.image || `https://polymarket.com/api/og?slug=${event.slug}`,
35
+ category: event.category || event.tags?.[0]?.label,
36
+ tags: event.tags?.map((t) => t.label) || []
37
+ };
38
+ });
39
+ }
40
+ async function fetchEventBySlug(slug) {
41
+ const response = await axios_1.default.get(utils_1.GAMMA_API_URL, {
42
+ params: { slug }
43
+ });
44
+ const events = response.data;
45
+ if (!events || events.length === 0)
46
+ return [];
47
+ return events.map((event) => {
48
+ const markets = [];
49
+ if (event.markets && Array.isArray(event.markets)) {
50
+ for (const market of event.markets) {
51
+ const unifiedMarket = (0, utils_1.mapMarketToUnified)(event, market, { useQuestionAsCandidateFallback: true });
52
+ if (unifiedMarket) {
53
+ markets.push(unifiedMarket);
54
+ }
55
+ }
56
+ }
57
+ return {
58
+ id: event.id || event.slug,
59
+ title: event.title,
60
+ description: event.description || '',
61
+ slug: event.slug,
62
+ markets,
63
+ url: `https://polymarket.com/event/${event.slug}`,
64
+ image: event.image || `https://polymarket.com/api/og?slug=${event.slug}`,
65
+ category: event.category || event.tags?.[0]?.label,
66
+ tags: event.tags?.map((t) => t.label) || []
67
+ };
68
+ });
69
+ }
6
70
  async function fetchEvents(params) {
7
71
  try {
72
+ // Handle eventId lookup (Gamma event ID)
73
+ if (params.eventId) {
74
+ return await fetchEventByGammaId(params.eventId);
75
+ }
76
+ // Handle slug lookup
77
+ if (params.slug) {
78
+ return await fetchEventBySlug(params.slug);
79
+ }
8
80
  if (!params.query) {
9
- // If no query is provided, we can't use the search endpoint effectively.
10
- // However, the BaseExchange interface enforces query presence for fetchEvents.
11
- // Just in case, we return empty or throw.
12
81
  throw new Error("Query is required for Polymarket event search");
13
82
  }
14
83
  const limit = params.limit || 10000;
@@ -9,10 +9,23 @@ const utils_1 = require("./utils");
9
9
  const errors_1 = require("./errors");
10
10
  async function fetchMarkets(params) {
11
11
  try {
12
+ // Handle marketId lookup (Gamma market condition ID)
13
+ if (params?.marketId) {
14
+ return await fetchMarketById(params.marketId);
15
+ }
12
16
  // Handle slug-based lookup
13
17
  if (params?.slug) {
14
18
  return await fetchMarketsBySlug(params.slug);
15
19
  }
20
+ // Handle outcomeId lookup (CLOB token ID -- no direct API, fetch and filter)
21
+ if (params?.outcomeId) {
22
+ const markets = await fetchMarketsDefault(params);
23
+ return markets.filter(m => m.outcomes.some(o => o.outcomeId === params.outcomeId));
24
+ }
25
+ // Handle eventId lookup (Gamma event ID)
26
+ if (params?.eventId) {
27
+ return await fetchMarketsByEventId(params.eventId);
28
+ }
16
29
  // Handle query-based search
17
30
  if (params?.query) {
18
31
  return await searchMarkets(params.query, params);
@@ -24,6 +37,45 @@ async function fetchMarkets(params) {
24
37
  throw errors_1.polymarketErrorMapper.mapError(error);
25
38
  }
26
39
  }
40
+ async function fetchMarketById(marketId) {
41
+ const GAMMA_MARKETS_URL = 'https://gamma-api.polymarket.com/markets';
42
+ const response = await axios_1.default.get(GAMMA_MARKETS_URL, {
43
+ params: { id: marketId }
44
+ });
45
+ const markets = response.data;
46
+ if (!markets || markets.length === 0)
47
+ return [];
48
+ const unifiedMarkets = [];
49
+ for (const market of markets) {
50
+ // The /markets endpoint returns markets with an embedded 'events' array
51
+ const event = market.events?.[0] || market;
52
+ const unifiedMarket = (0, utils_1.mapMarketToUnified)(event, market, { useQuestionAsCandidateFallback: true });
53
+ if (unifiedMarket) {
54
+ unifiedMarkets.push(unifiedMarket);
55
+ }
56
+ }
57
+ return unifiedMarkets;
58
+ }
59
+ async function fetchMarketsByEventId(eventId) {
60
+ const response = await axios_1.default.get(utils_1.GAMMA_API_URL, {
61
+ params: { id: eventId }
62
+ });
63
+ const events = response.data;
64
+ if (!events || events.length === 0)
65
+ return [];
66
+ const unifiedMarkets = [];
67
+ for (const event of events) {
68
+ if (!event.markets)
69
+ continue;
70
+ for (const market of event.markets) {
71
+ const unifiedMarket = (0, utils_1.mapMarketToUnified)(event, market, { useQuestionAsCandidateFallback: true });
72
+ if (unifiedMarket) {
73
+ unifiedMarkets.push(unifiedMarket);
74
+ }
75
+ }
76
+ }
77
+ return unifiedMarkets;
78
+ }
27
79
  async function fetchMarketsBySlug(slug) {
28
80
  const response = await axios_1.default.get(utils_1.GAMMA_API_URL, {
29
81
  params: { slug: slug }
@@ -11,6 +11,16 @@ const utils_1 = require("./utils");
11
11
  const errors_1 = require("./errors");
12
12
  async function fetchEvents(params) {
13
13
  try {
14
+ // Handle eventId lookup
15
+ if (params.eventId) {
16
+ const event = await fetchEventById(params.eventId);
17
+ return event ? [event] : [];
18
+ }
19
+ // Handle slug lookup
20
+ if (params.slug) {
21
+ const event = await fetchEventBySlug(params.slug);
22
+ return event ? [event] : [];
23
+ }
14
24
  // Query-based search: use the search endpoint (only endpoint with text search)
15
25
  if (params.query) {
16
26
  return await searchEvents(params);
@@ -9,10 +9,23 @@ const utils_1 = require("./utils");
9
9
  const errors_1 = require("./errors");
10
10
  async function fetchMarkets(params) {
11
11
  try {
12
+ // Handle marketId lookup (numeric ID or slug)
13
+ if (params?.marketId) {
14
+ return await fetchMarketByIdOrSlug(params.marketId);
15
+ }
12
16
  // Slug-based lookup: try market ID or slug via dedicated endpoint
13
17
  if (params?.slug) {
14
18
  return await fetchMarketByIdOrSlug(params.slug);
15
19
  }
20
+ // Handle outcomeId lookup (no direct API, fetch and filter client-side)
21
+ if (params?.outcomeId) {
22
+ const markets = await fetchMarketsList(params);
23
+ return markets.filter(m => m.outcomes.some(o => o.outcomeId === params.outcomeId));
24
+ }
25
+ // Handle eventId lookup (use markets list with eventId param)
26
+ if (params?.eventId) {
27
+ return await fetchMarketsList(params);
28
+ }
16
29
  // Query-based search: use the search endpoint (only endpoint with text search)
17
30
  if (params?.query) {
18
31
  return await searchAndExtractMarkets(params.query, params);
@@ -57,10 +70,16 @@ async function fetchMarketByIdOrSlug(slug) {
57
70
  return results;
58
71
  }
59
72
  catch (error) {
60
- if (error.response?.status === 404) {
61
- return [];
73
+ if (isMarketNotFoundError(error)) {
74
+ // Individual market endpoint returned 500/404; fall back to list and filter
75
+ const allMarkets = await fetchMarketsList({ limit: 100 });
76
+ const match = allMarkets.filter(m => m.marketId === cleanSlug);
77
+ if (match.length > 0)
78
+ return match;
79
+ }
80
+ else {
81
+ throw error;
62
82
  }
63
- throw error;
64
83
  }
65
84
  }
66
85
  // Fall back to search for slug-based matching
@@ -203,3 +222,14 @@ async function searchAndExtractMarkets(query, params) {
203
222
  await (0, utils_1.enrichMarketsWithPrices)(allMarkets);
204
223
  return allMarkets;
205
224
  }
225
+ function isMarketNotFoundError(error) {
226
+ const status = error.response?.status;
227
+ if (status === 404 || status === 400)
228
+ return true;
229
+ if (status === 500) {
230
+ const data = error.response?.data;
231
+ const msg = typeof data === 'string' ? data : (data?.detail || data?.message || '');
232
+ return /not found|failed to retrieve/i.test(String(msg));
233
+ }
234
+ return false;
235
+ }