Skip to content

Commit 5745c59

Browse files
committed
feat: add builder-authorized client support
1 parent 602b795 commit 5745c59

14 files changed

Lines changed: 392 additions & 102 deletions

File tree

.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
11
POLYMARKET_TEST_PRIVATE_KEY=0xYOUR_TEST_PRIVATE_KEY
2+
POLYMARKET_BUILDER_API_KEY=YOUR_BUILDER_API_KEY
3+
POLYMARKET_BUILDER_SECRET=YOUR_BUILDER_SECRET
4+
POLYMARKET_BUILDER_PASSPHRASE=YOUR_BUILDER_PASSPHRASE

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ jobs:
4444
- name: Test
4545
run: pnpm test
4646
env:
47+
POLYMARKET_BUILDER_API_KEY: ${{ secrets.POLYMARKET_BUILDER_API_KEY }}
48+
POLYMARKET_BUILDER_PASSPHRASE: ${{ secrets.POLYMARKET_BUILDER_PASSPHRASE }}
49+
POLYMARKET_BUILDER_SECRET: ${{ secrets.POLYMARKET_BUILDER_SECRET }}
4750
POLYMARKET_TEST_PRIVATE_KEY: ${{ secrets.POLYMARKET_TEST_PRIVATE_KEY }}
4851

4952
- name: Build

