Skip to content

Commit 0fbfd19

Browse files
committed
feat: optimize bundle for server/client targets
1 parent 631499d commit 0fbfd19

File tree

6 files changed

+120
-74
lines changed

6 files changed

+120
-74
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
".": {
5252
"types": "./dist/index.d.ts",
5353
"import": "./dist/index.js",
54-
"require": "./dist/index.js"
54+
"default": "./dist/index.js"
5555
}
5656
},
5757
"scripts": {

src/signing/hmac.ts

Lines changed: 3 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,9 @@
1-
import { isBrowser } from "../utilities";
1+
import { createHmac } from "../utils/crypto";
22

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

7-
// Node.js/Bun/Deno HMAC implementation
8-
export async function createHmacNode(secret: string, message: string) {
9-
const crypto = await import("crypto");
10-
const base64Secret = Buffer.from(secret, "base64");
11-
const hmac = crypto.createHmac("sha256", base64Secret);
12-
return hmac.update(message).digest("base64");
13-
}
14-
15-
// Browser HMAC implementation using Web Crypto API
16-
export async function createHmacBrowser(secret: string, message: string) {
17-
// Decode base64 secret
18-
const binarySecret = Uint8Array.from(atob(secret), (c) => c.charCodeAt(0));
19-
20-
// Import the key
21-
const key = await window.crypto.subtle.importKey(
22-
"raw",
23-
binarySecret,
24-
{ name: "HMAC", hash: "SHA-256" },
25-
false,
26-
["sign"],
27-
);
28-
29-
// Create message buffer
30-
const encoder = new TextEncoder();
31-
const messageBuffer = encoder.encode(message);
32-
33-
// Sign the message
34-
const signature = await window.crypto.subtle.sign("HMAC", key, messageBuffer);
35-
36-
// Convert to base64
37-
const signatureArray = new Uint8Array(signature);
38-
let binary = "";
39-
40-
for (let i = 0; i < signatureArray.byteLength; i++) {
41-
binary += String.fromCharCode(signatureArray[i]!);
42-
}
43-
44-
return btoa(binary);
45-
}
46-
477
/**
488
* Builds the canonical Polymarket CLOB HMAC signature
499
* @param secret - Base64 encoded secret key
@@ -66,10 +26,8 @@ export const buildPolyHmacSignature = async (
6626
message += body;
6727
}
6828

69-
// Use appropriate HMAC implementation based on environment
70-
const sig = isBrowser()
71-
? await createHmacBrowser(secret, message)
72-
: await createHmacNode(secret, message);
29+
// Use cross-platform HMAC implementation
30+
const sig = await createHmac(secret, message);
7331

7432
// NOTE: Must be url safe base64 encoding, but keep base64 "=" suffix
7533
// Convert '+' to '-'

src/utilities.ts

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,12 @@
11
import { Side as UtilsSide, type SignedOrder } from "@dschz/polymarket-clob-order-utils";
22

33
import { type NewOrder, type OrderBookSummary, OrderType, Side, type TickSize } from "./types";
4-
5-
/**
6-
* Warning: SHA-1 is now considered vulnerable and should not be used for cryptographic applications.
7-
*/
8-
type HashAlgorithm = "sha1" | "sha256" | "sha384" | "sha512";
4+
import { createHash } from "./utils/crypto";
95

106
export const isBrowser = () => {
117
return typeof window !== "undefined" && typeof window.document !== "undefined";
128
};
139

14-
const createHash = async (algorithm: HashAlgorithm, data: string) => {
15-
if (isBrowser()) {
16-
const encoder = new TextEncoder();
17-
const dataBuffer = encoder.encode(data);
18-
19-
// https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest#supported_algorithms
20-
const cryptoAlgorithm = {
21-
sha1: "SHA-1",
22-
sha256: "SHA-256",
23-
sha384: "SHA-384",
24-
sha512: "SHA-512",
25-
}[algorithm];
26-
27-
const hashBuffer = await window.crypto.subtle.digest(cryptoAlgorithm, dataBuffer);
28-
const hashArray = Array.from(new Uint8Array(hashBuffer));
29-
30-
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
31-
}
32-
33-
const crypto = await import("crypto");
34-
return crypto.createHash(algorithm).update(data).digest("hex");
35-
};
36-
3710
export function orderToJson<T extends OrderType>(
3811
order: SignedOrder,
3912
owner: string,

src/utils/crypto-browser.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* Browser crypto utilities using Web Crypto API
3+
* This module should only be imported in browser environments
4+
*/
5+
6+
type HashAlgorithm = "sha1" | "sha256" | "sha384" | "sha512";
7+
8+
const algorithmMap: Record<HashAlgorithm, string> = {
9+
sha1: "SHA-1",
10+
sha256: "SHA-256",
11+
sha384: "SHA-384",
12+
sha512: "SHA-512",
13+
};
14+
15+
export const createHash = async (algorithm: string, data: string): Promise<string> => {
16+
const encoder = new TextEncoder();
17+
const dataBuffer = encoder.encode(data);
18+
19+
const cryptoAlgorithm = algorithmMap[algorithm as HashAlgorithm];
20+
if (!cryptoAlgorithm) {
21+
throw new Error(`Unsupported hash algorithm: ${algorithm}`);
22+
}
23+
24+
const hashBuffer = await window.crypto.subtle.digest(cryptoAlgorithm, dataBuffer);
25+
const hashArray = Array.from(new Uint8Array(hashBuffer));
26+
27+
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
28+
};
29+
30+
export const createHmac = async (secret: string, message: string): Promise<string> => {
31+
// Decode base64 secret
32+
const binarySecret = Uint8Array.from(atob(secret), (c) => c.charCodeAt(0));
33+
34+
// Import the key
35+
const key = await window.crypto.subtle.importKey(
36+
"raw",
37+
binarySecret,
38+
{ name: "HMAC", hash: "SHA-256" },
39+
false,
40+
["sign"],
41+
);
42+
43+
// Create message buffer
44+
const encoder = new TextEncoder();
45+
const messageBuffer = encoder.encode(message);
46+
47+
// Sign the message
48+
const signature = await window.crypto.subtle.sign("HMAC", key, messageBuffer);
49+
50+
// Convert to base64
51+
const signatureArray = new Uint8Array(signature);
52+
let binary = "";
53+
54+
for (let i = 0; i < signatureArray.byteLength; i++) {
55+
binary += String.fromCharCode(signatureArray[i]!);
56+
}
57+
58+
return btoa(binary);
59+
};

src/utils/crypto-node.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* Node.js/Bun/Deno crypto utilities
3+
* This module should only be imported in server environments
4+
*/
5+
6+
export const createHash = async (algorithm: string, data: string): Promise<string> => {
7+
const crypto = await import("crypto");
8+
return crypto.createHash(algorithm).update(data).digest("hex");
9+
};
10+
11+
export const createHmac = async (secret: string, message: string): Promise<string> => {
12+
const crypto = await import("crypto");
13+
const base64Secret = Buffer.from(secret, "base64");
14+
const hmac = crypto.createHmac("sha256", base64Secret);
15+
return hmac.update(message).digest("base64");
16+
};

src/utils/crypto.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* Cross-platform crypto utilities
3+
* Automatically selects the appropriate implementation based on environment
4+
*/
5+
6+
import { isBrowser } from "../utilities";
7+
8+
// Type-only imports to avoid bundling issues
9+
type CryptoModule = {
10+
createHash: (algorithm: string, data: string) => Promise<string>;
11+
createHmac: (secret: string, message: string) => Promise<string>;
12+
};
13+
14+
let cryptoModule: CryptoModule | null = null;
15+
16+
const getCryptoModule = async (): Promise<CryptoModule> => {
17+
if (cryptoModule) {
18+
return cryptoModule;
19+
}
20+
21+
if (isBrowser()) {
22+
// Dynamic import that won't be included in server bundles
23+
cryptoModule = await import("./crypto-browser");
24+
} else {
25+
// Dynamic import that won't be included in browser bundles when using proper bundler config
26+
cryptoModule = await import("./crypto-node");
27+
}
28+
29+
return cryptoModule;
30+
};
31+
32+
export const createHash = async (algorithm: string, data: string): Promise<string> => {
33+
const crypto = await getCryptoModule();
34+
return crypto.createHash(algorithm, data);
35+
};
36+
37+
export const createHmac = async (secret: string, message: string): Promise<string> => {
38+
const crypto = await getCryptoModule();
39+
return crypto.createHmac(secret, message);
40+
};

0 commit comments

Comments
 (0)