Skip to content

Commit 075bbbc

Browse files
committed
feat(sdk): adds ox for hex and byte utils
1 parent 07af647 commit 075bbbc

File tree

6 files changed

+348
-472
lines changed

6 files changed

+348
-472
lines changed

.changeset/nice-gifts-argue.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"thirdweb": patch
3+
---
4+
5+
Adds ox for internal utilities

packages/thirdweb/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@
217217
"fuse.js": "7.0.0",
218218
"input-otp": "^1.4.1",
219219
"mipd": "0.0.7",
220+
"ox": "^0.1.1",
220221
"uqr": "0.1.2",
221222
"viem": "2.21.42"
222223
},

packages/thirdweb/src/utils/encoding/hex.ts

Lines changed: 22 additions & 213 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,8 @@
1-
import { cachedTextDecoder } from "../text-decoder.js";
2-
import { cachedTextEncoder } from "../text-encoder.js";
3-
// slightly tweaked re-exports from viem for the moment
4-
import { assertSize } from "./helpers/assert-size.js";
5-
import { charCodeToBase16 } from "./helpers/charcode-to-base-16.js";
1+
import { Hex as ox__Hex } from "ox";
62
import type { Hex } from "./helpers/is-hex.js";
73

84
export { type Hex, isHex, type IsHexOptions } from "./helpers/is-hex.js";
95