env.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,8 @@ declare namespace NodeJS {
22
interface ProcessEnv {
33
CI: string | undefined;
44
POLYMARKET_TEST_PRIVATE_KEY: string | undefined;
5+
POLYMARKET_BUILDER_API_KEY: string | undefined;
6+
POLYMARKET_BUILDER_SECRET: string | undefined;
7+
POLYMARKET_BUILDER_PASSPHRASE: string | undefined;
58
}
69
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { z } from 'zod';
2+
import { TokenIdSchema } from '../shared';
3+
import { OrderSideSchema } from './order';
4+
5+
export const BuilderTradeSchema = z.looseObject({
6+
id: z.string(),
7+
tradeType: z.string(),
8+
takerOrderHash: z.string(),
9+
builder: z.string(),
10+
market: z.string(),
11+
assetId: TokenIdSchema,
12+
side: OrderSideSchema,
13+
size: z.string(),
14+
sizeUsdc: z.string(),
15+
price: z.string(),
16+
status: z.string(),
17+
outcome: z.string(),
18+
outcomeIndex: z.number().int(),
19+
owner: z.string(),
20+
maker: z.string(),
21+
transactionHash: z.string(),
22+
matchTime: z.string(),
23+
bucketIndex: z.number().int(),
24+
fee: z.string(),
25+
feeUsdc: z.string(),
26+
err_msg: z.string().nullable().optional(),
27+
createdAt: z.string().optional(),
28+
updatedAt: z.string().optional(),
29+
});
30+
export type BuilderTrade = z.infer<typeof BuilderTradeSchema>;
31+
32+
export const PaginatedBuilderTradesSchema = z.object({
33+
limit: z.number().int(),
34+
count: z.number().int(),
35+
next_cursor: z.string(),
36+
data: z.array(BuilderTradeSchema),
37+
});
38+
export type PaginatedBuilderTrades = z.infer<
39+
typeof PaginatedBuilderTradesSchema
40+
>;

packages/bindings/src/clob/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export * from './account';
22
export * from './api-key';
3+
export * from './builder';
34
export * from './cancel';
45
export * from './fee-rate';
56
export * from './market-data';

packages/bindings/src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
export * from './clob';
21
export * from './shared';
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { describe, expect, it } from 'vitest';
2+
// biome-ignore lint/style/noRestrictedImports: intentional
3+
import { createPublicClient } from '../node';
4+
import { builderCredentials } from '../testing';
5+
import { listBuilderTrades } from './builders';
6+
7+
describe('Builders', () => {
8+
describe('listBuilderTrades', () => {
9+
it('lists builder trades', async () => {
10+
const client = createPublicClient({
11+
builder: builderCredentials,
12+
});
13+
14+
const result = await listBuilderTrades(client);
15+
16+
expect(Array.isArray(result)).toBe(true);
17+
});
18+
});
19+
});
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import {
2+
type BuilderTrade,
3+
PaginatedBuilderTradesSchema,
4+
} from '@polymarket/bindings/clob';
5+
import { unwrap } from '@polymarket/types';
6+
import { z } from 'zod';
7+
import type { Client } from '../clients';
8+
import type {
9+
RateLimitError,
10+
RequestRejectedError,
11+
TransportError,
12+
UnexpectedResponseError,
13+
UserInputError,
14+
} from '../errors';
15+
import { parseUserInput } from '../input';
16+
import { validateWith } from '../response';
17+
import { toSearchParams } from './params';
18+
19+
const ListBuilderTradesRequestSchema = z.object({
20+
after: z.string().optional(),
21+
before: z.string().optional(),
22+
builder: z.string().optional(),
23+
id: z.string().optional(),
24+
market: z.string().optional(),
25+
tokenId: z.string().optional(),
26+
});
27+
28+
export type ListBuilderTradesRequest = z.input<
29+
typeof ListBuilderTradesRequestSchema
30+
>;
31+
32+
export type ListBuilderTradesError =
33+
| RateLimitError
34+
| RequestRejectedError
35+
| TransportError
36+
| UnexpectedResponseError
37+
| UserInputError;
38+
39+
/**
40+
* Lists builder-attributed trades.
41+
*
42+
* @throws {@link ListBuilderTradesError}
43+
* Thrown when the request is invalid, rejected, rate limited, interrupted by transport issues, or returns an unexpected response.
44+
*
45+
* @example
46+
* ```ts
47+
* const trades = await listBuilderTrades(client, {})
48+
*
49+
* // trades === BuilderTrade[]
50+
* ```
51+
*/
52+
export async function listBuilderTrades(
53+
client: Client,
54+
request: ListBuilderTradesRequest = {},
55+
): Promise<BuilderTrade[]> {
56+
const params = parseUserInput(request, ListBuilderTradesRequestSchema);
57+
58+
return listAllBuilderTradePages(async (nextCursor) => {
59+
const requestPath = '/builder/trades';
60+
61+
return unwrap(
62+
client.clob
63+
.get(requestPath, {
64+
params: toSearchParams(
65+
{
66+
after: params.after,
67+
before: params.before,
68+
builder: params.builder,
69+
id: params.id,
70+
market: params.market,
71+
nextCursor,
72+
tokenId: params.tokenId,
73+
},
74+
{
75+
after: 'after',
76+
before: 'before',
77+
builder: 'builder',
78+
id: 'id',
79+
market: 'market',
80+
nextCursor: 'next_cursor',
81+
tokenId: 'asset_id',
82+
},
83+
),
84+
})
85+
.andThen(validateWith(PaginatedBuilderTradesSchema)),
86+
);
87+
});
88+
}
89+
90+
async function listAllBuilderTradePages(
91+
fetchPage: (nextCursor: string) => Promise<{
92+
data: BuilderTrade[];
93+
next_cursor: string;
94+
}>,
95+
): Promise<BuilderTrade[]> {
96+
let nextCursor = 'MA==';
97+
const results: BuilderTrade[] = [];
98+
99+
while (nextCursor !== 'LTE=') {
100+
const response = await fetchPage(nextCursor);
101+
102+
results.push(...response.data);
103+
nextCursor = response.next_cursor;
104+
}
105+
106+
return results;
107+
}

packages/client/src/actions/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export * from './account';
22
export * from './activity';
33
export * from './auth';
4+
export * from './builders';
45
export * from './clob';
56
export * from './comments';
67
export * from './events';

0 commit comments

Comments
 (0)