diff --git a/core/api-doc-config.generated.json b/core/api-doc-config.generated.json index 40aaff04..e0091b12 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-10T12:57:44.874Z. Do not edit manually.", "methods": { "has": { "summary": "HTTP verb for the endpoint (e.g. GET, POST). */", @@ -9,7 +9,7 @@ "type": "ExchangeHas", "description": "Result" }, - "source": "BaseExchange.ts:42" + "source": "BaseExchange.ts:43" }, "implicitApi": { "summary": "Override in subclasses to force specific capability values.", @@ -19,7 +19,7 @@ "type": "ImplicitApiMethodInfo[]", "description": "Result" }, - "source": "BaseExchange.ts:459" + "source": "BaseExchange.ts:460" }, "loadMarkets": { "summary": "Load and cache all markets from the exchange into `this.markets` and `this.marketsBySlug`.", @@ -36,7 +36,7 @@ "type": "Record", "description": "Dictionary of markets indexed by marketId" }, - "source": "BaseExchange.ts:572" + "source": "BaseExchange.ts:573" }, "fetchMarkets": { "summary": "Fetch markets with optional filtering, search, or slug lookup.", @@ -83,7 +83,63 @@ "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:610" + }, + "getAuthNonce": { + "summary": "Retrieves a cryptographic challenge string (nonce) from a Web3 exchange.", + "description": "Required for EIP-712 / wallet signature authentication workflows.", + "params": [ + { + "name": "walletAddress", + "type": "string", + "optional": false, + "description": "walletAddress" + } + ], + "returns": { + "type": "AuthNonceResponse", + "description": "Result" + }, + "source": "BaseExchange.ts:665" + }, + "loginWithSignature": { + "summary": "Submits a completed wallet signature to exchange authentication endpoints ", + "description": "to acquire temporary API credentials or establish a valid session.", + "params": [ + { + "name": "walletAddress", + "type": "string", + "optional": false, + "description": "walletAddress" + }, + { + "name": "signature", + "type": "string", + "optional": false, + "description": "signature" + }, + { + "name": "nonce", + "type": "string", + "optional": false, + "description": "nonce" + } + ], + "returns": { + "type": "SessionCredentials", + "description": "Result" + }, + "source": "BaseExchange.ts:673" + }, + "logout": { + "summary": "Programmatically terminates the active session or revokes the current credentials.", + "description": "Programmatically terminates the active session or revokes the current credentials.", + "params": [], + "returns": { + "type": "void", + "description": "Result" + }, + "source": "BaseExchange.ts:681" }, "fetchMarketsPaginated": { "summary": "Fetch markets with cursor-based pagination backed by a stable in-memory snapshot.", @@ -110,7 +166,7 @@ "type": "PaginatedMarketsResult", "description": "PaginatedMarketsResult with data, total, and optional nextCursor" }, - "source": "BaseExchange.ts:664" + "source": "BaseExchange.ts:688" }, "fetchEventsPaginated": { "summary": "Paginated variant of {@link fetchEvents}.", @@ -137,7 +193,7 @@ "type": "PaginatedEventsResult", "description": "PaginatedEventsResult with data, total, and optional nextCursor" }, - "source": "BaseExchange.ts:733" + "source": "BaseExchange.ts:757" }, "fetchEvents": { "summary": "Fetch events with optional keyword search.", @@ -175,7 +231,7 @@ "notes": [ "Some exchanges (like Limitless) may only support status 'active' for search results." ], - "source": "BaseExchange.ts:802" + "source": "BaseExchange.ts:826" }, "fetchSeries": { "summary": "Fetch the recurring series (fourth tier above Event -> Market -> Outcome)", @@ -192,7 +248,7 @@ "type": "UnifiedSeries[]", "description": "Array of unified series. Always an array, including the singular-lookup case." }, - "source": "BaseExchange.ts:841" + "source": "BaseExchange.ts:865" }, "fetchMarket": { "summary": "Fetch a single market by lookup parameters.", @@ -209,7 +265,7 @@ "type": "UnifiedMarket", "description": "A single unified market" }, - "source": "BaseExchange.ts:859" + "source": "BaseExchange.ts:883" }, "fetchEvent": { "summary": "Fetch a single event by lookup parameters.", @@ -226,7 +282,7 @@ "type": "UnifiedEvent", "description": "A single unified event" }, - "source": "BaseExchange.ts:959" + "source": "BaseExchange.ts:983" }, "fetchEventMetadata": { "summary": "Fetch venue-native metadata for a specific event when the exchange", @@ -243,7 +299,7 @@ "type": "Record", "description": "Result" }, - "source": "BaseExchange.ts:976" + "source": "BaseExchange.ts:1000" }, "fetchOHLCV": { "summary": "Fetch historical OHLCV (candlestick) price data for a specific market outcome.", @@ -271,7 +327,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:1011" }, "fetchOrderBook": { "summary": "Fetch the order book (bids/asks) for a specific outcome.", @@ -300,7 +356,7 @@ "type": "OrderBook", "description": "Order book with bids and asks. Returns OrderBook[] when" }, - "source": "BaseExchange.ts:1002" + "source": "BaseExchange.ts:1026" }, "fetchOrderBooks": { "summary": "Batch variant of {@link fetchOrderBook}. Fetches order books for", @@ -317,7 +373,7 @@ "type": "Record", "description": "A map keyed by the input id (preserving the caller's exact" }, - "source": "BaseExchange.ts:1030" + "source": "BaseExchange.ts:1054" }, "fetchTrades": { "summary": "Fetch raw trade history for a specific outcome.", @@ -343,7 +399,7 @@ "notes": [ "Polymarket requires an API key for trade history. Use fetchOHLCV for public historical data." ], - "source": "BaseExchange.ts:1043" + "source": "BaseExchange.ts:1067" }, "createOrder": { "summary": "Place a new order on the exchange.", @@ -360,7 +416,7 @@ "type": "Order", "description": "The created order" }, - "source": "BaseExchange.ts:1060" + "source": "BaseExchange.ts:1084" }, "buildOrder": { "summary": "Build an order payload without submitting it to the exchange.", @@ -377,7 +433,7 @@ "type": "BuiltOrder", "description": "A BuiltOrder containing the exchange-native payload" }, - "source": "BaseExchange.ts:1074" + "source": "BaseExchange.ts:1098" }, "submitOrder": { "summary": "Submit a pre-built order returned by buildOrder().", @@ -394,7 +450,7 @@ "type": "Order", "description": "The submitted order" }, - "source": "BaseExchange.ts:1086" + "source": "BaseExchange.ts:1110" }, "cancelOrder": { "summary": "Cancel an existing open order.", @@ -411,7 +467,7 @@ "type": "Order", "description": "The cancelled order" }, - "source": "BaseExchange.ts:1096" + "source": "BaseExchange.ts:1120" }, "fetchOrder": { "summary": "Fetch a specific order by ID.", @@ -428,7 +484,7 @@ "type": "Order", "description": "The order details" }, - "source": "BaseExchange.ts:1106" + "source": "BaseExchange.ts:1130" }, "fetchOpenOrders": { "summary": "Fetch all open orders, optionally filtered by market.", @@ -445,7 +501,7 @@ "type": "Order[]", "description": "Array of open orders" }, - "source": "BaseExchange.ts:1116" + "source": "BaseExchange.ts:1140" }, "fetchMyTrades": { "summary": "Fetch authenticated user trade history.", @@ -488,7 +544,7 @@ "type": "UserTrade[]", "description": "Array of user trades" }, - "source": "BaseExchange.ts:1126" + "source": "BaseExchange.ts:1150" }, "fetchClosedOrders": { "summary": "Fetch authenticated closed orders.", @@ -527,7 +583,7 @@ "type": "Order[]", "description": "Array of closed orders" }, - "source": "BaseExchange.ts:1142" + "source": "BaseExchange.ts:1166" }, "fetchAllOrders": { "summary": "Fetch authenticated order history across open and closed orders.", @@ -566,7 +622,7 @@ "type": "Order[]", "description": "Array of orders" }, - "source": "BaseExchange.ts:1157" + "source": "BaseExchange.ts:1181" }, "fetchPositions": { "summary": "Fetch current user positions across all markets.", @@ -583,7 +639,7 @@ "type": "Position[]", "description": "Array of user positions" }, - "source": "BaseExchange.ts:1172" + "source": "BaseExchange.ts:1196" }, "fetchBalance": { "summary": "Fetch account balances.", @@ -600,7 +656,7 @@ "type": "Balance[]", "description": "Array of account balances" }, - "source": "BaseExchange.ts:1182" + "source": "BaseExchange.ts:1206" }, "getExecutionPrice": { "summary": "Calculate the volume-weighted average execution price for a given order size.", @@ -629,7 +685,7 @@ "type": "number", "description": "Average execution price, or 0 if insufficient liquidity" }, - "source": "BaseExchange.ts:1192" + "source": "BaseExchange.ts:1216" }, "getExecutionPriceDetailed": { "summary": "Calculate detailed execution price information including partial fill data.", @@ -658,7 +714,7 @@ "type": "ExecutionPriceResult", "description": "Detailed execution result with price, filled amount, and fill status" }, - "source": "BaseExchange.ts:1205" + "source": "BaseExchange.ts:1229" }, "filterMarkets": { "summary": "Filter a list of markets by criteria.", @@ -681,7 +737,7 @@ "type": "UnifiedMarket[]", "description": "Filtered array of markets" }, - "source": "BaseExchange.ts:1221" + "source": "BaseExchange.ts:1245" }, "filterEvents": { "summary": "Filter a list of events by criteria.", @@ -704,7 +760,7 @@ "type": "UnifiedEvent[]", "description": "Filtered array of events" }, - "source": "BaseExchange.ts:1381" + "source": "BaseExchange.ts:1405" }, "watchOrderBook": { "summary": "Watch order book updates in real-time via WebSocket.", @@ -733,7 +789,7 @@ "type": "OrderBook", "description": "Promise that resolves with the current orderbook state" }, - "source": "BaseExchange.ts:1477" + "source": "BaseExchange.ts:1501" }, "watchOrderBooks": { "summary": "Watch multiple order books simultaneously via WebSocket.", @@ -762,7 +818,7 @@ "type": "Record", "description": "Promise that resolves with order books keyed by ID" }, - "source": "BaseExchange.ts:1490" + "source": "BaseExchange.ts:1514" }, "unwatchOrderBook": { "summary": "Unsubscribe from a previously watched order book stream.", @@ -779,7 +835,7 @@ "type": "void", "description": "Result" }, - "source": "BaseExchange.ts:1518" + "source": "BaseExchange.ts:1542" }, "watchTrades": { "summary": "Watch trade executions in real-time via WebSocket.", @@ -814,7 +870,7 @@ "type": "Trade[]", "description": "Promise that resolves with recent trades" }, - "source": "BaseExchange.ts:1531" + "source": "BaseExchange.ts:1555" }, "watchAddress": { "summary": "Stream activity for a public wallet address", @@ -837,7 +893,7 @@ "type": "SubscribedAddressSnapshot", "description": "Promise that resolves with the latest SubscribedAddressSnapshot snapshot" }, - "source": "BaseExchange.ts:1545" + "source": "BaseExchange.ts:1569" }, "unwatchAddress": { "summary": "Stop watching a previously registered wallet address and release its resource updates.", @@ -854,7 +910,7 @@ "type": "void", "description": "Result" }, - "source": "BaseExchange.ts:1558" + "source": "BaseExchange.ts:1582" }, "close": { "summary": "Close all WebSocket connections and clean up resources.", @@ -864,7 +920,7 @@ "type": "void", "description": "Result" }, - "source": "BaseExchange.ts:1567" + "source": "BaseExchange.ts:1591" }, "fetchMarketMatches": { "summary": "Find the same or related market on other venues. Two modes:", @@ -881,7 +937,7 @@ "type": "MatchResult[]", "description": "Array of matched markets with relation and confidence" }, - "source": "BaseExchange.ts:1581" + "source": "BaseExchange.ts:1605" }, "fetchMatches": { "summary": "fetchMatches", @@ -898,7 +954,7 @@ "type": "MatchResult[]", "description": "Result" }, - "source": "BaseExchange.ts:1597" + "source": "BaseExchange.ts:1621" }, "fetchEventMatches": { "summary": "Find the same or related event on other venues. Two modes:", @@ -915,7 +971,7 @@ "type": "EventMatchResult[]", "description": "Array of matched events with market-level match details" }, - "source": "BaseExchange.ts:1605" + "source": "BaseExchange.ts:1629" }, "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 +988,7 @@ "type": "PriceComparison[]", "description": "Array of price comparisons across venues" }, - "source": "BaseExchange.ts:1621" + "source": "BaseExchange.ts:1645" }, "fetchRelatedMarkets": { "summary": "Find related markets across venues. Discovers subset/superset market relationships", @@ -949,7 +1005,7 @@ "type": "PriceComparison[]", "description": "Array of subset/superset matches with live prices" }, - "source": "BaseExchange.ts:1631" + "source": "BaseExchange.ts:1655" }, "fetchMatchedMarkets": { "summary": "fetchMatchedMarkets", @@ -966,7 +1022,7 @@ "type": "MatchedMarketPair[]", "description": "Result" }, - "source": "BaseExchange.ts:1642" + "source": "BaseExchange.ts:1666" }, "fetchMatchedPrices": { "summary": "fetchMatchedPrices", @@ -983,7 +1039,7 @@ "type": "MatchedPricePair[]", "description": "Array of matched market pairs with prices from each venue" }, - "source": "BaseExchange.ts:1650" + "source": "BaseExchange.ts:1674" }, "fetchHedges": { "summary": "fetchHedges", @@ -1000,7 +1056,7 @@ "type": "PriceComparison[]", "description": "Array of subset/superset matches with live prices" }, - "source": "BaseExchange.ts:1661" + "source": "BaseExchange.ts:1685" }, "fetchArbitrage": { "summary": "fetchArbitrage", @@ -1017,7 +1073,7 @@ "type": "ArbitrageOpportunity[]", "description": "Array of arbitrage opportunities sorted by spread" }, - "source": "BaseExchange.ts:1671" + "source": "BaseExchange.ts:1695" }, "watchPrices": { "summary": "Watch AMM price updates for a market address (Limitless only).", diff --git a/core/src/BaseExchange.ts b/core/src/BaseExchange.ts index 469a32c9..a98fa3ab 100644 --- a/core/src/BaseExchange.ts +++ b/core/src/BaseExchange.ts @@ -1,6 +1,7 @@ import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig } from 'axios'; import { EventNotFound, MarketNotFound } from './errors'; import { SubscribedAddressSnapshot, SubscriptionOption } from './subscriber/base'; +import { AuthNonceResponse, SessionCredentials } from './types'; import { Balance, BuiltOrder, @@ -661,6 +662,29 @@ export abstract class PredictionMarketExchange { return limit !== undefined ? markets.slice(start, start + limit) : markets.slice(start); } + /** + * Retrieves a cryptographic challenge string (nonce) from a Web3 exchange. + * Required for EIP-712 / wallet signature authentication workflows. + */ + async getAuthNonce(walletAddress: string): Promise { + throw new Error(`getAuthNonce not implemented for exchange ${this.id}`); + } + + /** + * Submits a completed wallet signature to exchange authentication endpoints + * to acquire temporary API credentials or establish a valid session. + */ + async loginWithSignature(walletAddress: string, signature: string, nonce: string): Promise { + throw new Error(`loginWithSignature not implemented for exchange ${this.id}`); + } + + /** + * Programmatically terminates the active session or revokes the current credentials. + */ + async logout(): Promise { + throw new Error(`logout not implemented for exchange ${this.id}`); + } + /** * Fetch markets with cursor-based pagination backed by a stable in-memory snapshot. * diff --git a/core/src/exchanges/probable/auth.ts b/core/src/exchanges/probable/auth.ts index 9916d7c0..bda4f019 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, SessionCredentials } from '../../types'; /** * Manages Probable authentication and CLOB client initialization. @@ -80,3 +81,49 @@ export class ProbableAuth { return this.walletAddress; } } + +/** + * Fetches a cryptographic nonce for EIP-712 wallet login. + */ +export async function getAuthNonce(walletAddress: string, callApi: Function): Promise { + // OperationId based on Probable's OpenAPI spec for GET /public/api/v1/auth/nonce + const response = await callApi('getAuthNonce', { address: walletAddress }); + + return { + nonce: response.nonce, + // Fallbacks based on how different versions of the spec name the message field + messageToSign: response.message || response.messageToSign || `Sign to login: ${response.nonce}` + }; +} + +/** + * Submits the signed nonce to generate an API Key triplet. + */ +export async function loginWithSignature( + walletAddress: string, + signature: string, + nonce: string, + callApi: Function +): Promise { + // OperationId for POST /auth/login + const response = await callApi('postAuthLogin', { + address: walletAddress, + signature, + nonce + }); + + return { + apiKey: response.apiKey, + apiSecret: response.apiSecret, + passphrase: response.passphrase, + expiresAt: response.expiresAt + }; +} + +/** + * Destroys the current API key session. + */ +export async function logoutSession(callApi: Function): Promise { + // OperationId for POST /auth/logout + await callApi('postAuthLogout', {}); +} \ No newline at end of file diff --git a/core/src/router/types.ts b/core/src/router/types.ts index d32a024a..e5599ff8 100644 --- a/core/src/router/types.ts +++ b/core/src/router/types.ts @@ -92,6 +92,20 @@ export interface FetchMarketMatchesParams { sort?: 'confidence' | 'volume' | 'priceDifference'; } +export interface AuthNonceResponse { + nonce: string; + messageToSign: string; + expiresAt?: number; +} + +export interface SessionCredentials { + apiKey: string; + apiSecret: string; + passphrase?: string; + expiresAt?: number; + active?: boolean; +} + /** @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..0fafdd8a 100644 --- a/core/src/server/method-verbs.json +++ b/core/src/server/method-verbs.json @@ -19,6 +19,40 @@ } ] }, + "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": [] + }, "fetchMarketsPaginated": { "verb": "get", "args": [ diff --git a/core/src/server/openapi.yaml b/core/src/server/openapi.yaml index 428cf7d9..83297f0c 100644 --- a/core/src/server/openapi.yaml +++ b/core/src/server/openapi.yaml @@ -210,6 +210,112 @@ paths: description: >- Fetch markets with optional filtering, search, or slug lookup. Always hits the exchange API — results reflect the live state at the time of the call. + '/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: >- + Retrieves a cryptographic challenge string (nonce) from a Web3 exchange. Required for EIP-712 / wallet signature + authentication workflows. + '/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: >- + Submits a completed wallet signature to exchange authentication endpoints to acquire temporary API credentials + or establish a valid session. + '/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: 0 + items: {} + credentials: + $ref: '#/components/schemas/ExchangeCredentials' + responses: + '200': + description: Logout response + content: + application/json: + schema: + $ref: '#/components/schemas/BaseResponse' + description: Programmatically terminates the active session or revokes the current credentials. '/api/{exchange}/fetchMarketsPaginated': get: summary: Fetch Markets Paginated diff --git a/core/src/types.ts b/core/src/types.ts index eb122413..71f4fa22 100644 --- a/core/src/types.ts +++ b/core/src/types.ts @@ -221,6 +221,17 @@ export interface UserTrade extends Trade { blockNumber?: number | null; } +export interface AuthNonceResponse { + nonce: string; + messageToSign: string; +} + +export interface SessionCredentials { + apiKey: string; + apiSecret: string; + passphrase?: string; + expiresAt?: number; +} export interface QueuedPromise { /** Internal: resolver for a queued promise. */ diff --git a/core/test/exchanges/auth.test.ts b/core/test/exchanges/auth.test.ts new file mode 100644 index 00000000..332678fd --- /dev/null +++ b/core/test/exchanges/auth.test.ts @@ -0,0 +1,51 @@ +import { getAuthNonce, loginWithSignature, logoutSession } from '../../src/exchanges/probable/auth'; + +describe('Probable Auth Lifecycle', () => { + const mockWalletAddress = '0x1234567890abcdef1234567890abcdef12345678'; + const mockSignature = '0xabc123...'; + const mockNonce = 'random-nonce-string'; + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should fetch and map nonce correctly', async () => { + const mockCallApi = jest.fn().mockResolvedValue({ + nonce: mockNonce, + message: 'Sign this message to authenticate with Probable' + }); + + const result = await getAuthNonce(mockWalletAddress, mockCallApi); + + expect(mockCallApi).toHaveBeenCalledWith('getAuthNonce', { address: mockWalletAddress }); + expect(result.nonce).toBe(mockNonce); + expect(result.messageToSign).toBe('Sign this message to authenticate with Probable'); + }); + + it('should submit signature and map session credentials correctly', async () => { + const mockCallApi = jest.fn().mockResolvedValue({ + apiKey: 'key-123', + apiSecret: 'secret-456', + passphrase: 'passphrase-789', + expiresAt: 1700000000000 + }); + + const result = await loginWithSignature(mockWalletAddress, mockSignature, mockNonce, mockCallApi); + + expect(mockCallApi).toHaveBeenCalledWith('postAuthLogin', { + address: mockWalletAddress, + signature: mockSignature, + nonce: mockNonce + }); + expect(result.apiKey).toBe('key-123'); + expect(result.apiSecret).toBe('secret-456'); + }); + + it('should trigger logout successfully', async () => { + const mockCallApi = jest.fn().mockResolvedValue({}); + + await logoutSession(mockCallApi); + + expect(mockCallApi).toHaveBeenCalledWith('postAuthLogout', {}); + }); +}); \ No newline at end of file diff --git a/docs/api-reference/openapi.json b/docs/api-reference/openapi.json index 72ee155e..8d3f2ae5 100644 --- a/docs/api-reference/openapi.json +++ b/docs/api-reference/openapi.json @@ -650,6 +650,673 @@ ] } }, + "/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": "Retrieves a cryptographic challenge string (nonce) from a Web3 exchange. Required for EIP-712 / wallet signature authentication workflows.", + "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": "Submits a completed wallet signature to exchange authentication endpoints to acquire temporary API credentials or establish a valid 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.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": 0, + "items": {} + }, + "credentials": { + "$ref": "#/components/schemas/ExchangeCredentials" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Logout response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BaseResponse" + } + } + } + } + }, + "description": "Programmatically terminates the active session or revokes the current 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.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}/fetchMarketsPaginated": { "get": { "summary": "Fetch Markets Paginated", diff --git a/docs/docs.json b/docs/docs.json index dbdb4b93..0951d03a 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -216,6 +216,9 @@ { "group": "Other", "pages": [ + "POST /api/{exchange}/getAuthNonce", + "POST /api/{exchange}/loginWithSignature", + "POST /api/{exchange}/logout", "GET /api/{exchange}/fetchSeries", "GET /api/{exchange}/fetchEventMetadata" ], diff --git a/sdks/python/API_REFERENCE.md b/sdks/python/API_REFERENCE.md index 44a18595..9479f362 100644 --- a/sdks/python/API_REFERENCE.md +++ b/sdks/python/API_REFERENCE.md @@ -187,6 +187,83 @@ ordering — exchanges may reorder or add markets between requests. For stable i across pages, use `loadMarkets()` and paginate over `Object.values(exchange.markets)`. Some exchanges (like Limitless) may only support status 'active' for search results. +--- +### `get_auth_nonce` + +Retrieves a cryptographic challenge string (nonce) from a Web3 exchange. + + +**Signature:** + +```python +def get_auth_nonce(wallet_address: str) -> AuthNonceResponse: +``` + +**Parameters:** + +- `wallet_address` (str): walletAddress + +**Returns:** AuthNonceResponse - Result + +**Example:** + +```python +exchange.get_auth_nonce(wallet_address="...") +``` + + +--- +### `login_with_signature` + +Submits a completed wallet signature to exchange authentication endpoints + + +**Signature:** + +```python +def login_with_signature(wallet_address: str, signature: str, nonce: str) -> SessionCredentials: +``` + +**Parameters:** + +- `wallet_address` (str): walletAddress +- `signature` (str): signature +- `nonce` (str): nonce + +**Returns:** SessionCredentials - Result + +**Example:** + +```python +exchange.login_with_signature(wallet_address="...", signature="...", nonce="...") +``` + + +--- +### `logout` + +Programmatically terminates the active session or revokes the current credentials. + + +**Signature:** + +```python +def logout() -> None: +``` + +**Parameters:** + +- None + +**Returns:** None - Result + +**Example:** + +```python +exchange.logout() +``` + + --- ### `fetch_markets_paginated` diff --git a/sdks/python/pmxt/_exchanges.py b/sdks/python/pmxt/_exchanges.py index 224672b5..2c119cab 100644 --- a/sdks/python/pmxt/_exchanges.py +++ b/sdks/python/pmxt/_exchanges.py @@ -21,10 +21,6 @@ def __init__( base_url: Optional[str] = None, auto_start_server: Optional[bool] = None, pmxt_api_key: Optional[str] = None, - # NOTE: Generated wrapper; update the generator template in - # core/scripts/generate-python-exchanges.js in a follow-up. - wallet_address: Optional[str] = None, - signer: Optional[object] = None, ) -> None: """ Initialize Polymarket client. @@ -39,8 +35,6 @@ def __init__( base_url: Base URL of the PMXT sidecar server auto_start_server: Automatically start server if not running (default: True) pmxt_api_key: Hosted PMXT API key (optional; enables hosted mode) - wallet_address: Ethereum address for hosted reads/writes (optional) - signer: Optional callable for signing typed_data (optional) """ super().__init__( exchange_name="polymarket", @@ -51,8 +45,6 @@ def __init__( base_url=base_url, auto_start_server=auto_start_server, pmxt_api_key=pmxt_api_key, - wallet_address=wallet_address, - signer=signer, ) self.api_secret = api_secret @@ -84,8 +76,6 @@ def __init__( base_url: Optional[str] = None, auto_start_server: Optional[bool] = None, pmxt_api_key: Optional[str] = None, - wallet_address: Optional[str] = None, - signer: Optional[object] = None, ) -> None: """ Initialize Limitless client. @@ -98,8 +88,6 @@ def __init__( base_url: Base URL of the PMXT sidecar server auto_start_server: Automatically start server if not running (default: True) pmxt_api_key: Hosted PMXT API key (optional; enables hosted mode) - wallet_address: Ethereum address for hosted reads/writes (optional) - signer: Optional callable for signing typed_data (optional) """ super().__init__( exchange_name="limitless", @@ -108,8 +96,6 @@ def __init__( base_url=base_url, auto_start_server=auto_start_server, pmxt_api_key=pmxt_api_key, - wallet_address=wallet_address, - signer=signer, ) self.api_secret = api_secret @@ -284,7 +270,7 @@ def __init__( super().__init__( exchange_name="myriad", api_key=api_key, - wallet_address=wallet_address, + private_key=wallet_address, base_url=base_url, auto_start_server=auto_start_server, pmxt_api_key=pmxt_api_key, @@ -302,10 +288,6 @@ def __init__( base_url: Optional[str] = None, auto_start_server: Optional[bool] = None, pmxt_api_key: Optional[str] = None, - # NOTE: Generated wrapper; update the generator template in - # core/scripts/generate-python-exchanges.js in a follow-up. - wallet_address: Optional[str] = None, - signer: Optional[object] = None, ) -> None: """ Initialize Opinion client. @@ -317,8 +299,6 @@ def __init__( base_url: Base URL of the PMXT sidecar server auto_start_server: Automatically start server if not running (default: True) pmxt_api_key: Hosted PMXT API key (optional; enables hosted mode) - wallet_address: Ethereum address for hosted reads/writes (optional) - signer: Optional callable for signing typed_data (optional) """ super().__init__( exchange_name="opinion", @@ -328,8 +308,6 @@ def __init__( base_url=base_url, auto_start_server=auto_start_server, pmxt_api_key=pmxt_api_key, - wallet_address=wallet_address, - signer=signer, ) @@ -522,6 +500,7 @@ class Rain(Exchange): def __init__( self, + private_key: Optional[str] = None, base_url: Optional[str] = None, auto_start_server: Optional[bool] = None, pmxt_api_key: Optional[str] = None, @@ -530,12 +509,14 @@ def __init__( Initialize Rain client. Args: + private_key: Private key for authentication (optional) base_url: Base URL of the PMXT sidecar server auto_start_server: Automatically start server if not running (default: True) pmxt_api_key: Hosted PMXT API key (optional; enables hosted mode) """ super().__init__( exchange_name="rain", + private_key=private_key, base_url=base_url, auto_start_server=auto_start_server, pmxt_api_key=pmxt_api_key, @@ -551,7 +532,6 @@ def __init__( base_url: Optional[str] = None, auto_start_server: Optional[bool] = None, pmxt_api_key: Optional[str] = None, - wallet_address: Optional[str] = None, ) -> None: """ Initialize Hunch client. @@ -561,7 +541,6 @@ def __init__( base_url: Base URL of the PMXT sidecar server auto_start_server: Automatically start server if not running (default: True) pmxt_api_key: Hosted PMXT API key (optional; enables hosted mode) - wallet_address: EVM wallet address used for hosted reads/writes """ super().__init__( exchange_name="hunch", @@ -569,7 +548,6 @@ def __init__( base_url=base_url, auto_start_server=auto_start_server, pmxt_api_key=pmxt_api_key, - wallet_address=wallet_address, ) diff --git a/sdks/typescript/API_REFERENCE.md b/sdks/typescript/API_REFERENCE.md index 79ec1a85..ff87039c 100644 --- a/sdks/typescript/API_REFERENCE.md +++ b/sdks/typescript/API_REFERENCE.md @@ -189,6 +189,83 @@ ordering — exchanges may reorder or add markets between requests. For stable i across pages, use `loadMarkets()` and paginate over `Object.values(exchange.markets)`. Some exchanges (like Limitless) may only support status 'active' for search results. +--- +### `getAuthNonce` + +Retrieves a cryptographic challenge string (nonce) from a Web3 exchange. + + +**Signature:** + +```typescript +async getAuthNonce(walletAddress: string): Promise +``` + +**Parameters:** + +- `walletAddress` (string): walletAddress + +**Returns:** Promise - Result + +**Example:** + +```typescript +await exchange.getAuthNonce("...") +``` + + +--- +### `loginWithSignature` + +Submits a completed wallet signature to exchange authentication endpoints + + +**Signature:** + +```typescript +async loginWithSignature(walletAddress: string, signature: string, nonce: string): Promise +``` + +**Parameters:** + +- `walletAddress` (string): walletAddress +- `signature` (string): signature +- `nonce` (string): nonce + +**Returns:** Promise - Result + +**Example:** + +```typescript +await exchange.loginWithSignature("...", "...", "...") +``` + + +--- +### `logout` + +Programmatically terminates the active session or revokes the current credentials. + + +**Signature:** + +```typescript +async logout(): Promise +``` + +**Parameters:** + +- None + +**Returns:** Promise - Result + +**Example:** + +```typescript +await exchange.logout() +``` + + --- ### `fetchMarketsPaginated` diff --git a/sdks/typescript/pmxt/client.ts b/sdks/typescript/pmxt/client.ts index 41eadab9..3bf5a90e 100644 --- a/sdks/typescript/pmxt/client.ts +++ b/sdks/typescript/pmxt/client.ts @@ -1000,6 +1000,82 @@ export abstract class Exchange { } } + 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(): Promise { + await this.initPromise; + try { + const args: any[] = []; + 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 fetchMarketsPaginated(params?: any): Promise { await this.initPromise; try { diff --git a/sdks/typescript/pmxt/models.ts b/sdks/typescript/pmxt/models.ts index fc5ca349..a645a9ad 100644 --- a/sdks/typescript/pmxt/models.ts +++ b/sdks/typescript/pmxt/models.ts @@ -208,6 +208,8 @@ export interface ExecutionPriceResult { fullyFilled: boolean; } + + /** * A trade made by the authenticated user. */ diff --git a/sdks/typescript/scripts/generate-client-methods.js b/sdks/typescript/scripts/generate-client-methods.js index 7eed6d61..be6782dd 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' }, + SessionCredentials: { 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', + 'SessionCredentials', ]); // Parameter names that represent outcome IDs and should accept MarketOutcome.