From 3ee1a935a955d76a6a60c4c86fa4fefbe34e3adf Mon Sep 17 00:00:00 2001 From: pradeep-selva Date: Tue, 28 Apr 2026 15:52:09 +0530 Subject: [PATCH 1/7] ft: replace node:crypto with globalThis.crypto --- src/client.ts | 2 +- src/headers/index.ts | 2 +- src/signing/hmac.ts | 53 ++++++++++++++++++++++++-------------------- src/types/clob.ts | 28 ++++++++++++++++++----- src/utilities.ts | 13 +++++------ 5 files changed, 59 insertions(+), 39 deletions(-) diff --git a/src/client.ts b/src/client.ts index 1f4c77d..3f16d3a 100644 --- a/src/client.ts +++ b/src/client.ts @@ -423,7 +423,7 @@ export class ClobClient { * @param orderbook * @returns */ - public getOrderBookHash(orderbook: OrderBookSummary): string { + public async getOrderBookHash(orderbook: OrderBookSummary): Promise { return generateOrderBookSummaryHash(orderbook); } diff --git a/src/headers/index.ts b/src/headers/index.ts index c04bac9..03236ff 100644 --- a/src/headers/index.ts +++ b/src/headers/index.ts @@ -51,7 +51,7 @@ export const createL2Headers = async ( } const address = await getSignerAddress(signer); - const sig = buildPolyHmacSignature( + const sig = await buildPolyHmacSignature( creds.secret, ts, l2HeaderArgs.method, diff --git a/src/signing/hmac.ts b/src/signing/hmac.ts index bab280d..c953628 100644 --- a/src/signing/hmac.ts +++ b/src/signing/hmac.ts @@ -1,35 +1,40 @@ -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 - * @returns string - */ -export const buildPolyHmacSignature = ( +export const buildPolyHmacSignature = async ( secret: string, timestamp: number, method: string, requestPath: string, body?: string, -): string => { +): Promise => { 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"); - // NOTE: Must be url safe base64 encoding, but keep base64 "=" suffix - // Convert '+' to '-' - // Convert '/' to '_' - const sigUrlSafe = replaceAll(replaceAll(sig, "+", "-"), "/", "_"); - return sigUrlSafe; + 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]); + } + // URL-safe base64: '+' → '-', '/' → '_' + return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_"); }; diff --git a/src/types/clob.ts b/src/types/clob.ts index 5ff9a05..09e9cab 100644 --- a/src/types/clob.ts +++ b/src/types/clob.ts @@ -282,7 +282,15 @@ export type TokenConditionMap = Record; 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 { @@ -292,12 +300,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 { diff --git a/src/utilities.ts b/src/utilities.ts index a73ee6d..016045b 100644 --- a/src/utilities.ts +++ b/src/utilities.ts @@ -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 => { @@ -35,14 +34,12 @@ export const decimalPlaces = (num: number): number => { return arr[1].length; }; -/** - * Calculates the hash for the given orderbook - * @param orderbook - * @returns - */ -export const generateOrderBookSummaryHash = (orderbook: OrderBookSummary): string => { +export const generateOrderBookSummaryHash = async (orderbook: OrderBookSummary): Promise => { 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; }; From 4f17e828942f8d029c7dcfd5d51d5862c34348e8 Mon Sep 17 00:00:00 2001 From: pradeep-selva Date: Tue, 28 Apr 2026 16:29:33 +0530 Subject: [PATCH 2/7] rf: typw fixes --- src/types/clob.ts | 17 +++++++++++------ src/utilities.ts | 6 ++++-- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/types/clob.ts b/src/types/clob.ts index 09e9cab..b2f3a5c 100644 --- a/src/types/clob.ts +++ b/src/types/clob.ts @@ -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; @@ -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; @@ -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"; } @@ -222,7 +225,7 @@ export interface BalanceAllowanceParams { export interface BalanceAllowanceResponse { balance: string; - allowance: string; + allowances: Record; } export interface OrderScoringParams { @@ -420,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; diff --git a/src/utilities.ts b/src/utilities.ts index 016045b..8840fb6 100644 --- a/src/utilities.ts +++ b/src/utilities.ts @@ -34,12 +34,14 @@ export const decimalPlaces = (num: number): number => { return arr[1].length; }; -export const generateOrderBookSummaryHash = async (orderbook: OrderBookSummary): Promise => { +export const generateOrderBookSummaryHash = async ( + orderbook: OrderBookSummary, +): Promise => { orderbook.hash = ""; 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(""); + const hash = hashArray.map(b => b.toString(16).padStart(2, "0")).join(""); orderbook.hash = hash; return hash; }; From 7bb9d85aa9cef32386b4fbb91071466c5611a733 Mon Sep 17 00:00:00 2001 From: pradeep-selva Date: Tue, 28 Apr 2026 16:31:08 +0530 Subject: [PATCH 3/7] rf: remove logs --- src/http-helpers/index.ts | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/http-helpers/index.ts b/src/http-helpers/index.ts index 7e74435..7f08959 100644 --- a/src/http-helpers/index.ts +++ b/src/http-helpers/index.ts @@ -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( @@ -110,15 +109,6 @@ export const del = async (endpoint: string, options?: RequestOptions): Promise { 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" || @@ -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 }; }; From a295e583e38ef9a6de6a93eb2e90d908a4bd0d77 Mon Sep 17 00:00:00 2001 From: pradeep-selva Date: Tue, 28 Apr 2026 16:41:50 +0530 Subject: [PATCH 4/7] rf: comments --- src/signing/hmac.ts | 13 ++++++++++++- src/utilities.ts | 5 +++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/signing/hmac.ts b/src/signing/hmac.ts index c953628..30c579b 100644 --- a/src/signing/hmac.ts +++ b/src/signing/hmac.ts @@ -1,3 +1,12 @@ +/** + * Builds the canonical Polymarket CLOB HMAC signature + * @param secret + * @param timestamp + * @param method + * @param requestPath + * @param body + * @returns string + */ export const buildPolyHmacSignature = async ( secret: string, timestamp: number, @@ -35,6 +44,8 @@ export const buildPolyHmacSignature = async ( for (let i = 0; i < sigBytes.length; i++) { binary += String.fromCharCode(sigBytes[i]); } - // URL-safe base64: '+' → '-', '/' → '_' + // NOTE: Must be url safe base64 encoding, but keep base64 "=" suffix + // Convert '+' to '-' + // Convert '/' to '_' return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_"); }; diff --git a/src/utilities.ts b/src/utilities.ts index 8840fb6..94ef07c 100644 --- a/src/utilities.ts +++ b/src/utilities.ts @@ -34,6 +34,11 @@ export const decimalPlaces = (num: number): number => { return arr[1].length; }; +/** + * Calculates the hash for the given orderbook + * @param orderbook + * @returns + */ export const generateOrderBookSummaryHash = async ( orderbook: OrderBookSummary, ): Promise => { From 056fdc18f32f8937be0d0414dd466fd7fb9e484f Mon Sep 17 00:00:00 2001 From: pradeep-selva Date: Tue, 28 Apr 2026 16:44:15 +0530 Subject: [PATCH 5/7] bg: fix hmac sig --- examples/market/getOrderbook.ts | 2 +- tests/signing/hmac.test.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/market/getOrderbook.ts b/examples/market/getOrderbook.ts index 73969fe..f482598 100644 --- a/examples/market/getOrderbook.ts +++ b/examples/market/getOrderbook.ts @@ -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); } diff --git a/tests/signing/hmac.test.ts b/tests/signing/hmac.test.ts index b4f8adc..84867e6 100644 --- a/tests/signing/hmac.test.ts +++ b/tests/signing/hmac.test.ts @@ -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", From 87d4ba4e0cf9876b5df1bd274a92b47a516a51ec Mon Sep 17 00:00:00 2001 From: pradeep-selva Date: Tue, 28 Apr 2026 17:03:06 +0530 Subject: [PATCH 6/7] version: 1.0.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 17e3c85..ebbff88 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@polymarket/clob-client-v2", "description": "TypeScript client for Polymarket's CLOB", - "version": "1.0.2", + "version": "1.0.3", "type": "module", "main": "dist/index.cjs", "types": "dist/index.d.ts", From 27de6908a4e6de7f316589a0a88ee89cc1de17f6 Mon Sep 17 00:00:00 2001 From: pradeep-selva Date: Thu, 7 May 2026 15:12:30 +0530 Subject: [PATCH 7/7] ft: enforce node >=20.10; bump version: 1.0.4 --- package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index ebbff88..bfd418b 100644 --- a/package.json +++ b/package.json @@ -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", @@ -25,6 +25,9 @@ "blockchain", "ethereum" ], + "engines": { + "node": ">=20.10" + }, "packageManager": "pnpm@10.30.3", "license": "MIT", "repository": {