diff --git a/src/client.ts b/src/client.ts index 7a2ff56..f967ab3 100644 --- a/src/client.ts +++ b/src/client.ts @@ -102,6 +102,7 @@ import type { L2HeaderArgs, L2PolyHeader, L2WithBuilderHeader, + Market, MarketPrice, MarketReward, MarketTradeEvent, @@ -114,6 +115,7 @@ import type { OrderBookSummary, OrderMarketCancelParams, OrderPayload, + OrderResponse, OrderScoring, OrderScoringParams, OrdersScoring, @@ -123,6 +125,7 @@ import type { PriceHistoryFilterParams, ReadonlyApiKeyResponse, RewardsPercentages, + SimplifiedMarket, TickSize, TickSizes, TotalUserEarning, @@ -252,31 +255,35 @@ export class ClobClient { public async getSamplingSimplifiedMarkets( next_cursor = INITIAL_CURSOR, - ): Promise { + ): Promise> { return this.get(`${this.host}${GET_SAMPLING_SIMPLIFIED_MARKETS}`, { params: { next_cursor }, }); } - public async getSamplingMarkets(next_cursor = INITIAL_CURSOR): Promise { + public async getSamplingMarkets( + next_cursor = INITIAL_CURSOR, + ): Promise> { return this.get(`${this.host}${GET_SAMPLING_MARKETS}`, { params: { next_cursor }, }); } - public async getSimplifiedMarkets(next_cursor = INITIAL_CURSOR): Promise { + public async getSimplifiedMarkets( + next_cursor = INITIAL_CURSOR, + ): Promise> { return this.get(`${this.host}${GET_SIMPLIFIED_MARKETS}`, { params: { next_cursor }, }); } - public async getMarkets(next_cursor = INITIAL_CURSOR): Promise { + public async getMarkets(next_cursor = INITIAL_CURSOR): Promise> { return this.get(`${this.host}${GET_MARKETS}`, { params: { next_cursor }, }); } - public async getMarket(conditionID: string): Promise { + public async getMarket(conditionID: string): Promise { return this.get(`${this.host}${GET_MARKET}${conditionID}`); } @@ -936,7 +943,7 @@ export class ClobClient { orderType: T = OrderType.GTC as T, deferExec = false, postOnly = false, - ): Promise { + ): Promise { const order = await this.createOrder(userOrder, options); return this.postOrder(order, orderType, deferExec, postOnly); } @@ -946,7 +953,7 @@ export class ClobClient { options?: Partial, orderType: T = OrderType.FOK as T, deferExec = false, - ): Promise { + ): Promise { const order = await this.createMarketOrder(userMarketOrder, options); return this.postOrder(order, orderType, deferExec); } @@ -1001,7 +1008,7 @@ export class ClobClient { orderType: T = OrderType.GTC as T, deferExec = false, postOnly = false, - ): Promise { + ): Promise { this.canL2Auth(); const endpoint = POST_ORDER; const orderPayload = orderToJson( diff --git a/src/types.ts b/src/types.ts index 2464336..325d89a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -192,13 +192,15 @@ export interface BanStatus { } export interface OrderResponse { + orderID: string; + status: string; success: boolean; errorMsg: string; - orderID: string; transactionsHashes: string[]; - status: string; takingAmount: string; makingAmount: string; + transactTime?: string; + owner?: string; } export interface OpenOrder { @@ -385,11 +387,63 @@ export interface FeeRates { [tokenId: string]: number; } -export interface PaginationPayload { +export interface PaginationPayload { readonly limit: number; readonly count: number; readonly next_cursor: string; - readonly data: any[]; + readonly data: T[]; +} + +export interface SimplifiedMarket { + condition_id: string; + tokens: Token[]; + rewards: { + min_size: number; + max_spread: number; + }; + min_incentive_size: string; + max_incentive_spread: string; + accepting_orders: boolean; + enable_order_book: boolean; +} + +export interface Market { + condition_id: string; + question_id: string; + question: string; + description: string; + market_slug: string; + end_date_iso: string; + game_start_time: string; + seconds_delay: number; + fpmm: string; + maker_base_fee: number; + taker_base_fee: number; + notifications_enabled: boolean; + neg_risk: boolean; + neg_risk_market_id: string; + neg_risk_request_id: string; + icon: string; + image: string; + tokens: Token[]; + tags: string[]; + is_50_50_outcome: boolean; + accepting_orders: boolean; + accepting_order_timestamp: string | null; + minimum_order_size: string; + minimum_tick_size: string; + active: boolean; + closed: boolean; + archived: boolean; + enable_order_book: boolean; + rewards: { + min_size: number; + max_spread: number; + event_start_date: string; + event_end_date: string; + in_game_multiplier: number; + reward_epoch: number; + }; } export interface MarketTradeEvent { diff --git a/tests/client/types.test.ts b/tests/client/types.test.ts new file mode 100644 index 0000000..672a00f --- /dev/null +++ b/tests/client/types.test.ts @@ -0,0 +1,187 @@ +import { ClobClient } from "../../src/client.ts"; +import type { + Market, + OrderResponse, + PaginationPayload, + SimplifiedMarket, +} from "../../src/types.ts"; +import { Chain } from "../../src/types.ts"; + +class StubClient extends ClobClient { + private _stub: unknown; + setStub(v: unknown) { + this._stub = v; + } + protected async get(): Promise { + return this._stub; + } + protected async post(): Promise { + return this._stub; + } +} + +describe("typed return values", () => { + const client = new StubClient("http://localhost", Chain.AMOY); + + describe("PaginationPayload", () => { + it("data is typed as Market[] from getMarkets", async () => { + const payload: PaginationPayload = { + limit: 100, + count: 1, + next_cursor: "LTE=", + data: [ + { + condition_id: "0xabc", + question_id: "0xdef", + question: "Will X happen?", + description: "", + market_slug: "will-x-happen", + end_date_iso: "2025-01-01T00:00:00Z", + game_start_time: "", + seconds_delay: 0, + fpmm: "", + maker_base_fee: 0, + taker_base_fee: 0, + notifications_enabled: false, + neg_risk: false, + neg_risk_market_id: "", + neg_risk_request_id: "", + icon: "", + image: "", + tokens: [], + tags: [], + is_50_50_outcome: false, + accepting_orders: true, + accepting_order_timestamp: null, + minimum_order_size: "5", + minimum_tick_size: "0.01", + active: true, + closed: false, + archived: false, + enable_order_book: true, + rewards: { + min_size: 0, + max_spread: 0, + event_start_date: "", + event_end_date: "", + in_game_multiplier: 1, + reward_epoch: 0, + }, + }, + ], + }; + client.setStub(payload); + const result = await client.getMarkets(); + expect(result.data[0].condition_id).to.equal("0xabc"); + expect(result.data[0].accepting_orders).to.equal(true); + }); + + it("data is typed as SimplifiedMarket[] from getSimplifiedMarkets", async () => { + const payload: PaginationPayload = { + limit: 100, + count: 1, + next_cursor: "LTE=", + data: [ + { + condition_id: "0xabc", + tokens: [{ token_id: "1", outcome: "Yes", price: 0.5 }], + rewards: { min_size: 5, max_spread: 0.02 }, + min_incentive_size: "5", + max_incentive_spread: "0.02", + accepting_orders: true, + enable_order_book: true, + }, + ], + }; + client.setStub(payload); + const result = await client.getSimplifiedMarkets(); + expect(result.data[0].condition_id).to.equal("0xabc"); + expect(result.data[0].tokens[0].outcome).to.equal("Yes"); + }); + }); + + describe("OrderResponse", () => { + it("postOrder returns typed OrderResponse", async () => { + const response: OrderResponse = { + orderID: "0x123", + status: "matched", + success: true, + errorMsg: "", + transactionsHashes: ["0xhash1"], + takingAmount: "100", + makingAmount: "50", + transactTime: "1234567890", + owner: "0xabc", + }; + client.setStub(response); + // postOrder requires L2 auth; test the type shape directly + const typed: OrderResponse = response; + expect(typed.orderID).to.equal("0x123"); + expect(typed.status).to.equal("matched"); + expect(typed.success).to.equal(true); + expect(typed.transactTime).to.equal("1234567890"); + expect(typed.owner).to.equal("0xabc"); + }); + + it("OrderResponse optional fields are optional", () => { + const minimal: OrderResponse = { + orderID: "0x456", + status: "live", + success: false, + errorMsg: "", + transactionsHashes: [], + takingAmount: "0", + makingAmount: "0", + }; + expect(minimal.transactTime).to.be.undefined; + expect(minimal.owner).to.be.undefined; + }); + }); + + describe("getMarket", () => { + it("returns a Market (not any)", async () => { + const market: Market = { + condition_id: "0xfeed", + question_id: "0xbeef", + question: "Test?", + description: "", + market_slug: "test", + end_date_iso: "", + game_start_time: "", + seconds_delay: 0, + fpmm: "", + maker_base_fee: 0, + taker_base_fee: 0, + notifications_enabled: false, + neg_risk: false, + neg_risk_market_id: "", + neg_risk_request_id: "", + icon: "", + image: "", + tokens: [], + tags: [], + is_50_50_outcome: false, + accepting_orders: false, + accepting_order_timestamp: null, + minimum_order_size: "5", + minimum_tick_size: "0.01", + active: false, + closed: true, + archived: false, + enable_order_book: false, + rewards: { + min_size: 0, + max_spread: 0, + event_start_date: "", + event_end_date: "", + in_game_multiplier: 1, + reward_epoch: 0, + }, + }; + client.setStub(market); + const result = await client.getMarket("0xfeed"); + expect(result.condition_id).to.equal("0xfeed"); + expect(result.closed).to.equal(true); + }); + }); +});