Skip to content
This repository was archived by the owner on May 11, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ import type {
L2HeaderArgs,
L2PolyHeader,
L2WithBuilderHeader,
Market,
MarketPrice,
MarketReward,
MarketTradeEvent,
Expand All @@ -119,10 +120,12 @@ import type {
OrdersScoring,
OrdersScoringParams,
PaginationPayload,
PostOrderResponse,
PostOrdersArgs,
PriceHistoryFilterParams,
ReadonlyApiKeyResponse,
RewardsPercentages,
SimplifiedMarket,
TickSize,
TickSizes,
TotalUserEarning,
Expand Down Expand Up @@ -252,31 +255,35 @@ export class ClobClient {

public async getSamplingSimplifiedMarkets(
next_cursor = INITIAL_CURSOR,
): Promise<PaginationPayload> {
): Promise<PaginationPayload<SimplifiedMarket>> {
return this.get(`${this.host}${GET_SAMPLING_SIMPLIFIED_MARKETS}`, {
params: { next_cursor },
});
}

public async getSamplingMarkets(next_cursor = INITIAL_CURSOR): Promise<PaginationPayload> {
public async getSamplingMarkets(
next_cursor = INITIAL_CURSOR,
): Promise<PaginationPayload<Market>> {
return this.get(`${this.host}${GET_SAMPLING_MARKETS}`, {
params: { next_cursor },
});
}

public async getSimplifiedMarkets(next_cursor = INITIAL_CURSOR): Promise<PaginationPayload> {
public async getSimplifiedMarkets(
next_cursor = INITIAL_CURSOR,
): Promise<PaginationPayload<SimplifiedMarket>> {
return this.get(`${this.host}${GET_SIMPLIFIED_MARKETS}`, {
params: { next_cursor },
});
}

public async getMarkets(next_cursor = INITIAL_CURSOR): Promise<PaginationPayload> {
public async getMarkets(next_cursor = INITIAL_CURSOR): Promise<PaginationPayload<Market>> {
return this.get(`${this.host}${GET_MARKETS}`, {
params: { next_cursor },
});
}

public async getMarket(conditionID: string): Promise<any> {
public async getMarket(conditionID: string): Promise<Market> {
return this.get(`${this.host}${GET_MARKET}${conditionID}`);
}

Expand Down Expand Up @@ -936,7 +943,7 @@ export class ClobClient {
orderType: T = OrderType.GTC as T,
deferExec = false,
postOnly = false,
): Promise<any> {
): Promise<PostOrderResponse> {
const order = await this.createOrder(userOrder, options);
return this.postOrder(order, orderType, deferExec, postOnly);
}
Expand All @@ -946,7 +953,7 @@ export class ClobClient {
options?: Partial<CreateOrderOptions>,
orderType: T = OrderType.FOK as T,
deferExec = false,
): Promise<any> {
): Promise<PostOrderResponse> {
const order = await this.createMarketOrder(userMarketOrder, options);
return this.postOrder(order, orderType, deferExec);
}
Expand Down Expand Up @@ -1001,7 +1008,7 @@ export class ClobClient {
orderType: T = OrderType.GTC as T,
deferExec = false,
postOnly = false,
): Promise<any> {
): Promise<PostOrderResponse> {
this.canL2Auth();
const endpoint = POST_ORDER;
const orderPayload = orderToJson(
Expand Down
67 changes: 65 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,11 +385,74 @@ export interface FeeRates {
[tokenId: string]: number;
}

export interface PaginationPayload {
export interface PaginationPayload<T = unknown> {
readonly limit: number;
readonly count: number;
readonly next_cursor: string;
readonly data: any[];
readonly data: T[];
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PaginationPayload default breaks consumers using bare type

Medium Severity

The default type parameter for PaginationPayload changed from an effective any (via data: any[]) to T = unknown, making bare PaginationPayload resolve data to unknown[] instead of any[]. Since this is a publicly exported type from an npm package, any downstream consumer referencing PaginationPayload without a type argument will now get compilation errors when accessing properties on data items. Using T = any as the default would preserve backward compatibility while still enabling generic usage.

Fix in Cursor Fix in Web


export interface PostOrderResponse {
orderID: string;
status: string;
transactTime: string;
owner: string;
errorMsg?: string;
takingAmount?: string;
makingAmount?: string;
transactionsHashes?: string[];
}
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated

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 {
Expand Down
180 changes: 180 additions & 0 deletions tests/client/types.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import { ClobClient } from "../../src/client.ts";
import { Chain } from "../../src/types.ts";
import type {
Market,
PaginationPayload,
PostOrderResponse,
SimplifiedMarket,
} from "../../src/types.ts";

class StubClient extends ClobClient {
private _stub: unknown;
setStub(v: unknown) {
this._stub = v;
}
protected async get(): Promise<unknown> {
return this._stub;
}
protected async post(): Promise<unknown> {
return this._stub;
}
}

describe("typed return values", () => {
const client = new StubClient("http://localhost", Chain.AMOY);

describe("PaginationPayload<T>", () => {
it("data is typed as Market[] from getMarkets", async () => {
const payload: PaginationPayload<Market> = {
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<SimplifiedMarket> = {
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("PostOrderResponse", () => {
it("postOrder returns typed PostOrderResponse", async () => {
const response: PostOrderResponse = {
orderID: "0x123",
status: "matched",
transactTime: "1234567890",
owner: "0xabc",
};
client.setStub(response);
// postOrder requires L2 auth; test the type shape directly
const typed: PostOrderResponse = response;
expect(typed.orderID).to.equal("0x123");
expect(typed.status).to.equal("matched");
expect(typed.transactTime).to.equal("1234567890");
expect(typed.owner).to.equal("0xabc");
});

it("PostOrderResponse optional fields are optional", () => {
const minimal: PostOrderResponse = {
orderID: "0x456",
status: "live",
transactTime: "999",
owner: "0xdef",
};
expect(minimal.errorMsg).to.be.undefined;
expect(minimal.takingAmount).to.be.undefined;
expect(minimal.makingAmount).to.be.undefined;
expect(minimal.transactionsHashes).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);
});
});
});