10-
type TrimOptions = {
11-
dir?: "left" | "right";
12-
};
13-
type TrimReturnType<TValue extends Uint8Array | Hex> = TValue extends Hex
14-
? Hex
15-
: Uint8Array;
16-
17-
function trim<TValue extends Uint8Array | Hex>(
18-
hexOrBytes: TValue,
19-
options: TrimOptions = {},
20-
): TrimReturnType<TValue> {
21-
const dir = options.dir || "left";
22-
// biome-ignore lint/suspicious/noExplicitAny: TODO: fix any
23-
let data: any =
24-
typeof hexOrBytes === "string" ? hexOrBytes.replace("0x", "") : hexOrBytes;
25-
26-
let sliceLength = 0;
27-
for (let i = 0; i < data.length - 1; i++) {
28-
if (data[dir === "left" ? i : data.length - i - 1].toString() === "0") {
29-
sliceLength++;
30-
} else {
31-
break;
32-
}
33-
}
34-
data =
35-
dir === "left"
36-
? data.slice(sliceLength)
37-
: data.slice(0, data.length - sliceLength);
38-
39-
if (typeof hexOrBytes === "string") {
40-
if (data.length === 1 && dir === "right") {
41-
data = `${data}0`;
42-
}
43-
return `0x${
44-
data.length % 2 === 1 ? `0${data}` : data
45-
}` as TrimReturnType<TValue>;
46-
}
47-
return data as TrimReturnType<TValue>;
48-
}
49-
506
type PadOptions = {
517
dir?: "left" | "right";
528
size?: number | null;
@@ -71,25 +27,17 @@ export function padHex(hex_: Hex, options: PadOptions = {}) {
7127
if (size === null) {
7228
return hex_;
7329
}
74-
const hex = hex_.replace("0x", "");
75-
if (hex.length > size * 2) {
76-
throw new Error(`Size overflow: ${Math.ceil(hex.length / 2)} > ${size}`);
30+
if (dir === "left") {
31+
return ox__Hex.padLeft(hex_, size);
7732
}
78-
79-
return `0x${hex[dir === "right" ? "padEnd" : "padStart"](
80-
size * 2,
81-
"0",
82-
)}` as Hex;
33+
return ox__Hex.padRight(hex_, size);
8334
}
8435

8536
//--------------------------------------------------------------------------
8637
// FROM HEX
8738
//--------------------------------------------------------------------------
8839

89-
export type HexToStringOpts = {
90-
/** Size (in bytes) of the hex value. */
91-
size?: number;
92-
};
40+
export type HexToStringOpts = ox__Hex.toString.Options;
9341

9442
/**
9543
* Converts a hexadecimal string to a UTF-8 string.
@@ -105,20 +53,10 @@ export type HexToStringOpts = {
10553
* @utils
10654
*/
10755
export function hexToString(hex: Hex, opts: HexToStringOpts = {}): string {
108-
let bytes = hexToUint8Array(hex);
109-
if (opts.size) {
110-
assertSize(bytes, { size: opts.size });
111-
bytes = trim(bytes, { dir: "right" });
112-
}
113-
return cachedTextDecoder().decode(bytes);
56+
return ox__Hex.toString(hex, opts);
11457
}
11558

116-
export type HexToBigIntOpts = {
117-
/** Whether or not the number of a signed representation. */
118-
signed?: boolean;
119-
/** Size (in bytes) of the hex value. */
120-
size?: number;
121-
};
59+
export type HexToBigIntOpts = ox__Hex.toBigInt.Options;
12260

12361
/**
12462
* Converts a hexadecimal string to a BigInt.
@@ -134,24 +72,7 @@ export type HexToBigIntOpts = {
13472
* @utils
13573
*/
13674
export function hexToBigInt(hex: Hex, opts: HexToBigIntOpts = {}): bigint {
137-
const { signed } = opts;
138-
139-
if (opts.size) {
140-
assertSize(hex, { size: opts.size });
141-
}
142-
143-
const value = BigInt(hex);
144-
if (!signed) {
145-
return value;
146-
}
147-
148-
const size = (hex.length - 2) / 2;
149-
const max = (1n << (BigInt(size) * 8n - 1n)) - 1n;
150-
if (value <= max) {
151-
return value;
152-
}
153-
154-
return value - BigInt(`0x${"f".padStart(size * 2, "f")}`) - 1n;
75+
return ox__Hex.toBigInt(hex, opts);
15576
}
15677

15778
export type HexToNumberOpts = HexToBigIntOpts;
@@ -169,13 +90,10 @@ export type HexToNumberOpts = HexToBigIntOpts;
16990
* @utils
17091
*/
17192
export function hexToNumber(hex: Hex, opts: HexToNumberOpts = {}): number {
172-
return Number(hexToBigInt(hex, opts));
93+
return ox__Hex.toNumber(hex, opts);
17394
}
17495

175-
export type HexToBoolOpts = {
176-
/** Size (in bytes) of the hex value. */
177-
size?: number;
178-
};
96+
export type HexToBoolOpts = ox__Hex.toBoolean.Options;
17997

18098
/**
18199
* Converts a hexadecimal string to a boolean value.
@@ -192,24 +110,10 @@ export type HexToBoolOpts = {
192110
* @utils
193111
*/
194112
export function hexToBool(hex: Hex, opts: HexToBoolOpts = {}): boolean {
195-
if (opts.size) {
196-
assertSize(hex, { size: opts.size });
197-
// biome-ignore lint/style/noParameterAssign: for perf
198-
hex = trim(hex);
199-
}
200-
if (trim(hex) === "0x00") {
201-
return false;
202-
}
203-
if (trim(hex) === "0x01") {
204-
return true;
205-
}
206-
throw new Error(`Invalid hex boolean: ${hex}`);
113+
return ox__Hex.toBoolean(hex, opts);
207114
}
208115

209-
export type HexToUint8ArrayOpts = {
210-
/** Size of the output bytes. */
211-
size?: number;
212-
};
116+
export type HexToUint8ArrayOpts = ox__Hex.toBytes.Options;
213117

214118
/**
215119
* Converts a hexadecimal string to a Uint8Array.
@@ -228,28 +132,7 @@ export function hexToUint8Array(
228132
hex: Hex,
229133
opts: HexToUint8ArrayOpts = {},
230134
): Uint8Array {
231-
if (opts.size) {
232-
assertSize(hex, { size: opts.size });
233-
// biome-ignore lint/style/noParameterAssign: for perf
234-
hex = padHex(hex, { dir: "right", size: opts.size });
235-
}
236-
237-
let hexString = hex.slice(2) as string;
238-
if (hexString.length % 2) {
239-
hexString = `0${hexString}`;
240-
}
241-
242-
const length = hexString.length / 2;
243-
const bytes = new Uint8Array(length);
244-
for (let index = 0, j = 0; index < length; index++) {
245-
const nibbleLeft = charCodeToBase16(hexString.charCodeAt(j++));
246-
const nibbleRight = charCodeToBase16(hexString.charCodeAt(j++));
247-
if (nibbleLeft === undefined || nibbleRight === undefined) {
248-
throw new Error(`Invalid hex character: ${hexString}`);
249-
}
250-
bytes[index] = nibbleLeft * 16 + nibbleRight;
251-
}
252-
return bytes;
135+
return ox__Hex.toBytes(hex, opts);
253136
}
254137

255138
export type FromHexParameters<
@@ -311,13 +194,7 @@ export function fromHex<
311194
// TO HEX
312195
//--------------------------------------------------------------------------
313196

314-
const hexes = /* @__PURE__ */ (() =>
315-
Array.from({ length: 256 }, (_v, i) => i.toString(16).padStart(2, "0")))();
316-
317-
export type BoolToHexOpts = {
318-
/** The size (in bytes) of the output hex value. */
319-
size?: number;
320-
};
197+
export type BoolToHexOpts = ox__Hex.fromBoolean.Options;
321198

322199
/**
323200
* Converts a boolean value to a hexadecimal string representation.
@@ -333,18 +210,10 @@ export type BoolToHexOpts = {
333210
* @utils
334211
*/
335212
export function boolToHex(value: boolean, opts: BoolToHexOpts = {}): Hex {
336-
const hex = `0x${Number(value)}` as const;
337-
if (typeof opts.size === "number") {
338-
assertSize(hex, { size: opts.size });
339-
return padHex(hex, { size: opts.size });
340-
}
341-
return hex;
213+
return ox__Hex.fromBoolean(value, opts);
342214
}
343215

344-
export type Uint8ArrayToHexOpts = {
345-
/** The size (in bytes) of the output hex value. */
346-
size?: number;
347-
};
216+
export type Uint8ArrayToHexOpts = ox__Hex.fromBoolean.Options;
348217

349218
/**
350219
* Converts an array of bytes to a hexadecimal string.
@@ -363,32 +232,10 @@ export function uint8ArrayToHex(
363232
value: Uint8Array,
364233
opts: Uint8ArrayToHexOpts = {},
365234
): Hex {
366-
let string = "";
367-
for (let i = 0; i < value.length; i++) {
368-
// biome-ignore lint/style/noNonNullAssertion: we know this is defined
369-
string += hexes[value[i]!];
370-
}
371-
const hex = `0x${string}` as const;
372-
373-
if (typeof opts.size === "number") {
374-
assertSize(hex, { size: opts.size });
375-
return padHex(hex, { dir: "right", size: opts.size });
376-
}
377-
return hex;
235+
return ox__Hex.fromBytes(value, opts);
378236
}
379237

380-
export type NumberToHexOpts =
381-
| {
382-
/** Whether or not the number of a signed representation. */
383-
signed?: boolean;
384-
/** The size (in bytes) of the output hex value. */
385-
size: number;
386-
}
387-
| {
388-
signed?: never;
389-
/** The size (in bytes) of the output hex value. */
390-
size?: number;
391-
};
238+
export type NumberToHexOpts = ox__Hex.fromNumber.Options;
392239

393240
/**
394241
* Converts a number or bigint to a hexadecimal string.
@@ -408,47 +255,10 @@ export function numberToHex(
408255
value_: number | bigint,
409256
opts: NumberToHexOpts = {},
410257
): Hex {
411-
const { signed, size } = opts;
412-
413-
const value = BigInt(value_);
414-
415-
let maxValue: bigint | number | undefined;
416-
if (size) {
417-
if (signed) {
418-
maxValue = (1n << (BigInt(size) * 8n - 1n)) - 1n;
419-
} else {
420-
maxValue = 2n ** (BigInt(size) * 8n) - 1n;
421-
}
422-
} else if (typeof value_ === "number") {
423-
maxValue = BigInt(Number.MAX_SAFE_INTEGER);
424-
}
425-
426-
const minValue = typeof maxValue === "bigint" && signed ? -maxValue - 1n : 0;
427-
428-
if ((maxValue && value > maxValue) || value < minValue) {
429-
const suffix = typeof value_ === "bigint" ? "n" : "";
430-
throw new Error(
431-
`Number "${value_}${suffix}" is not in safe ${
432-
size ? `${size * 8}-bit ${signed ? "signed" : "unsigned"} ` : ""
433-
}integer range ${
434-
maxValue ? `(${minValue} to ${maxValue})` : `(above ${minValue})`
435-
}`,
436-
);
437-
}
438-
439-
const hex = `0x${(
440-
signed && value < 0 ? (1n << BigInt(size * 8)) + BigInt(value) : value
441-
).toString(16)}` as Hex;
442-
if (size) {
443-
return padHex(hex, { size }) as Hex;
444-
}
445-
return hex;
258+
return ox__Hex.fromNumber(value_, opts);
446259
}
447260

448-
export type StringToHexOpts = {
449-
/** The size (in bytes) of the output hex value. */
450-
size?: number;
451-
};
261+
export type StringToHexOpts = ox__Hex.fromString.Options;
452262

453263
/**
454264
* Converts a string to its hexadecimal representation.
@@ -464,11 +274,10 @@ export type StringToHexOpts = {
464274
* @utils
465275
*/
466276
export function stringToHex(value_: string, opts: StringToHexOpts = {}): Hex {
467-
const value = cachedTextEncoder().encode(value_);
468-
return uint8ArrayToHex(value, opts);
277+
return ox__Hex.fromString(value_, opts);
469278
}
470279

471-
export type ToHexParameters = {
280+
type ToHexParameters = {
472281
/** The size (in bytes) of the output hex value. */
473282
size?: number;
474283
};

0 commit comments

Comments
 (0)