diff --git a/core/api-doc-config.generated.json b/core/api-doc-config.generated.json index 40aaff04..4b629e6a 100644 --- a/core/api-doc-config.generated.json +++ b/core/api-doc-config.generated.json @@ -1,5 +1,5 @@ { - "_generated": "Auto-generated by extract-jsdoc.js on 2026-07-10T06:40:05.097Z. Do not edit manually.", + "_generated": "Auto-generated by extract-jsdoc.js on 2026-07-10T13:48:37.901Z. Do not edit manually.", "methods": { "has": { "summary": "HTTP verb for the endpoint (e.g. GET, POST). */", @@ -9,17 +9,114 @@ "type": "ExchangeHas", "description": "Result" }, - "source": "BaseExchange.ts:42" + "source": "BaseExchange.ts:45" }, - "implicitApi": { + "getAuthNonce": { "summary": "Override in subclasses to force specific capability values.", - "description": "Use `'emulated'` for methods backed by a non-native mechanism,\nor `false` for methods that override the base only to throw a\nbetter error message (e.g. \"pari-mutuel bets cannot be cancelled\").\n/\n protected readonly capabilityOverrides: Partial> = {};\n\n protected credentials?: ExchangeCredentials;\n // Implicit API (merged across multiple defineImplicitApi calls)\n protected apiDescriptor?: ApiDescriptor;\n private _throttler: Throttler;\n // Snapshot state for cursor-based pagination\n private _snapshotTTL: number;\n private _snapshot?: { markets: UnifiedMarket[]; takenAt: number; id: string };\n private _eventSnapshot?: { events: UnifiedEvent[]; takenAt: number; id: string };\n private apiDescriptors: ApiDescriptor[] = [];\n\n constructor(credentials?: ExchangeCredentials, options?: ExchangeOptions) {\n this.credentials = credentials;\n this._snapshotTTL = options?.snapshotTTL ?? 0;\n this.http = axios.create({\n headers: {\n 'User-Agent': `pmxt (https://github.com/pmxt-dev/pmxt)`\n },\n paramsSerializer: {\n serialize: (params) => {\n const sp = new URLSearchParams();\n for (const [k, v] of Object.entries(params)) {\n if (v === undefined || v === null) continue;\n if (Array.isArray(v)) v.forEach((x) => sp.append(k, String(x)));\n else sp.append(k, String(v));\n }\n return sp.toString();\n },\n },\n });\n this._throttler = new Throttler({\n refillRate: 1 / this._rateLimit,\n capacity: 1,\n delay: 1,\n });\n\n // Rate Limit Interceptor\n this.http.interceptors.request.use(async (config: InternalAxiosRequestConfig) => {\n if (this.enableRateLimit) {\n await this._throttler.throttle();\n }\n return config;\n });\n\n // Request Interceptor\n this.http.interceptors.request.use((config: InternalAxiosRequestConfig) => {\n if (this.verbose) {\n logger.debug(`-> ${config.method?.toUpperCase()} ${config.url}`);\n if (config.params) logger.debug('params:', { params: config.params });\n if (config.data) logger.debug('body:', { body: config.data });\n }\n return config;\n });\n\n // Response Interceptor\n this.http.interceptors.response.use(\n (response: AxiosResponse) => {\n if (this.verbose) {\n logger.debug(`<- ${response.status} ${response.statusText} ${response.config.url}`);\n }\n return response;\n },\n (error: any) => {\n if (this.verbose) {\n logger.debug(`REQUEST FAILED: ${error.config?.url}`, {\n error: error.message,\n status: error.response?.status,\n data: error.response?.data,\n });\n }\n return Promise.reject(error);\n }\n );\n }\n\n private _rateLimit: number = 1000;\n\n get rateLimit(): number {\n return this._rateLimit;\n }\n\n set rateLimit(value: number) {\n this._rateLimit = value;\n this._throttler = new Throttler({\n refillRate: 1 / value,\n capacity: 1,\n delay: 1,\n });\n }\n\n abstract get name(): string;\n\n /**\nIntrospection getter: returns info about all implicit API methods.", + "description": "Use `'emulated'` for methods backed by a non-native mechanism,\nor `false` for methods that override the base only to throw a\nbetter error message (e.g. \"pari-mutuel bets cannot be cancelled\").\n/\n protected readonly capabilityOverrides: Partial> = {};\n\n protected credentials?: ExchangeCredentials;\n // Implicit API (merged across multiple defineImplicitApi calls)\n protected apiDescriptor?: ApiDescriptor;\n private _throttler: Throttler;\n // Snapshot state for cursor-based pagination\n private _snapshotTTL: number;\n private _snapshot?: { markets: UnifiedMarket[]; takenAt: number; id: string };\n private _eventSnapshot?: { events: UnifiedEvent[]; takenAt: number; id: string };\n private apiDescriptors: ApiDescriptor[] = [];\n protected _sessions: Map = new Map();\n\n constructor(credentials?: ExchangeCredentials, options?: ExchangeOptions) {\n this.credentials = credentials;\n this._snapshotTTL = options?.snapshotTTL ?? 0;\n this.http = axios.create({\n headers: {\n 'User-Agent': `pmxt (https://github.com/pmxt-dev/pmxt)`\n },\n paramsSerializer: {\n serialize: (params) => {\n const sp = new URLSearchParams();\n for (const [k, v] of Object.entries(params)) {\n if (v === undefined || v === null) continue;\n if (Array.isArray(v)) v.forEach((x) => sp.append(k, String(x)));\n else sp.append(k, String(v));\n }\n return sp.toString();\n },\n },\n });\n this._throttler = new Throttler({\n refillRate: 1 / this._rateLimit,\n capacity: 1,\n delay: 1,\n });\n\n // Rate Limit Interceptor\n this.http.interceptors.request.use(async (config: InternalAxiosRequestConfig) => {\n if (this.enableRateLimit) {\n await this._throttler.throttle();\n }\n return config;\n });\n\n // Request Interceptor\n this.http.interceptors.request.use((config: InternalAxiosRequestConfig) => {\n if (this.verbose) {\n logger.debug(`-> ${config.method?.toUpperCase()} ${config.url}`);\n if (config.params) logger.debug('params:', { params: config.params });\n if (config.data) logger.debug('body:', { body: config.data });\n }\n return config;\n });\n\n // Response Interceptor\n this.http.interceptors.response.use(\n (response: AxiosResponse) => {\n if (this.verbose) {\n logger.debug(`<- ${response.status} ${response.statusText} ${response.config.url}`);\n }\n return response;\n },\n (error: any) => {\n if (this.verbose) {\n logger.debug(`REQUEST FAILED: ${error.config?.url}`, {\n error: error.message,\n status: error.response?.status,\n data: error.response?.data,\n });\n }\n return Promise.reject(error);\n }\n );\n }\n\n private _rateLimit: number = 1000;\n\n get rateLimit(): number {\n return this._rateLimit;\n }\n\n set rateLimit(value: number) {\n this._rateLimit = value;\n this._throttler = new Throttler({\n refillRate: 1 / value,\n capacity: 1,\n delay: 1,\n });\n }\n\n abstract get name(): string;\n\n\n \n /**\nGet a cryptographic nonce for Web3 login.", + "params": [ + { + "name": "walletAddress", + "type": "string", + "optional": false, + "description": "The wallet address to authenticate" + } + ], + "returns": { + "type": "AuthNonceResponse", + "description": "Result" + }, + "source": "BaseExchange.ts:462" + }, + "loginWithSignature": { + "summary": "Login with a signed nonce to obtain session credentials.", + "description": "Login with a signed nonce to obtain session credentials.", + "params": [ + { + "name": "walletAddress", + "type": "string", + "optional": false, + "description": "The wallet address" + }, + { + "name": "signature", + "type": "string", + "optional": false, + "description": "The signature of the nonce message" + }, + { + "name": "nonce", + "type": "string", + "optional": false, + "description": "The nonce that was signed" + } + ], + "returns": { + "type": "AuthLoginResponse", + "description": "Result" + }, + "source": "BaseExchange.ts:572" + }, + "logout": { + "summary": "Logout and invalidate the current session.", + "description": "Logout and invalidate the current session.", + "params": [ + { + "name": "walletAddress", + "type": "string", + "optional": true, + "description": "The wallet address to logout (optional)" + } + ], + "returns": { + "type": "void", + "description": "Result" + }, + "source": "BaseExchange.ts:586" + }, + "isSessionActive": { + "summary": "Check if a session is active for the given wallet address.", + "description": "Check if a session is active for the given wallet address.", + "params": [ + { + "name": "walletAddress", + "type": "string", + "optional": false, + "description": "The wallet address to check" + } + ], + "returns": { + "type": "boolean", + "description": "Result" + }, + "source": "BaseExchange.ts:594" + }, + "getSession": { + "summary": "Get the stored session for a wallet address.", + "description": "Get the stored session for a wallet address.", + "params": [ + { + "name": "walletAddress", + "type": "string", + "optional": false, + "description": "The wallet address" + } + ], + "returns": { + "type": "AuthSession | undefined", + "description": "Result" + }, + "source": "BaseExchange.ts:608" + }, + "implicitApi": { + "summary": "Introspection getter: returns info about all implicit API methods.", + "description": "Introspection getter: returns info about all implicit API methods.", "params": [], "returns": { "type": "ImplicitApiMethodInfo[]", "description": "Result" }, - "source": "BaseExchange.ts:459" + "source": "BaseExchange.ts:651" }, "loadMarkets": { "summary": "Load and cache all markets from the exchange into `this.markets` and `this.marketsBySlug`.", @@ -36,7 +133,7 @@ "type": "Record", "description": "Dictionary of markets indexed by marketId" }, - "source": "BaseExchange.ts:572" + "source": "BaseExchange.ts:665" }, "fetchMarkets": { "summary": "Fetch markets with optional filtering, search, or slug lookup.", @@ -83,7 +180,7 @@ "ordering — exchanges may reorder or add markets between requests. For stable iteration\nacross pages, use `loadMarkets()` and paginate over `Object.values(exchange.markets)`.", "Some exchanges (like Limitless) may only support status 'active' for search results." ], - "source": "BaseExchange.ts:609" + "source": "BaseExchange.ts:702" }, "fetchMarketsPaginated": { "summary": "Fetch markets with cursor-based pagination backed by a stable in-memory snapshot.", @@ -110,7 +207,7 @@ "type": "PaginatedMarketsResult", "description": "PaginatedMarketsResult with data, total, and optional nextCursor" }, - "source": "BaseExchange.ts:664" + "source": "BaseExchange.ts:757" }, "fetchEventsPaginated": { "summary": "Paginated variant of {@link fetchEvents}.", @@ -137,7 +234,7 @@ "type": "PaginatedEventsResult", "description": "PaginatedEventsResult with data, total, and optional nextCursor" }, - "source": "BaseExchange.ts:733" + "source": "BaseExchange.ts:826" }, "fetchEvents": { "summary": "Fetch events with optional keyword search.", @@ -175,7 +272,7 @@ "notes": [ "Some exchanges (like Limitless) may only support status 'active' for search results." ], - "source": "BaseExchange.ts:802" + "source": "BaseExchange.ts:895" }, "fetchSeries": { "summary": "Fetch the recurring series (fourth tier above Event -> Market -> Outcome)", @@ -192,7 +289,7 @@ "type": "UnifiedSeries[]", "description": "Array of unified series. Always an array, including the singular-lookup case." }, - "source": "BaseExchange.ts:841" + "source": "BaseExchange.ts:934" }, "fetchMarket": { "summary": "Fetch a single market by lookup parameters.", @@ -209,7 +306,7 @@ "type": "UnifiedMarket", "description": "A single unified market" }, - "source": "BaseExchange.ts:859" + "source": "BaseExchange.ts:952" }, "fetchEvent": { "summary": "Fetch a single event by lookup parameters.", @@ -226,7 +323,7 @@ "type": "UnifiedEvent", "description": "A single unified event" }, - "source": "BaseExchange.ts:959" + "source": "BaseExchange.ts:1052" }, "fetchEventMetadata": { "summary": "Fetch venue-native metadata for a specific event when the exchange", @@ -243,7 +340,7 @@ "type": "Record", "description": "Result" }, - "source": "BaseExchange.ts:976" + "source": "BaseExchange.ts:1069" }, "fetchOHLCV": { "summary": "Fetch historical OHLCV (candlestick) price data for a specific market outcome.", @@ -271,7 +368,7 @@ "Polymarket: outcomeId is the CLOB Token ID. Kalshi: outcomeId is the Market Ticker.", "Common resolutions: '1m' | '5m' | '15m' | '1h' | '6h' | '1d'. Arbitrary intervals (e.g. '30s', '120s', '3h') accepted by venues that support them." ], - "source": "BaseExchange.ts:987" + "source": "BaseExchange.ts:1080" }, "fetchOrderBook": { "summary": "Fetch the order book (bids/asks) for a specific outcome.", @@ -300,7 +397,7 @@ "type": "OrderBook", "description": "Order book with bids and asks. Returns OrderBook[] when" }, - "source": "BaseExchange.ts:1002" + "source": "BaseExchange.ts:1095" }, "fetchOrderBooks": { "summary": "Batch variant of {@link fetchOrderBook}. Fetches order books for", @@ -317,7 +414,7 @@ "type": "Record", "description": "A map keyed by the input id (preserving the caller's exact" }, - "source": "BaseExchange.ts:1030" + "source": "BaseExchange.ts:1123" }, "fetchTrades": { "summary": "Fetch raw trade history for a specific outcome.", @@ -343,7 +440,7 @@ "notes": [ "Polymarket requires an API key for trade history. Use fetchOHLCV for public historical data." ], - "source": "BaseExchange.ts:1043" + "source": "BaseExchange.ts:1136" }, "createOrder": { "summary": "Place a new order on the exchange.", @@ -360,7 +457,7 @@ "type": "Order", "description": "The created order" }, - "source": "BaseExchange.ts:1060" + "source": "BaseExchange.ts:1153" }, "buildOrder": { "summary": "Build an order payload without submitting it to the exchange.", @@ -377,7 +474,7 @@ "type": "BuiltOrder", "description": "A BuiltOrder containing the exchange-native payload" }, - "source": "BaseExchange.ts:1074" + "source": "BaseExchange.ts:1167" }, "submitOrder": { "summary": "Submit a pre-built order returned by buildOrder().", @@ -394,7 +491,7 @@ "type": "Order", "description": "The submitted order" }, - "source": "BaseExchange.ts:1086" + "source": "BaseExchange.ts:1179" }, "cancelOrder": { "summary": "Cancel an existing open order.", @@ -411,7 +508,7 @@ "type": "Order", "description": "The cancelled order" }, - "source": "BaseExchange.ts:1096" + "source": "BaseExchange.ts:1189" }, "fetchOrder": { "summary": "Fetch a specific order by ID.", @@ -428,7 +525,7 @@ "type": "Order", "description": "The order details" }, - "source": "BaseExchange.ts:1106" + "source": "BaseExchange.ts:1199" }, "fetchOpenOrders": { "summary": "Fetch all open orders, optionally filtered by market.", @@ -445,7 +542,7 @@ "type": "Order[]", "description": "Array of open orders" }, - "source": "BaseExchange.ts:1116" + "source": "BaseExchange.ts:1209" }, "fetchMyTrades": { "summary": "Fetch authenticated user trade history.", @@ -488,7 +585,7 @@ "type": "UserTrade[]", "description": "Array of user trades" }, - "source": "BaseExchange.ts:1126" + "source": "BaseExchange.ts:1219" }, "fetchClosedOrders": { "summary": "Fetch authenticated closed orders.", @@ -527,7 +624,7 @@ "type": "Order[]", "description": "Array of closed orders" }, - "source": "BaseExchange.ts:1142" + "source": "BaseExchange.ts:1235" }, "fetchAllOrders": { "summary": "Fetch authenticated order history across open and closed orders.", @@ -566,7 +663,7 @@ "type": "Order[]", "description": "Array of orders" }, - "source": "BaseExchange.ts:1157" + "source": "BaseExchange.ts:1250" }, "fetchPositions": { "summary": "Fetch current user positions across all markets.", @@ -583,7 +680,7 @@ "type": "Position[]", "description": "Array of user positions" }, - "source": "BaseExchange.ts:1172" + "source": "BaseExchange.ts:1265" }, "fetchBalance": { "summary": "Fetch account balances.", @@ -600,7 +697,7 @@ "type": "Balance[]", "description": "Array of account balances" }, - "source": "BaseExchange.ts:1182" + "source": "BaseExchange.ts:1275" }, "getExecutionPrice": { "summary": "Calculate the volume-weighted average execution price for a given order size.", @@ -629,7 +726,7 @@ "type": "number", "description": "Average execution price, or 0 if insufficient liquidity" }, - "source": "BaseExchange.ts:1192" + "source": "BaseExchange.ts:1285" }, "getExecutionPriceDetailed": { "summary": "Calculate detailed execution price information including partial fill data.", @@ -658,7 +755,7 @@ "type": "ExecutionPriceResult", "description": "Detailed execution result with price, filled amount, and fill status" }, - "source": "BaseExchange.ts:1205" + "source": "BaseExchange.ts:1298" }, "filterMarkets": { "summary": "Filter a list of markets by criteria.", @@ -681,7 +778,7 @@ "type": "UnifiedMarket[]", "description": "Filtered array of markets" }, - "source": "BaseExchange.ts:1221" + "source": "BaseExchange.ts:1314" }, "filterEvents": { "summary": "Filter a list of events by criteria.", @@ -704,7 +801,7 @@ "type": "UnifiedEvent[]", "description": "Filtered array of events" }, - "source": "BaseExchange.ts:1381" + "source": "BaseExchange.ts:1474" }, "watchOrderBook": { "summary": "Watch order book updates in real-time via WebSocket.", @@ -733,7 +830,7 @@ "type": "OrderBook", "description": "Promise that resolves with the current orderbook state" }, - "source": "BaseExchange.ts:1477" + "source": "BaseExchange.ts:1570" }, "watchOrderBooks": { "summary": "Watch multiple order books simultaneously via WebSocket.", @@ -762,7 +859,7 @@ "type": "Record", "description": "Promise that resolves with order books keyed by ID" }, - "source": "BaseExchange.ts:1490" + "source": "BaseExchange.ts:1583" }, "unwatchOrderBook": { "summary": "Unsubscribe from a previously watched order book stream.", @@ -779,7 +876,7 @@ "type": "void", "description": "Result" }, - "source": "BaseExchange.ts:1518" + "source": "BaseExchange.ts:1611" }, "watchTrades": { "summary": "Watch trade executions in real-time via WebSocket.", @@ -814,7 +911,7 @@ "type": "Trade[]", "description": "Promise that resolves with recent trades" }, - "source": "BaseExchange.ts:1531" + "source": "BaseExchange.ts:1624" }, "watchAddress": { "summary": "Stream activity for a public wallet address", @@ -837,7 +934,7 @@ "type": "SubscribedAddressSnapshot", "description": "Promise that resolves with the latest SubscribedAddressSnapshot snapshot" }, - "source": "BaseExchange.ts:1545" + "source": "BaseExchange.ts:1638" }, "unwatchAddress": { "summary": "Stop watching a previously registered wallet address and release its resource updates.", @@ -854,7 +951,7 @@ "type": "void", "description": "Result" }, - "source": "BaseExchange.ts:1558" + "source": "BaseExchange.ts:1651" }, "close": { "summary": "Close all WebSocket connections and clean up resources.", @@ -864,7 +961,7 @@ "type": "void", "description": "Result" }, - "source": "BaseExchange.ts:1567" + "source": "BaseExchange.ts:1660" }, "fetchMarketMatches": { "summary": "Find the same or related market on other venues. Two modes:", @@ -881,7 +978,7 @@ "type": "MatchResult[]", "description": "Array of matched markets with relation and confidence" }, - "source": "BaseExchange.ts:1581" + "source": "BaseExchange.ts:1674" }, "fetchMatches": { "summary": "fetchMatches", @@ -898,7 +995,7 @@ "type": "MatchResult[]", "description": "Result" }, - "source": "BaseExchange.ts:1597" + "source": "BaseExchange.ts:1690" }, "fetchEventMatches": { "summary": "Find the same or related event on other venues. Two modes:", @@ -915,7 +1012,7 @@ "type": "EventMatchResult[]", "description": "Array of matched events with market-level match details" }, - "source": "BaseExchange.ts:1605" + "source": "BaseExchange.ts:1698" }, "compareMarketPrices": { "summary": "Compare live prices for the same market across venues. Finds identity matches and returns side-by-side best bid/ask prices so you can spot price differences at a glance.", @@ -932,7 +1029,7 @@ "type": "PriceComparison[]", "description": "Array of price comparisons across venues" }, - "source": "BaseExchange.ts:1621" + "source": "BaseExchange.ts:1714" }, "fetchRelatedMarkets": { "summary": "Find related markets across venues. Discovers subset/superset market relationships", @@ -949,7 +1046,7 @@ "type": "PriceComparison[]", "description": "Array of subset/superset matches with live prices" }, - "source": "BaseExchange.ts:1631" + "source": "BaseExchange.ts:1724" }, "fetchMatchedMarkets": { "summary": "fetchMatchedMarkets", @@ -966,7 +1063,7 @@ "type": "MatchedMarketPair[]", "description": "Result" }, - "source": "BaseExchange.ts:1642" + "source": "BaseExchange.ts:1735" }, "fetchMatchedPrices": { "summary": "fetchMatchedPrices", @@ -983,7 +1080,7 @@ "type": "MatchedPricePair[]", "description": "Array of matched market pairs with prices from each venue" }, - "source": "BaseExchange.ts:1650" + "source": "BaseExchange.ts:1743" }, "fetchHedges": { "summary": "fetchHedges", @@ -1000,7 +1097,7 @@ "type": "PriceComparison[]", "description": "Array of subset/superset matches with live prices" }, - "source": "BaseExchange.ts:1661" + "source": "BaseExchange.ts:1754" }, "fetchArbitrage": { "summary": "fetchArbitrage", @@ -1017,7 +1114,7 @@ "type": "ArbitrageOpportunity[]", "description": "Array of arbitrage opportunities sorted by spread" }, - "source": "BaseExchange.ts:1671" + "source": "BaseExchange.ts:1764" }, "watchPrices": { "summary": "Watch AMM price updates for a market address (Limitless only).", @@ -1124,7 +1221,7 @@ "description": "The UnifiedEvent, or null if not found" }, "exchangeOnly": "probable", - "source": "index.ts:152" + "source": "index.ts:197" }, "getEventBySlug": { "summary": "Fetch a single event by its URL slug (Probable only).", @@ -1142,7 +1239,7 @@ "description": "The UnifiedEvent, or null if not found" }, "exchangeOnly": "probable", - "source": "index.ts:171" + "source": "index.ts:216" }, "watchAllOrderBooks": { "summary": "Stream all orderbook updates across venues via the hosted WebSocket API.", @@ -1159,7 +1256,7 @@ "type": "FirehoseEvent", "description": "Next event with source, symbol, and orderbook" }, - "source": "client.ts:2057" + "source": "client.ts:2159" }, "firehose": { "summary": "Stream all orderbook updates across venues.", @@ -1176,7 +1273,7 @@ "type": "FirehoseEvent", "description": "Next event with source, symbol, and orderbook" }, - "source": "client.ts:2098" + "source": "client.ts:2200" } }, "workflowExample": { diff --git a/core/src/BaseExchange.ts b/core/src/BaseExchange.ts index 469a32c9..8adf998e 100644 --- a/core/src/BaseExchange.ts +++ b/core/src/BaseExchange.ts @@ -32,6 +32,9 @@ import type { ArbitrageOpportunity, MatchedMarketPair, MatchedPricePair, + AuthNonceResponse, + AuthLoginResponse, + AuthSession, } from './router/types'; // ---------------------------------------------------------------------------- @@ -473,6 +476,7 @@ export abstract class PredictionMarketExchange { private _snapshot?: { markets: UnifiedMarket[]; takenAt: number; id: string }; private _eventSnapshot?: { events: UnifiedEvent[]; takenAt: number; id: string }; private apiDescriptors: ApiDescriptor[] = []; + protected _sessions: Map = new Map(); constructor(credentials?: ExchangeCredentials, options?: ExchangeOptions) { this.credentials = credentials; @@ -555,6 +559,95 @@ export abstract class PredictionMarketExchange { abstract get name(): string; + + + /** + * Get a cryptographic nonce for Web3 login. + * @param walletAddress - The wallet address to authenticate + */ + async getAuthNonce(walletAddress: string): Promise { + throw new Error(`getAuthNonce not implemented for ${this.name}`); + } + + /** + * Login with a signed nonce to obtain session credentials. + * @param walletAddress - The wallet address + * @param signature - The signature of the nonce message + * @param nonce - The nonce that was signed + */ + async loginWithSignature( + walletAddress: string, + signature: string, + nonce: string + ): Promise { + throw new Error(`loginWithSignature not implemented for ${this.name}`); + } + + /** + * Logout and invalidate the current session. + * @param walletAddress - The wallet address to logout (optional) + */ + async logout(walletAddress?: string): Promise { + throw new Error(`logout not implemented for ${this.name}`); + } + + /** + * Check if a session is active for the given wallet address. + * @param walletAddress - The wallet address to check + */ + async isSessionActive(walletAddress: string): Promise { + const session = this._sessions.get(walletAddress); + if (!session) return false; + if (session.credentials.expiresAt && session.credentials.expiresAt < Date.now()) { + this._sessions.delete(walletAddress); + return false; + } + return session.credentials.active !== false; + } + + /** + * Get the stored session for a wallet address. + * @param walletAddress - The wallet address + */ + getSession(walletAddress: string): AuthSession | undefined { + return this._sessions.get(walletAddress); + } + + /** + * Store a session in memory. + * @param walletAddress - The wallet address + * @param credentials - The session credentials + */ + protected storeSession(walletAddress: string, credentials: AuthLoginResponse): void { + this._sessions.set(walletAddress, { + walletAddress, + credentials, + createdAt: Date.now(), + lastUsedAt: Date.now(), + }); + } + + /** + * Remove a session from memory. + * @param walletAddress - The wallet address + */ + protected removeSession(walletAddress: string): void { + this._sessions.delete(walletAddress); + } + + /** + * Clean expired sessions from memory. + * Should be called periodically to prevent memory leaks. + */ + protected cleanExpiredSessions(): void { + const now = Date.now(); + for (const [address, session] of this._sessions) { + if (session.credentials.expiresAt && session.credentials.expiresAt < now) { + this._sessions.delete(address); + } + } + } + /** * Introspection getter: returns info about all implicit API methods. */ diff --git a/core/src/exchanges/probable/auth.ts b/core/src/exchanges/probable/auth.ts index 9916d7c0..677de058 100644 --- a/core/src/exchanges/probable/auth.ts +++ b/core/src/exchanges/probable/auth.ts @@ -4,6 +4,7 @@ import { createWalletClient, http } from 'viem'; import { bsc, bscTestnet } from 'viem/chains'; import { ExchangeCredentials } from '../../BaseExchange'; import { PROBABLE_CHAIN_ID, PROBABLE_TESTNET_CHAIN_ID } from './config'; +import { AuthNonceResponse, AuthLoginResponse } from '../../router/types'; /** * Manages Probable authentication and CLOB client initialization. @@ -80,3 +81,104 @@ export class ProbableAuth { return this.walletAddress; } } + +/** + * Get a nonce from Probable. + * Uses the implicit API endpoint 'getPublicApiV1AuthNonce'. + */ +export async function getAuthNonce( + walletAddress: string, + callApi: Function +): Promise { + try { + const response = await callApi('getPublicApiV1AuthNonce', { address: walletAddress }); + return { + nonce: response.nonce, + messageToSign: response.message || response.messageToSign, + expiresAt: response.expiresAt, + }; + } catch (error: any) { + throw new Error(`Failed to get Probable auth nonce: ${error.message}`); + } +} + +/** + * Login to Probable with signature. + * Uses the implicit API endpoint 'postPublicApiV1AuthLogin'. + */ +export async function loginWithSignature( + walletAddress: string, + signature: string, + nonce: string, + callApi: Function +): Promise { + try { + const response = await callApi('postPublicApiV1AuthLogin', { + address: walletAddress, + signature, + nonce, + }); + return { + apiKey: response.apiKey, + apiSecret: response.apiSecret, + passphrase: response.passphrase, + expiresAt: response.expiresAt, + active: true, + }; + } catch (error: any) { + throw new Error(`Failed to login to Probable: ${error.message}`); + } +} + +/** + * Logout from Probable. + * Uses the implicit API endpoint 'postPublicApiV1AuthLogout'. + */ +export async function logout(callApi: Function): Promise { + try { + await callApi('postPublicApiV1AuthLogout', {}); + } catch (error: any) { + // Log but don't throw - logout should be best-effort + console.warn(`Failed to logout from Probable: ${error.message}`); + } +} + +/** + * Verify L1 signature. + * Uses the implicit API endpoint 'postPublicApiV1AuthVerifyL1'. + */ +export async function verifyL1( + walletAddress: string, + signature: string, + callApi: Function +): Promise { + try { + const response = await callApi('postPublicApiV1AuthVerifyL1', { + address: walletAddress, + signature, + }); + return response.verified === true; + } catch (error: any) { + return false; + } +} + +/** + * Verify L2 signature. + * Uses the implicit API endpoint 'postPublicApiV1AuthVerifyL2'. + */ +export async function verifyL2( + walletAddress: string, + signature: string, + callApi: Function +): Promise { + try { + const response = await callApi('postPublicApiV1AuthVerifyL2', { + address: walletAddress, + signature, + }); + return response.verified === true; + } catch (error: any) { + return false; + } +} \ No newline at end of file diff --git a/core/src/exchanges/probable/index.ts b/core/src/exchanges/probable/index.ts index 57af3e50..73f2c188 100644 --- a/core/src/exchanges/probable/index.ts +++ b/core/src/exchanges/probable/index.ts @@ -8,6 +8,17 @@ import { TradesParams, MyTradesParams, } from '../../BaseExchange'; +import { + getAuthNonce as getAuthNonceFn, + loginWithSignature as loginWithSignatureFn, + logout as logoutFn, + verifyL1 as verifyL1Fn, + verifyL2 as verifyL2Fn, +} from './auth'; +import { + AuthNonceResponse, + AuthLoginResponse, +} from '../../router/types'; import { UnifiedMarket, UnifiedEvent, @@ -90,6 +101,40 @@ export class ProbableExchange extends PredictionMarketExchange { return this.auth; } + async getAuthNonce(walletAddress: string): Promise { + return getAuthNonceFn(walletAddress, this.callApi.bind(this)); + } + + async loginWithSignature( + walletAddress: string, + signature: string, + nonce: string + ): Promise { + const credentials = await loginWithSignatureFn( + walletAddress, + signature, + nonce, + this.callApi.bind(this) + ); + this.storeSession(walletAddress, credentials); + return credentials; + } + + async logout(walletAddress?: string): Promise { + await logoutFn(this.callApi.bind(this)); + if (walletAddress) { + this.removeSession(walletAddress); + } + } + + async verifyL1(walletAddress: string, signature: string): Promise { + return verifyL1Fn(walletAddress, signature, this.callApi.bind(this)); + } + + async verifyL2(walletAddress: string, signature: string): Promise { + return verifyL2Fn(walletAddress, signature, this.callApi.bind(this)); + } + // -------------------------------------------------------------------------- // Market Data (fetcher -> normalizer) // -------------------------------------------------------------------------- diff --git a/core/src/router/types.ts b/core/src/router/types.ts index d32a024a..a5747972 100644 --- a/core/src/router/types.ts +++ b/core/src/router/types.ts @@ -92,6 +92,50 @@ export interface FetchMarketMatchesParams { sort?: 'confidence' | 'volume' | 'priceDifference'; } +// ===== Probable Auth Types ===== + +/** + * Response from /auth/nonce + */ +export interface AuthNonceResponse { + /** Random nonce to be signed */ + nonce: string; + /** Human-readable message to sign */ + messageToSign: string; + /** Optional expiry timestamp (milliseconds) */ + expiresAt?: number; +} + +/** + * Response from /auth/login + */ +export interface AuthLoginResponse { + /** API key for authenticated requests */ + apiKey: string; + /** API secret for signing */ + apiSecret: string; + /** Passphrase for trading */ + passphrase?: string; + /** Session expiry timestamp (milliseconds) */ + expiresAt?: number; + /** Whether session is active */ + active?: boolean; +} + +/** + * Session state stored in memory + */ +export interface AuthSession { + /** Wallet address that authenticated */ + walletAddress: string; + /** Session credentials */ + credentials: AuthLoginResponse; + /** When session was created (milliseconds) */ + createdAt: number; + /** When session was last used (milliseconds) */ + lastUsedAt: number; +} + /** @deprecated Use {@link FetchMarketMatchesParams} instead. */ export type FetchMatchesParams = FetchMarketMatchesParams; diff --git a/core/src/server/method-verbs.json b/core/src/server/method-verbs.json index 205be445..f76486bd 100644 --- a/core/src/server/method-verbs.json +++ b/core/src/server/method-verbs.json @@ -1,4 +1,64 @@ { + "getAuthNonce": { + "verb": "post", + "args": [ + { + "name": "walletAddress", + "kind": "string", + "optional": false + } + ] + }, + "loginWithSignature": { + "verb": "post", + "args": [ + { + "name": "walletAddress", + "kind": "string", + "optional": false + }, + { + "name": "signature", + "kind": "string", + "optional": false + }, + { + "name": "nonce", + "kind": "string", + "optional": false + } + ] + }, + "logout": { + "verb": "post", + "args": [ + { + "name": "walletAddress", + "kind": "string", + "optional": true + } + ] + }, + "isSessionActive": { + "verb": "post", + "args": [ + { + "name": "walletAddress", + "kind": "string", + "optional": false + } + ] + }, + "getSession": { + "verb": "post", + "args": [ + { + "name": "walletAddress", + "kind": "string", + "optional": false + } + ] + }, "loadMarkets": { "verb": "post", "args": [ diff --git a/core/src/server/openapi.yaml b/core/src/server/openapi.yaml index 428cf7d9..f85ab4e1 100644 --- a/core/src/server/openapi.yaml +++ b/core/src/server/openapi.yaml @@ -27,6 +27,181 @@ paths: timestamp: type: integer format: int64 + '/api/{exchange}/getAuthNonce': + post: + summary: Get Auth Nonce + operationId: getAuthNonce + parameters: + - $ref: '#/components/parameters/ExchangeParam' + requestBody: + content: + application/json: + schema: + title: GetAuthNonceRequest + type: object + properties: + args: + type: array + maxItems: 1 + items: + type: string + minItems: 1 + credentials: + $ref: '#/components/schemas/ExchangeCredentials' + required: + - args + responses: + '200': + description: Get Auth Nonce response + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/BaseResponse' + - type: object + properties: + data: + type: object + description: Get a cryptographic nonce for Web3 login. + '/api/{exchange}/loginWithSignature': + post: + summary: Login With Signature + operationId: loginWithSignature + parameters: + - $ref: '#/components/parameters/ExchangeParam' + requestBody: + content: + application/json: + schema: + title: LoginWithSignatureRequest + type: object + properties: + args: + type: array + minItems: 3 + maxItems: 3 + items: + oneOf: + - type: string + - type: string + - type: string + credentials: + $ref: '#/components/schemas/ExchangeCredentials' + required: + - args + responses: + '200': + description: Login With Signature response + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/BaseResponse' + - type: object + properties: + data: + type: object + description: Login with a signed nonce to obtain session credentials. + '/api/{exchange}/logout': + post: + summary: Logout + operationId: logout + parameters: + - $ref: '#/components/parameters/ExchangeParam' + requestBody: + content: + application/json: + schema: + title: LogoutRequest + type: object + properties: + args: + type: array + maxItems: 1 + items: + type: string + credentials: + $ref: '#/components/schemas/ExchangeCredentials' + responses: + '200': + description: Logout response + content: + application/json: + schema: + $ref: '#/components/schemas/BaseResponse' + description: Logout and invalidate the current session. + '/api/{exchange}/isSessionActive': + post: + summary: Is Session Active + operationId: isSessionActive + parameters: + - $ref: '#/components/parameters/ExchangeParam' + requestBody: + content: + application/json: + schema: + title: IsSessionActiveRequest + type: object + properties: + args: + type: array + maxItems: 1 + items: + type: string + minItems: 1 + credentials: + $ref: '#/components/schemas/ExchangeCredentials' + required: + - args + responses: + '200': + description: Is Session Active response + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/BaseResponse' + - type: object + properties: + data: + type: boolean + description: Check if a session is active for the given wallet address. + '/api/{exchange}/getSession': + post: + summary: Get Session + operationId: getSession + parameters: + - $ref: '#/components/parameters/ExchangeParam' + requestBody: + content: + application/json: + schema: + title: GetSessionRequest + type: object + properties: + args: + type: array + maxItems: 1 + items: + type: string + minItems: 1 + credentials: + $ref: '#/components/schemas/ExchangeCredentials' + required: + - args + responses: + '200': + description: Get Session response + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/BaseResponse' + - type: object + properties: + data: + type: object + description: Get the stored session for a wallet address. '/api/{exchange}/loadMarkets': post: summary: Load Markets diff --git a/core/test/exchanges/probable-auth.test.ts b/core/test/exchanges/probable-auth.test.ts new file mode 100644 index 00000000..2614274b --- /dev/null +++ b/core/test/exchanges/probable-auth.test.ts @@ -0,0 +1,89 @@ +import { getAuthNonce, loginWithSignature, logout, verifyL1, verifyL2 } from '../../src/exchanges/probable/auth'; + +describe('Probable Auth Lifecycle', () => { + const mockCallApi = jest.fn(); + const walletAddress = '0x1234567890123456789012345678901234567890'; + + beforeEach(() => { + mockCallApi.mockReset(); + }); + + test('getAuthNonce should fetch and map nonce correctly', async () => { + mockCallApi.mockResolvedValue({ + nonce: 'abc123', + messageToSign: 'Sign this message', + expiresAt: Date.now() + 300000, + }); + + const result = await getAuthNonce(walletAddress, mockCallApi); + + expect(mockCallApi).toHaveBeenCalledWith('getPublicApiV1AuthNonce', { address: walletAddress }); + expect(result.nonce).toBe('abc123'); + expect(result.messageToSign).toBe('Sign this message'); + }); + + test('loginWithSignature should exchange signature for credentials', async () => { + mockCallApi.mockResolvedValue({ + apiKey: 'key_123', + apiSecret: 'secret_456', + passphrase: 'phrase_789', + expiresAt: Date.now() + 3600000, + active: true, + }); + + const result = await loginWithSignature( + walletAddress, + '0xSignature', + 'abc123', + mockCallApi + ); + + expect(mockCallApi).toHaveBeenCalledWith('postPublicApiV1AuthLogin', { + address: walletAddress, + signature: '0xSignature', + nonce: 'abc123', + }); + expect(result.apiKey).toBe('key_123'); + expect(result.apiSecret).toBe('secret_456'); + }); + + test('logout should call the logout endpoint', async () => { + mockCallApi.mockResolvedValue({ success: true }); + + await logout(mockCallApi); + + expect(mockCallApi).toHaveBeenCalledWith('postPublicApiV1AuthLogout', {}); + }); + + test('verifyL1 should return verification result', async () => { + mockCallApi.mockResolvedValue({ verified: true }); + + const result = await verifyL1(walletAddress, '0xSignature', mockCallApi); + + expect(mockCallApi).toHaveBeenCalledWith('postPublicApiV1AuthVerifyL1', { + address: walletAddress, + signature: '0xSignature', + }); + expect(result).toBe(true); + }); + + test('verifyL2 should return verification result', async () => { + mockCallApi.mockResolvedValue({ verified: true }); + + const result = await verifyL2(walletAddress, '0xSignature', mockCallApi); + + expect(mockCallApi).toHaveBeenCalledWith('postPublicApiV1AuthVerifyL2', { + address: walletAddress, + signature: '0xSignature', + }); + expect(result).toBe(true); + }); + + test('should handle errors gracefully', async () => { + mockCallApi.mockRejectedValue(new Error('Network error')); + + await expect(getAuthNonce(walletAddress, mockCallApi)).rejects.toThrow( + 'Failed to get Probable auth nonce' + ); + }); +}); \ No newline at end of file diff --git a/docs/api-reference/openapi.json b/docs/api-reference/openapi.json index 72ee155e..191f8b77 100644 --- a/docs/api-reference/openapi.json +++ b/docs/api-reference/openapi.json @@ -41,6 +41,1125 @@ "security": [] } }, + "/api/{exchange}/getAuthNonce": { + "post": { + "summary": "Get Auth Nonce", + "operationId": "getAuthNonce", + "parameters": [ + { + "$ref": "#/components/parameters/ExchangeParam" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "GetAuthNonceRequest", + "type": "object", + "properties": { + "args": { + "type": "array", + "maxItems": 1, + "items": { + "type": "string" + }, + "minItems": 1 + }, + "credentials": { + "$ref": "#/components/schemas/ExchangeCredentials" + } + }, + "required": [ + "args" + ] + } + } + } + }, + "responses": { + "200": { + "description": "Get Auth Nonce response", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/BaseResponse" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object" + } + } + } + ] + } + } + } + } + }, + "description": "Get a cryptographic nonce for Web3 login.", + "security": [], + "x-codeSamples": [ + { + "lang": "python", + "label": "Polymarket", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Polymarket(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_auth_nonce()" + }, + { + "lang": "python", + "label": "Limitless", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Limitless(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_auth_nonce()" + }, + { + "lang": "python", + "label": "Kalshi", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Kalshi(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_auth_nonce()" + }, + { + "lang": "python", + "label": "KalshiDemo", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.KalshiDemo(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_auth_nonce()" + }, + { + "lang": "python", + "label": "Probable", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Probable(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_auth_nonce()" + }, + { + "lang": "python", + "label": "Baozi", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Baozi(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_auth_nonce()" + }, + { + "lang": "python", + "label": "Myriad", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Myriad(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_auth_nonce()" + }, + { + "lang": "python", + "label": "Opinion", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Opinion(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_auth_nonce()" + }, + { + "lang": "python", + "label": "Metaculus", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Metaculus(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_auth_nonce()" + }, + { + "lang": "python", + "label": "Smarkets", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Smarkets(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_auth_nonce()" + }, + { + "lang": "python", + "label": "PolymarketUS", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.PolymarketUS(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_auth_nonce()" + }, + { + "lang": "python", + "label": "Hyperliquid", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Hyperliquid(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_auth_nonce()" + }, + { + "lang": "python", + "label": "GeminiTitan", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.GeminiTitan(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_auth_nonce()" + }, + { + "lang": "python", + "label": "SuiBets", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.SuiBets(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_auth_nonce()" + }, + { + "lang": "python", + "label": "Rain", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Rain(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_auth_nonce()" + }, + { + "lang": "python", + "label": "Hunch", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Hunch(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_auth_nonce()" + }, + { + "lang": "javascript", + "label": "Polymarket", + "source": "import { Polymarket } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Polymarket({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getAuthNonce();" + }, + { + "lang": "javascript", + "label": "Limitless", + "source": "import { Limitless } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Limitless({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getAuthNonce();" + }, + { + "lang": "javascript", + "label": "Kalshi", + "source": "import { Kalshi } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Kalshi({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getAuthNonce();" + }, + { + "lang": "javascript", + "label": "KalshiDemo", + "source": "import { KalshiDemo } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new KalshiDemo({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getAuthNonce();" + }, + { + "lang": "javascript", + "label": "Probable", + "source": "import { Probable } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Probable({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getAuthNonce();" + }, + { + "lang": "javascript", + "label": "Baozi", + "source": "import { Baozi } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Baozi({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getAuthNonce();" + }, + { + "lang": "javascript", + "label": "Myriad", + "source": "import { Myriad } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Myriad({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getAuthNonce();" + }, + { + "lang": "javascript", + "label": "Opinion", + "source": "import { Opinion } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Opinion({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getAuthNonce();" + }, + { + "lang": "javascript", + "label": "Metaculus", + "source": "import { Metaculus } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Metaculus({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getAuthNonce();" + }, + { + "lang": "javascript", + "label": "Smarkets", + "source": "import { Smarkets } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Smarkets({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getAuthNonce();" + }, + { + "lang": "javascript", + "label": "PolymarketUS", + "source": "import { PolymarketUS } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new PolymarketUS({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getAuthNonce();" + }, + { + "lang": "javascript", + "label": "Hyperliquid", + "source": "import { Hyperliquid } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Hyperliquid({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getAuthNonce();" + }, + { + "lang": "javascript", + "label": "GeminiTitan", + "source": "import { GeminiTitan } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new GeminiTitan({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getAuthNonce();" + }, + { + "lang": "javascript", + "label": "SuiBets", + "source": "import { SuiBets } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new SuiBets({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getAuthNonce();" + }, + { + "lang": "javascript", + "label": "Rain", + "source": "import { Rain } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Rain({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getAuthNonce();" + }, + { + "lang": "javascript", + "label": "Hunch", + "source": "import { Hunch } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Hunch({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getAuthNonce();" + } + ] + } + }, + "/api/{exchange}/loginWithSignature": { + "post": { + "summary": "Login With Signature", + "operationId": "loginWithSignature", + "parameters": [ + { + "$ref": "#/components/parameters/ExchangeParam" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "LoginWithSignatureRequest", + "type": "object", + "properties": { + "args": { + "type": "array", + "minItems": 3, + "maxItems": 3, + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ] + } + }, + "credentials": { + "$ref": "#/components/schemas/ExchangeCredentials" + } + }, + "required": [ + "args" + ] + } + } + } + }, + "responses": { + "200": { + "description": "Login With Signature response", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/BaseResponse" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object" + } + } + } + ] + } + } + } + } + }, + "description": "Login with a signed nonce to obtain session credentials.", + "security": [], + "x-codeSamples": [ + { + "lang": "python", + "label": "Polymarket", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Polymarket(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.login_with_signature()" + }, + { + "lang": "python", + "label": "Limitless", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Limitless(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.login_with_signature()" + }, + { + "lang": "python", + "label": "Kalshi", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Kalshi(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.login_with_signature()" + }, + { + "lang": "python", + "label": "KalshiDemo", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.KalshiDemo(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.login_with_signature()" + }, + { + "lang": "python", + "label": "Probable", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Probable(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.login_with_signature()" + }, + { + "lang": "python", + "label": "Baozi", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Baozi(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.login_with_signature()" + }, + { + "lang": "python", + "label": "Myriad", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Myriad(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.login_with_signature()" + }, + { + "lang": "python", + "label": "Opinion", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Opinion(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.login_with_signature()" + }, + { + "lang": "python", + "label": "Metaculus", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Metaculus(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.login_with_signature()" + }, + { + "lang": "python", + "label": "Smarkets", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Smarkets(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.login_with_signature()" + }, + { + "lang": "python", + "label": "PolymarketUS", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.PolymarketUS(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.login_with_signature()" + }, + { + "lang": "python", + "label": "Hyperliquid", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Hyperliquid(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.login_with_signature()" + }, + { + "lang": "python", + "label": "GeminiTitan", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.GeminiTitan(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.login_with_signature()" + }, + { + "lang": "python", + "label": "SuiBets", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.SuiBets(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.login_with_signature()" + }, + { + "lang": "python", + "label": "Rain", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Rain(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.login_with_signature()" + }, + { + "lang": "python", + "label": "Hunch", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Hunch(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.login_with_signature()" + }, + { + "lang": "javascript", + "label": "Polymarket", + "source": "import { Polymarket } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Polymarket({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.loginWithSignature();" + }, + { + "lang": "javascript", + "label": "Limitless", + "source": "import { Limitless } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Limitless({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.loginWithSignature();" + }, + { + "lang": "javascript", + "label": "Kalshi", + "source": "import { Kalshi } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Kalshi({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.loginWithSignature();" + }, + { + "lang": "javascript", + "label": "KalshiDemo", + "source": "import { KalshiDemo } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new KalshiDemo({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.loginWithSignature();" + }, + { + "lang": "javascript", + "label": "Probable", + "source": "import { Probable } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Probable({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.loginWithSignature();" + }, + { + "lang": "javascript", + "label": "Baozi", + "source": "import { Baozi } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Baozi({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.loginWithSignature();" + }, + { + "lang": "javascript", + "label": "Myriad", + "source": "import { Myriad } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Myriad({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.loginWithSignature();" + }, + { + "lang": "javascript", + "label": "Opinion", + "source": "import { Opinion } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Opinion({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.loginWithSignature();" + }, + { + "lang": "javascript", + "label": "Metaculus", + "source": "import { Metaculus } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Metaculus({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.loginWithSignature();" + }, + { + "lang": "javascript", + "label": "Smarkets", + "source": "import { Smarkets } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Smarkets({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.loginWithSignature();" + }, + { + "lang": "javascript", + "label": "PolymarketUS", + "source": "import { PolymarketUS } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new PolymarketUS({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.loginWithSignature();" + }, + { + "lang": "javascript", + "label": "Hyperliquid", + "source": "import { Hyperliquid } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Hyperliquid({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.loginWithSignature();" + }, + { + "lang": "javascript", + "label": "GeminiTitan", + "source": "import { GeminiTitan } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new GeminiTitan({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.loginWithSignature();" + }, + { + "lang": "javascript", + "label": "SuiBets", + "source": "import { SuiBets } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new SuiBets({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.loginWithSignature();" + }, + { + "lang": "javascript", + "label": "Rain", + "source": "import { Rain } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Rain({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.loginWithSignature();" + }, + { + "lang": "javascript", + "label": "Hunch", + "source": "import { Hunch } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Hunch({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.loginWithSignature();" + } + ] + } + }, + "/api/{exchange}/logout": { + "post": { + "summary": "Logout", + "operationId": "logout", + "parameters": [ + { + "$ref": "#/components/parameters/ExchangeParam" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "LogoutRequest", + "type": "object", + "properties": { + "args": { + "type": "array", + "maxItems": 1, + "items": { + "type": "string" + } + }, + "credentials": { + "$ref": "#/components/schemas/ExchangeCredentials" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Logout response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BaseResponse" + } + } + } + } + }, + "description": "Logout and invalidate the current session.", + "security": [], + "x-codeSamples": [ + { + "lang": "python", + "label": "Polymarket", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Polymarket(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.logout()" + }, + { + "lang": "python", + "label": "Limitless", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Limitless(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.logout()" + }, + { + "lang": "python", + "label": "Kalshi", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Kalshi(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.logout()" + }, + { + "lang": "python", + "label": "KalshiDemo", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.KalshiDemo(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.logout()" + }, + { + "lang": "python", + "label": "Probable", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Probable(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.logout()" + }, + { + "lang": "python", + "label": "Baozi", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Baozi(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.logout()" + }, + { + "lang": "python", + "label": "Myriad", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Myriad(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.logout()" + }, + { + "lang": "python", + "label": "Opinion", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Opinion(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.logout()" + }, + { + "lang": "python", + "label": "Metaculus", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Metaculus(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.logout()" + }, + { + "lang": "python", + "label": "Smarkets", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Smarkets(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.logout()" + }, + { + "lang": "python", + "label": "PolymarketUS", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.PolymarketUS(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.logout()" + }, + { + "lang": "python", + "label": "Hyperliquid", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Hyperliquid(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.logout()" + }, + { + "lang": "python", + "label": "GeminiTitan", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.GeminiTitan(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.logout()" + }, + { + "lang": "python", + "label": "SuiBets", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.SuiBets(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.logout()" + }, + { + "lang": "python", + "label": "Rain", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Rain(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.logout()" + }, + { + "lang": "python", + "label": "Hunch", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Hunch(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.logout()" + }, + { + "lang": "javascript", + "label": "Polymarket", + "source": "import { Polymarket } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Polymarket({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.logout();" + }, + { + "lang": "javascript", + "label": "Limitless", + "source": "import { Limitless } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Limitless({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.logout();" + }, + { + "lang": "javascript", + "label": "Kalshi", + "source": "import { Kalshi } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Kalshi({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.logout();" + }, + { + "lang": "javascript", + "label": "KalshiDemo", + "source": "import { KalshiDemo } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new KalshiDemo({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.logout();" + }, + { + "lang": "javascript", + "label": "Probable", + "source": "import { Probable } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Probable({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.logout();" + }, + { + "lang": "javascript", + "label": "Baozi", + "source": "import { Baozi } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Baozi({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.logout();" + }, + { + "lang": "javascript", + "label": "Myriad", + "source": "import { Myriad } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Myriad({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.logout();" + }, + { + "lang": "javascript", + "label": "Opinion", + "source": "import { Opinion } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Opinion({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.logout();" + }, + { + "lang": "javascript", + "label": "Metaculus", + "source": "import { Metaculus } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Metaculus({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.logout();" + }, + { + "lang": "javascript", + "label": "Smarkets", + "source": "import { Smarkets } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Smarkets({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.logout();" + }, + { + "lang": "javascript", + "label": "PolymarketUS", + "source": "import { PolymarketUS } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new PolymarketUS({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.logout();" + }, + { + "lang": "javascript", + "label": "Hyperliquid", + "source": "import { Hyperliquid } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Hyperliquid({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.logout();" + }, + { + "lang": "javascript", + "label": "GeminiTitan", + "source": "import { GeminiTitan } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new GeminiTitan({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.logout();" + }, + { + "lang": "javascript", + "label": "SuiBets", + "source": "import { SuiBets } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new SuiBets({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.logout();" + }, + { + "lang": "javascript", + "label": "Rain", + "source": "import { Rain } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Rain({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.logout();" + }, + { + "lang": "javascript", + "label": "Hunch", + "source": "import { Hunch } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Hunch({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.logout();" + } + ] + } + }, + "/api/{exchange}/isSessionActive": { + "post": { + "summary": "Is Session Active", + "operationId": "isSessionActive", + "parameters": [ + { + "$ref": "#/components/parameters/ExchangeParam" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "IsSessionActiveRequest", + "type": "object", + "properties": { + "args": { + "type": "array", + "maxItems": 1, + "items": { + "type": "string" + }, + "minItems": 1 + }, + "credentials": { + "$ref": "#/components/schemas/ExchangeCredentials" + } + }, + "required": [ + "args" + ] + } + } + } + }, + "responses": { + "200": { + "description": "Is Session Active response", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/BaseResponse" + }, + { + "type": "object", + "properties": { + "data": { + "type": "boolean" + } + } + } + ] + } + } + } + } + }, + "description": "Check if a session is active for the given wallet address.", + "security": [], + "x-codeSamples": [ + { + "lang": "python", + "label": "Polymarket", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Polymarket(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.is_session_active()" + }, + { + "lang": "python", + "label": "Limitless", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Limitless(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.is_session_active()" + }, + { + "lang": "python", + "label": "Kalshi", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Kalshi(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.is_session_active()" + }, + { + "lang": "python", + "label": "KalshiDemo", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.KalshiDemo(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.is_session_active()" + }, + { + "lang": "python", + "label": "Probable", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Probable(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.is_session_active()" + }, + { + "lang": "python", + "label": "Baozi", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Baozi(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.is_session_active()" + }, + { + "lang": "python", + "label": "Myriad", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Myriad(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.is_session_active()" + }, + { + "lang": "python", + "label": "Opinion", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Opinion(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.is_session_active()" + }, + { + "lang": "python", + "label": "Metaculus", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Metaculus(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.is_session_active()" + }, + { + "lang": "python", + "label": "Smarkets", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Smarkets(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.is_session_active()" + }, + { + "lang": "python", + "label": "PolymarketUS", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.PolymarketUS(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.is_session_active()" + }, + { + "lang": "python", + "label": "Hyperliquid", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Hyperliquid(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.is_session_active()" + }, + { + "lang": "python", + "label": "GeminiTitan", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.GeminiTitan(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.is_session_active()" + }, + { + "lang": "python", + "label": "SuiBets", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.SuiBets(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.is_session_active()" + }, + { + "lang": "python", + "label": "Rain", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Rain(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.is_session_active()" + }, + { + "lang": "python", + "label": "Hunch", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Hunch(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.is_session_active()" + }, + { + "lang": "javascript", + "label": "Polymarket", + "source": "import { Polymarket } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Polymarket({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.isSessionActive();" + }, + { + "lang": "javascript", + "label": "Limitless", + "source": "import { Limitless } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Limitless({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.isSessionActive();" + }, + { + "lang": "javascript", + "label": "Kalshi", + "source": "import { Kalshi } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Kalshi({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.isSessionActive();" + }, + { + "lang": "javascript", + "label": "KalshiDemo", + "source": "import { KalshiDemo } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new KalshiDemo({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.isSessionActive();" + }, + { + "lang": "javascript", + "label": "Probable", + "source": "import { Probable } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Probable({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.isSessionActive();" + }, + { + "lang": "javascript", + "label": "Baozi", + "source": "import { Baozi } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Baozi({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.isSessionActive();" + }, + { + "lang": "javascript", + "label": "Myriad", + "source": "import { Myriad } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Myriad({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.isSessionActive();" + }, + { + "lang": "javascript", + "label": "Opinion", + "source": "import { Opinion } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Opinion({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.isSessionActive();" + }, + { + "lang": "javascript", + "label": "Metaculus", + "source": "import { Metaculus } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Metaculus({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.isSessionActive();" + }, + { + "lang": "javascript", + "label": "Smarkets", + "source": "import { Smarkets } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Smarkets({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.isSessionActive();" + }, + { + "lang": "javascript", + "label": "PolymarketUS", + "source": "import { PolymarketUS } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new PolymarketUS({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.isSessionActive();" + }, + { + "lang": "javascript", + "label": "Hyperliquid", + "source": "import { Hyperliquid } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Hyperliquid({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.isSessionActive();" + }, + { + "lang": "javascript", + "label": "GeminiTitan", + "source": "import { GeminiTitan } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new GeminiTitan({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.isSessionActive();" + }, + { + "lang": "javascript", + "label": "SuiBets", + "source": "import { SuiBets } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new SuiBets({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.isSessionActive();" + }, + { + "lang": "javascript", + "label": "Rain", + "source": "import { Rain } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Rain({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.isSessionActive();" + }, + { + "lang": "javascript", + "label": "Hunch", + "source": "import { Hunch } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Hunch({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.isSessionActive();" + } + ] + } + }, + "/api/{exchange}/getSession": { + "post": { + "summary": "Get Session", + "operationId": "getSession", + "parameters": [ + { + "$ref": "#/components/parameters/ExchangeParam" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "GetSessionRequest", + "type": "object", + "properties": { + "args": { + "type": "array", + "maxItems": 1, + "items": { + "type": "string" + }, + "minItems": 1 + }, + "credentials": { + "$ref": "#/components/schemas/ExchangeCredentials" + } + }, + "required": [ + "args" + ] + } + } + } + }, + "responses": { + "200": { + "description": "Get Session response", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/BaseResponse" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object" + } + } + } + ] + } + } + } + } + }, + "description": "Get the stored session for a wallet address.", + "security": [], + "x-codeSamples": [ + { + "lang": "python", + "label": "Polymarket", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Polymarket(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_session()" + }, + { + "lang": "python", + "label": "Limitless", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Limitless(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_session()" + }, + { + "lang": "python", + "label": "Kalshi", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Kalshi(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_session()" + }, + { + "lang": "python", + "label": "KalshiDemo", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.KalshiDemo(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_session()" + }, + { + "lang": "python", + "label": "Probable", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Probable(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_session()" + }, + { + "lang": "python", + "label": "Baozi", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Baozi(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_session()" + }, + { + "lang": "python", + "label": "Myriad", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Myriad(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_session()" + }, + { + "lang": "python", + "label": "Opinion", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Opinion(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_session()" + }, + { + "lang": "python", + "label": "Metaculus", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Metaculus(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_session()" + }, + { + "lang": "python", + "label": "Smarkets", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Smarkets(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_session()" + }, + { + "lang": "python", + "label": "PolymarketUS", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.PolymarketUS(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_session()" + }, + { + "lang": "python", + "label": "Hyperliquid", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Hyperliquid(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_session()" + }, + { + "lang": "python", + "label": "GeminiTitan", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.GeminiTitan(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_session()" + }, + { + "lang": "python", + "label": "SuiBets", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.SuiBets(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_session()" + }, + { + "lang": "python", + "label": "Rain", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Rain(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_session()" + }, + { + "lang": "python", + "label": "Hunch", + "source": "import pmxt\n\n# API key optional — enables faster catalog-backed lookups\nexchange = pmxt.Hunch(\n pmxt_api_key=\"YOUR_PMXT_API_KEY\",\n)\nresult = exchange.get_session()" + }, + { + "lang": "javascript", + "label": "Polymarket", + "source": "import { Polymarket } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Polymarket({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getSession();" + }, + { + "lang": "javascript", + "label": "Limitless", + "source": "import { Limitless } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Limitless({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getSession();" + }, + { + "lang": "javascript", + "label": "Kalshi", + "source": "import { Kalshi } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Kalshi({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getSession();" + }, + { + "lang": "javascript", + "label": "KalshiDemo", + "source": "import { KalshiDemo } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new KalshiDemo({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getSession();" + }, + { + "lang": "javascript", + "label": "Probable", + "source": "import { Probable } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Probable({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getSession();" + }, + { + "lang": "javascript", + "label": "Baozi", + "source": "import { Baozi } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Baozi({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getSession();" + }, + { + "lang": "javascript", + "label": "Myriad", + "source": "import { Myriad } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Myriad({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getSession();" + }, + { + "lang": "javascript", + "label": "Opinion", + "source": "import { Opinion } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Opinion({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getSession();" + }, + { + "lang": "javascript", + "label": "Metaculus", + "source": "import { Metaculus } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Metaculus({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getSession();" + }, + { + "lang": "javascript", + "label": "Smarkets", + "source": "import { Smarkets } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Smarkets({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getSession();" + }, + { + "lang": "javascript", + "label": "PolymarketUS", + "source": "import { PolymarketUS } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new PolymarketUS({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getSession();" + }, + { + "lang": "javascript", + "label": "Hyperliquid", + "source": "import { Hyperliquid } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Hyperliquid({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getSession();" + }, + { + "lang": "javascript", + "label": "GeminiTitan", + "source": "import { GeminiTitan } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new GeminiTitan({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getSession();" + }, + { + "lang": "javascript", + "label": "SuiBets", + "source": "import { SuiBets } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new SuiBets({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getSession();" + }, + { + "lang": "javascript", + "label": "Rain", + "source": "import { Rain } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Rain({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getSession();" + }, + { + "lang": "javascript", + "label": "Hunch", + "source": "import { Hunch } from \"pmxtjs\";\n\n// API key optional — enables faster catalog-backed lookups\nconst exchange = new Hunch({\n pmxtApiKey: \"YOUR_PMXT_API_KEY\",\n});\nconst result = await exchange.getSession();" + } + ] + } + }, "/api/{exchange}/loadMarkets": { "post": { "summary": "Load Markets", diff --git a/docs/docs.json b/docs/docs.json index dbdb4b93..04c87f9c 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -216,6 +216,11 @@ { "group": "Other", "pages": [ + "POST /api/{exchange}/getAuthNonce", + "POST /api/{exchange}/loginWithSignature", + "POST /api/{exchange}/logout", + "POST /api/{exchange}/isSessionActive", + "POST /api/{exchange}/getSession", "GET /api/{exchange}/fetchSeries", "GET /api/{exchange}/fetchEventMetadata" ], diff --git a/docs/llms-full.txt b/docs/llms-full.txt index 62391cd6..4717fedd 100644 --- a/docs/llms-full.txt +++ b/docs/llms-full.txt @@ -6103,6 +6103,159 @@ for (const p of prices) { ## Other +### Get Auth Nonce + +`POST /api/{exchange}/getAuthNonce` + +Get a cryptographic nonce for Web3 login. + +**Response:** `{ success: true, data: ... }` + +**Python:** +```python +import pmxt + +# API key optional — enables faster catalog-backed lookups +exchange = pmxt.Kalshi( + pmxt_api_key="YOUR_PMXT_API_KEY", +) +result = exchange.get_auth_nonce() +``` + +**TypeScript:** +```typescript +import { Kalshi } from "pmxtjs"; + +// API key optional — enables faster catalog-backed lookups +const exchange = new Kalshi({ + pmxtApiKey: "YOUR_PMXT_API_KEY", +}); +const result = await exchange.getAuthNonce(); +``` + + +### Login With Signature + +`POST /api/{exchange}/loginWithSignature` + +Login with a signed nonce to obtain session credentials. + +**Response:** `{ success: true, data: ... }` + +**Python:** +```python +import pmxt + +# API key optional — enables faster catalog-backed lookups +exchange = pmxt.Kalshi( + pmxt_api_key="YOUR_PMXT_API_KEY", +) +result = exchange.login_with_signature() +``` + +**TypeScript:** +```typescript +import { Kalshi } from "pmxtjs"; + +// API key optional — enables faster catalog-backed lookups +const exchange = new Kalshi({ + pmxtApiKey: "YOUR_PMXT_API_KEY", +}); +const result = await exchange.loginWithSignature(); +``` + + +### Logout + +`POST /api/{exchange}/logout` + +Logout and invalidate the current session. + +**Python:** +```python +import pmxt + +# API key optional — enables faster catalog-backed lookups +exchange = pmxt.Kalshi( + pmxt_api_key="YOUR_PMXT_API_KEY", +) +result = exchange.logout() +``` + +**TypeScript:** +```typescript +import { Kalshi } from "pmxtjs"; + +// API key optional — enables faster catalog-backed lookups +const exchange = new Kalshi({ + pmxtApiKey: "YOUR_PMXT_API_KEY", +}); +const result = await exchange.logout(); +``` + + +### Is Session Active + +`POST /api/{exchange}/isSessionActive` + +Check if a session is active for the given wallet address. + +**Response:** `{ success: true, data: ... }` + +**Python:** +```python +import pmxt + +# API key optional — enables faster catalog-backed lookups +exchange = pmxt.Kalshi( + pmxt_api_key="YOUR_PMXT_API_KEY", +) +result = exchange.is_session_active() +``` + +**TypeScript:** +```typescript +import { Kalshi } from "pmxtjs"; + +// API key optional — enables faster catalog-backed lookups +const exchange = new Kalshi({ + pmxtApiKey: "YOUR_PMXT_API_KEY", +}); +const result = await exchange.isSessionActive(); +``` + + +### Get Session + +`POST /api/{exchange}/getSession` + +Get the stored session for a wallet address. + +**Response:** `{ success: true, data: ... }` + +**Python:** +```python +import pmxt + +# API key optional — enables faster catalog-backed lookups +exchange = pmxt.Kalshi( + pmxt_api_key="YOUR_PMXT_API_KEY", +) +result = exchange.get_session() +``` + +**TypeScript:** +```typescript +import { Kalshi } from "pmxtjs"; + +// API key optional — enables faster catalog-backed lookups +const exchange = new Kalshi({ + pmxtApiKey: "YOUR_PMXT_API_KEY", +}); +const result = await exchange.getSession(); +``` + + ### Fetch Series `GET /api/{exchange}/fetchSeries` diff --git a/docs/llms.txt b/docs/llms.txt index eec8d298..cf935da6 100644 --- a/docs/llms.txt +++ b/docs/llms.txt @@ -74,6 +74,11 @@ GitHub: https://github.com/pmxt-dev/pmxt - [Fetch Oracle Round](https://pmxt.dev/docs/api-reference/feedFetchOracleRound): Returns the latest Chainlink oracle round for a price feed. - [Fetch Oracle History](https://pmxt.dev/docs/api-reference/feedFetchOracleHistory): Returns historical Chainlink oracle rounds for a price feed. - [Fetch Historical Prices](https://pmxt.dev/docs/api-reference/feedFetchHistoricalPrices): Returns historical price data as tickers within a time range. +- [Get Auth Nonce](https://pmxt.dev/docs/api-reference/getAuthNonce): Get a cryptographic nonce for Web3 login. +- [Login With Signature](https://pmxt.dev/docs/api-reference/loginWithSignature): Login with a signed nonce to obtain session credentials. +- [Logout](https://pmxt.dev/docs/api-reference/logout): Logout and invalidate the current session. +- [Is Session Active](https://pmxt.dev/docs/api-reference/isSessionActive): Check if a session is active for the given wallet address. +- [Get Session](https://pmxt.dev/docs/api-reference/getSession): Get the stored session for a wallet address. - [Fetch Series](https://pmxt.dev/docs/api-reference/fetchSeries): Fetch the recurring series (fourth tier above Event -> Market -> Outcome) that this venue exposes. Returns an empty array on venues without a series concept (Limitless, Smarkets, Probable, Metaculus, Baozi, Hyperliquid, SuiBets, Polymarket US). - `params.id` -> a single matching series with its events populated where supported. - no params -> the full list, typically without nested events for payload size. - [Fetch Event Metadata](https://pmxt.dev/docs/api-reference/fetchEventMetadata): Fetch venue-native metadata for a specific event when the exchange exposes a dedicated metadata endpoint. Kalshi implements this for `GET /events/{event_ticker}/metadata`. - [Execute a read-only SQL query against ClickHouse](https://pmxt.dev/docs/api-reference/postV0Sql) diff --git a/sdks/python/API_REFERENCE.md b/sdks/python/API_REFERENCE.md index 44a18595..bb66c183 100644 --- a/sdks/python/API_REFERENCE.md +++ b/sdks/python/API_REFERENCE.md @@ -127,6 +127,133 @@ exchange.has ``` +--- +### `get_auth_nonce` + +Override in subclasses to force specific capability values. + + +**Signature:** + +```python +def get_auth_nonce(wallet_address: str) -> AuthNonceResponse: +``` + +**Parameters:** + +- `wallet_address` (str): The wallet address to authenticate + +**Returns:** AuthNonceResponse - Result + +**Example:** + +```python +exchange.get_auth_nonce(wallet_address="...") +``` + + +--- +### `login_with_signature` + +Login with a signed nonce to obtain session credentials. + + +**Signature:** + +```python +def login_with_signature(wallet_address: str, signature: str, nonce: str) -> AuthLoginResponse: +``` + +**Parameters:** + +- `wallet_address` (str): The wallet address +- `signature` (str): The signature of the nonce message +- `nonce` (str): The nonce that was signed + +**Returns:** AuthLoginResponse - Result + +**Example:** + +```python +exchange.login_with_signature(wallet_address="...", signature="...", nonce="...") +``` + + +--- +### `logout` + +Logout and invalidate the current session. + + +**Signature:** + +```python +def logout(wallet_address: Optional[str] = None) -> None: +``` + +**Parameters:** + +- `wallet_address` (str) - **Optional**: The wallet address to logout (optional) + +**Returns:** None - Result + +**Example:** + +```python +exchange.logout(wallet_address="...") +``` + + +--- +### `is_session_active` + +Check if a session is active for the given wallet address. + + +**Signature:** + +```python +def is_session_active(wallet_address: str) -> bool: +``` + +**Parameters:** + +- `wallet_address` (str): The wallet address to check + +**Returns:** bool - Result + +**Example:** + +```python +exchange.is_session_active(wallet_address="...") +``` + + +--- +### `get_session` + +Get the stored session for a wallet address. + + +**Signature:** + +```python +def get_session(wallet_address: str) -> Optional[AuthSession]: +``` + +**Parameters:** + +- `wallet_address` (str): The wallet address + +**Returns:** Optional[AuthSession] - Result + +**Example:** + +```python +exchange.get_session(wallet_address="...") +``` + + --- ### `load_markets` diff --git a/sdks/python/pmxt/client.py b/sdks/python/pmxt/client.py index dcf32247..7aa47f38 100644 --- a/sdks/python/pmxt/client.py +++ b/sdks/python/pmxt/client.py @@ -1300,6 +1300,88 @@ def load_markets(self, reload: bool = False) -> Dict[str, UnifiedMarket]: # BEGIN GENERATED METHODS + def get_auth_nonce(self, wallet_address: str) -> Dict[str, Any]: + try: + args = [] + args.append(wallet_address) + body: dict = {"args": args} + creds = self._get_credentials_dict() + if creds: + body["credentials"] = creds + url = f"{self._resolve_sidecar_host()}/api/{self.exchange_name}/getAuthNonce" + headers = {"Content-Type": "application/json", "Accept": "application/json"} + headers.update(self._get_auth_headers()) + response = self._fetch_with_retry( + lambda: self._api_client.call_api(method="POST", url=url, body=body, header_params=headers) + ) + response.read() + data = self._handle_response(json.loads(response.data)) + return data + except ApiException as e: + raise self._parse_api_exception(e) from None + + def login_with_signature(self, wallet_address: str, signature: str, nonce: str) -> Dict[str, Any]: + try: + args = [] + args.append(wallet_address) + args.append(signature) + args.append(nonce) + body: dict = {"args": args} + creds = self._get_credentials_dict() + if creds: + body["credentials"] = creds + url = f"{self._resolve_sidecar_host()}/api/{self.exchange_name}/loginWithSignature" + headers = {"Content-Type": "application/json", "Accept": "application/json"} + headers.update(self._get_auth_headers()) + response = self._fetch_with_retry( + lambda: self._api_client.call_api(method="POST", url=url, body=body, header_params=headers) + ) + response.read() + data = self._handle_response(json.loads(response.data)) + return data + except ApiException as e: + raise self._parse_api_exception(e) from None + + def logout(self, wallet_address: Optional[str] = None) -> None: + try: + args = [] + if wallet_address is not None: + args.append(wallet_address) + body: dict = {"args": args} + creds = self._get_credentials_dict() + if creds: + body["credentials"] = creds + url = f"{self._resolve_sidecar_host()}/api/{self.exchange_name}/logout" + headers = {"Content-Type": "application/json", "Accept": "application/json"} + headers.update(self._get_auth_headers()) + response = self._fetch_with_retry( + lambda: self._api_client.call_api(method="POST", url=url, body=body, header_params=headers) + ) + response.read() + self._handle_response(json.loads(response.data)) + except ApiException as e: + raise self._parse_api_exception(e) from None + + def is_session_active(self, wallet_address: str) -> bool: + try: + args = [] + args.append(wallet_address) + body: dict = {"args": args} + creds = self._get_credentials_dict() + if creds: + body["credentials"] = creds + url = f"{self._resolve_sidecar_host()}/api/{self.exchange_name}/isSessionActive" + headers = {"Content-Type": "application/json", "Accept": "application/json"} + headers.update(self._get_auth_headers()) + response = self._fetch_with_retry( + lambda: self._api_client.call_api(method="POST", url=url, body=body, header_params=headers) + ) + response.read() + data = self._handle_response(json.loads(response.data)) + return data + except ApiException as e: + raise self._parse_api_exception(e) from None + def fetch_markets(self, params: Optional[dict] = None, **kwargs) -> List[UnifiedMarket]: try: args = [] diff --git a/sdks/python/scripts/generate-client-methods.js b/sdks/python/scripts/generate-client-methods.js index ad0d4bb7..7688cf22 100644 --- a/sdks/python/scripts/generate-client-methods.js +++ b/sdks/python/scripts/generate-client-methods.js @@ -42,6 +42,11 @@ const PRESERVE_EXISTING_METHODS = new Set([ 'fetchBalance', 'unwatchOrderBook', 'fetchMatchedMarkets', + + 'getAuthNonce', + 'loginWithSignature', + 'logout', + 'isSessionActive', ]); function extractExistingPyMethod(generatedRegion, snakeName) { @@ -96,6 +101,9 @@ const TYPE_MAP = { // Pagination wrapper: detected by name, not structure — gets its own response handler PaginatedMarketsResult: { pyType: 'PaginatedMarketsResult', converter: null, pattern: 'paginatedMarkets' }, PaginatedEventsResult: { pyType: 'PaginatedEventsResult', converter: null, pattern: 'paginatedEvents' }, + + AuthNonceResponse: { pyType: 'Dict[str, Any]', converter: null, pattern: 'raw' }, + AuthLoginResponse: { pyType: 'Dict[str, Any]', converter: null, pattern: 'raw' }, }; // Parameter names that represent outcome IDs and should accept MarketOutcome. diff --git a/sdks/typescript/API_REFERENCE.md b/sdks/typescript/API_REFERENCE.md index 79ec1a85..f8917868 100644 --- a/sdks/typescript/API_REFERENCE.md +++ b/sdks/typescript/API_REFERENCE.md @@ -129,6 +129,133 @@ exchange.has ``` +--- +### `getAuthNonce` + +Override in subclasses to force specific capability values. + + +**Signature:** + +```typescript +async getAuthNonce(walletAddress: string): Promise +``` + +**Parameters:** + +- `walletAddress` (string): The wallet address to authenticate + +**Returns:** Promise - Result + +**Example:** + +```typescript +await exchange.getAuthNonce("...") +``` + + +--- +### `loginWithSignature` + +Login with a signed nonce to obtain session credentials. + + +**Signature:** + +```typescript +async loginWithSignature(walletAddress: string, signature: string, nonce: string): Promise +``` + +**Parameters:** + +- `walletAddress` (string): The wallet address +- `signature` (string): The signature of the nonce message +- `nonce` (string): The nonce that was signed + +**Returns:** Promise - Result + +**Example:** + +```typescript +await exchange.loginWithSignature("...", "...", "...") +``` + + +--- +### `logout` + +Logout and invalidate the current session. + + +**Signature:** + +```typescript +async logout(walletAddress?: string): Promise +``` + +**Parameters:** + +- `walletAddress` (string) - **Optional**: The wallet address to logout (optional) + +**Returns:** Promise - Result + +**Example:** + +```typescript +await exchange.logout({ walletAddress: "..." }) +``` + + +--- +### `isSessionActive` + +Check if a session is active for the given wallet address. + + +**Signature:** + +```typescript +async isSessionActive(walletAddress: string): Promise +``` + +**Parameters:** + +- `walletAddress` (string): The wallet address to check + +**Returns:** Promise - Result + +**Example:** + +```typescript +await exchange.isSessionActive("...") +``` + + +--- +### `getSession` + +Get the stored session for a wallet address. + + +**Signature:** + +```typescript +async getSession(walletAddress: string): Promise +``` + +**Parameters:** + +- `walletAddress` (string): The wallet address + +**Returns:** Promise - Result + +**Example:** + +```typescript +await exchange.getSession("...") +``` + + --- ### `loadMarkets` diff --git a/sdks/typescript/pmxt/client.ts b/sdks/typescript/pmxt/client.ts index 41eadab9..05a31de5 100644 --- a/sdks/typescript/pmxt/client.ts +++ b/sdks/typescript/pmxt/client.ts @@ -944,6 +944,108 @@ export abstract class Exchange { // BEGIN GENERATED METHODS + async getAuthNonce(walletAddress: string): Promise { + await this.initPromise; + try { + const args: any[] = []; + args.push(walletAddress); + const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/getAuthNonce`, { + method: 'POST', + headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() }, + body: JSON.stringify({ args, credentials: this.getCredentials() }), + }); + if (!response.ok) { + const body = await response.json().catch(() => ({})); + if (body.error && typeof body.error === "object") { + throw fromServerError(body.error); + } + throw new PmxtError(body.error?.message || response.statusText); + } + const json = await response.json(); + return this.handleResponse(json); + } catch (error) { + if (error instanceof PmxtError) throw error; + throw new PmxtError(`Failed to getAuthNonce: ${error}`); + } + } + + async loginWithSignature(walletAddress: string, signature: string, nonce: string): Promise { + await this.initPromise; + try { + const args: any[] = []; + args.push(walletAddress); + args.push(signature); + args.push(nonce); + const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/loginWithSignature`, { + method: 'POST', + headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() }, + body: JSON.stringify({ args, credentials: this.getCredentials() }), + }); + if (!response.ok) { + const body = await response.json().catch(() => ({})); + if (body.error && typeof body.error === "object") { + throw fromServerError(body.error); + } + throw new PmxtError(body.error?.message || response.statusText); + } + const json = await response.json(); + return this.handleResponse(json); + } catch (error) { + if (error instanceof PmxtError) throw error; + throw new PmxtError(`Failed to loginWithSignature: ${error}`); + } + } + + async logout(walletAddress?: string): Promise { + await this.initPromise; + try { + const args: any[] = []; + if (walletAddress !== undefined) args.push(walletAddress); + const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/logout`, { + method: 'POST', + headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() }, + body: JSON.stringify({ args, credentials: this.getCredentials() }), + }); + if (!response.ok) { + const body = await response.json().catch(() => ({})); + if (body.error && typeof body.error === "object") { + throw fromServerError(body.error); + } + throw new PmxtError(body.error?.message || response.statusText); + } + const json = await response.json(); + this.handleResponse(json); + } catch (error) { + if (error instanceof PmxtError) throw error; + throw new PmxtError(`Failed to logout: ${error}`); + } + } + + async isSessionActive(walletAddress: string): Promise { + await this.initPromise; + try { + const args: any[] = []; + args.push(walletAddress); + const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/isSessionActive`, { + method: 'POST', + headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() }, + body: JSON.stringify({ args, credentials: this.getCredentials() }), + }); + if (!response.ok) { + const body = await response.json().catch(() => ({})); + if (body.error && typeof body.error === "object") { + throw fromServerError(body.error); + } + throw new PmxtError(body.error?.message || response.statusText); + } + const json = await response.json(); + return this.handleResponse(json); + } catch (error) { + if (error instanceof PmxtError) throw error; + throw new PmxtError(`Failed to isSessionActive: ${error}`); + } + } + async loadMarkets(reload: boolean = false): Promise> { await this.initPromise; try { diff --git a/sdks/typescript/scripts/generate-client-methods.js b/sdks/typescript/scripts/generate-client-methods.js index 7eed6d61..b53c0a0d 100644 --- a/sdks/typescript/scripts/generate-client-methods.js +++ b/sdks/typescript/scripts/generate-client-methods.js @@ -88,6 +88,9 @@ const TYPE_MAP = { // Pagination wrapper — gets its own response handler PaginatedMarketsResult: { converter: null, pattern: 'paginatedMarkets' }, PaginatedEventsResult: { converter: null, pattern: 'paginatedEvents' }, + + AuthNonceResponse: { converter: null, pattern: 'single' }, + AuthLoginResponse: { converter: null, pattern: 'single' }, }; // SDK types that can appear in generated signatures without extra imports @@ -101,6 +104,9 @@ const SDK_PARAM_TYPES = new Set([ 'MyTradesParams', 'OrderHistoryParams', 'CreateOrderParams', 'MarketFilterCriteria', 'EventFilterCriteria', 'SubscriptionOption', + + 'AuthNonceResponse', + 'AuthLoginResponse', ]); // Parameter names that represent outcome IDs and should accept MarketOutcome.