Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion examples/market/getOrderbook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ async function main() {
const orderbook = await clobClient.getOrderBook(YES);
console.log("orderbook", orderbook);

const hash = clobClient.getOrderBookHash(orderbook);
const hash = await clobClient.getOrderBookHash(orderbook);
console.log("orderbook hash", hash);
}

Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@polymarket/clob-client-v2",
"description": "TypeScript client for Polymarket's CLOB",
"version": "1.0.3",
"version": "1.0.4",
"type": "module",
"main": "dist/index.cjs",
"types": "dist/index.d.ts",
Expand All @@ -25,6 +25,9 @@
"blockchain",
"ethereum"
],
"engines": {
"node": ">=20.10"
},
"packageManager": "pnpm@10.30.3",
"license": "MIT",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ export class ClobClient {
* @param orderbook
* @returns
*/
public getOrderBookHash(orderbook: OrderBookSummary): string {
public async getOrderBookHash(orderbook: OrderBookSummary): Promise<string> {
Comment thread
cursor[bot] marked this conversation as resolved.
return generateOrderBookSummaryHash(orderbook);
}

Expand Down
2 changes: 1 addition & 1 deletion src/headers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const createL2Headers = async (
}
const address = await getSignerAddress(signer);

const sig = buildPolyHmacSignature(
const sig = await buildPolyHmacSignature(
creds.secret,
ts,
l2HeaderArgs.method,
Expand Down
17 changes: 0 additions & 17 deletions src/http-helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ export const post = async (
return resp.data;
} catch (err: unknown) {
if (retryOnError && isTransientAxiosError(err)) {
console.log("[CLOB Client-v2] transient error, retrying once after 30 ms");
await sleep(30);
try {
const resp = await request(
Expand Down Expand Up @@ -110,15 +109,6 @@ export const del = async (endpoint: string, options?: RequestOptions): Promise<a
const errorHandling = (err: unknown) => {
if (axios.isAxiosError(err)) {
if (err.response) {
console.error(
"[CLOB Client] request error",
JSON.stringify({
status: err.response?.status,
statusText: err.response?.statusText,
data: err.response?.data,
config: err.response?.config,
}),
);
if (err.response?.data) {
if (
typeof err.response?.data === "string" ||
Expand All @@ -136,17 +126,10 @@ const errorHandling = (err: unknown) => {
}

if (err.message) {
console.error(
"[CLOB Client] request error",
JSON.stringify({
error: err.message,
}),
);
return { error: err.message };
}
}

console.error("[CLOB Client] request error", err);
return { error: err };
};

Expand Down
48 changes: 32 additions & 16 deletions src/signing/hmac.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,51 @@
import crypto from "node:crypto";

function replaceAll(s: string, search: string, replace: string) {
return s.split(search).join(replace);
}

/**
* Builds the canonical Polymarket CLOB HMAC signature
* @param signer
* @param key
* @param secret
* @param passphrase
* @param timestamp
* @param method
* @param requestPath
* @param body
* @returns string
*/
export const buildPolyHmacSignature = (
export const buildPolyHmacSignature = async (
secret: string,
timestamp: number,
method: string,
requestPath: string,
body?: string,
): string => {
): Promise<string> => {
Comment thread
cursor[bot] marked this conversation as resolved.
let message = timestamp + method + requestPath;
if (body !== undefined) {
message += body;
}
const base64Secret = Buffer.from(secret, "base64");
const hmac = crypto.createHmac("sha256", base64Secret);
const sig = hmac.update(message).digest("base64");

const binarySecret = atob(secret);
const keyBytes = new Uint8Array(binarySecret.length);
for (let i = 0; i < binarySecret.length; i++) {
keyBytes[i] = binarySecret.charCodeAt(i);
}

const key = await globalThis.crypto.subtle.importKey(
"raw",
keyBytes,
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"],
);

const sigBuffer = await globalThis.crypto.subtle.sign(
"HMAC",
key,
new TextEncoder().encode(message),
);

const sigBytes = new Uint8Array(sigBuffer);
let binary = "";
for (let i = 0; i < sigBytes.length; i++) {
binary += String.fromCharCode(sigBytes[i]);
}
// NOTE: Must be url safe base64 encoding, but keep base64 "=" suffix
// Convert '+' to '-'
// Convert '/' to '_'
const sigUrlSafe = replaceAll(replaceAll(sig, "+", "-"), "/", "_");
return sigUrlSafe;
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_");
};
45 changes: 34 additions & 11 deletions src/types/clob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ export interface OrderResponse {
success: boolean;
errorMsg: string;
orderID: string;
transactionsHashes: string[];
transactionsHashes?: string[];
tradeIDs?: string[];
status: string;
takingAmount: string;
makingAmount: string;
Expand Down Expand Up @@ -97,14 +98,14 @@ export interface MakerOrder {
fee_rate_bps: string;
asset_id: string;
outcome: string;
side: Side;
side?: Side;
builder_fee?: string;
builder_code?: string;
}

export interface Trade {
id: string;

taker_order_id: string;

market: string;
asset_id: string;
side: Side;
Expand All @@ -113,13 +114,15 @@ export interface Trade {
price: string;
status: string;
match_time: string;
match_time_nano?: string;
last_update: string;
outcome: string;
bucket_index: number;
owner: string;
maker_address: string;
maker_orders: MakerOrder[];
transaction_hash: string;
transaction_hash?: string;
err_msg?: string | null;
trader_side: "TAKER" | "MAKER";
}

Expand Down Expand Up @@ -222,7 +225,7 @@ export interface BalanceAllowanceParams {

export interface BalanceAllowanceResponse {
balance: string;
allowance: string;
allowances: Record<string, string>;
}

export interface OrderScoringParams {
Expand Down Expand Up @@ -282,7 +285,15 @@ export type TokenConditionMap = Record<string, string>;
export interface FeeDetails {
r?: number; // fee rate
e?: number; // fee exponent
to: boolean; // taker only
to?: boolean; // taker only (omitted when false)
}

export interface ClobRewards {
mi?: number; // min size
ma?: number; // max spread
e?: boolean; // enabled
smoa?: boolean; // skip min order age
moas?: number; // min order age seconds
}

export interface ClobToken {
Expand All @@ -292,12 +303,22 @@ export interface ClobToken {

export interface MarketDetails {
c: string; // condition ID
t: [ClobToken | null, ClobToken | null]; // YES and NO tokens
t: [ClobToken, ClobToken]; // YES and NO tokens
mts: number; // min tick size
nr: boolean; // neg risk
nr?: boolean; // neg risk (omitted when false)
fd?: FeeDetails; // platform fee details
mbf?: number; // v1 maker base fee
tbf?: number; // v1 taker base fee
mbf?: number; // maker base fee
tbf?: number; // taker base fee
r: ClobRewards | null; // rewards config (always present, null if unset)
ao?: boolean; // accepting orders
mos?: number; // min order size
sd?: number; // seconds delay
gst?: string; // game start time (ISO 8601)
cbos?: boolean; // clear book on start
aot?: string; // accepting orders timestamp (ISO 8601)
rfqe?: boolean; // RFQ enabled
itode?: boolean; // taker order delay enabled
ibce?: boolean; // blockaid check enabled
}

export interface PaginationPayload {
Expand Down Expand Up @@ -402,6 +423,8 @@ export interface BuilderTrade {
bucketIndex: number;
fee: string;
feeUsdc: string;
builderFee: string;
builderCode: string;
err_msg?: string | null;
createdAt: string | null;
updatedAt: string | null;
Expand Down
10 changes: 7 additions & 3 deletions src/utilities.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { createHash } from "node:crypto";
import type { OrderBookSummary, TickSize } from "./types/index.js";

export const roundNormal = (num: number, decimals: number): number => {
Expand Down Expand Up @@ -40,9 +39,14 @@ export const decimalPlaces = (num: number): number => {
* @param orderbook
* @returns
*/
export const generateOrderBookSummaryHash = (orderbook: OrderBookSummary): string => {
export const generateOrderBookSummaryHash = async (
orderbook: OrderBookSummary,
): Promise<string> => {
orderbook.hash = "";
const hash = createHash("sha1").update(JSON.stringify(orderbook)).digest("hex");
const data = new TextEncoder().encode(JSON.stringify(orderbook));
const hashBuffer = await globalThis.crypto.subtle.digest("SHA-1", data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hash = hashArray.map(b => b.toString(16).padStart(2, "0")).join("");
orderbook.hash = hash;
return hash;
};
Expand Down
4 changes: 2 additions & 2 deletions tests/signing/hmac.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { describe, expect, it } from "vitest";
import { buildPolyHmacSignature } from "../../src/signing/hmac";

describe("hmac", () => {
it("buildPolyHmacSignature", () => {
const signature = buildPolyHmacSignature(
it("buildPolyHmacSignature", async () => {
const signature = await buildPolyHmacSignature(
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
1000000,
"test-sign",
Expand Down
Loading