From 33819cf2b507b838b113df61cd0973abc0530f95 Mon Sep 17 00:00:00 2001 From: ripe0x Date: Wed, 6 May 2026 09:23:34 -0400 Subject: [PATCH] Remove @ethersproject/bytes and tinycolor2 runtime dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both deps were single-call-site shims: - @ethersproject/bytes: only used for `arrayify()` (hex string -> Uint8Array). No keccak / signing / blockchain primitives — just hex parsing. - tinycolor2: only used for `tinycolor({h,s,l}).toHslString()` to format HSL into a CSS string. Replaced both with inline implementations in src/lib.ts. The hslString helper reproduces tinycolor's HSL -> RGB -> HSL roundtrip and integer-rounding so output stays byte-for-byte identical. Output verification: ran upstream main's built dist/main.js and the patched dist/main.js side-by-side through a 2,315-address corpus (edge cases, famous addresses, 2,000 deterministic Mulberry32-seeded random addresses, and 100 addresses' worth of case variants). All gradient arrays and zorbImageDataURI outputs matched byte-for-byte. Bundle size impact (`dist/main.js`): 57,027 -> 8,839 bytes (-84.5%). `dist/component.es.js`: 70,515 -> 23,602 bytes (-66.5%). The public API (`` custom element, `gradientForAddress`, `zorbImageSVG`, `zorbImageDataURI`) and the SVG template are unchanged. Svelte build remains in place — only the two runtime deps and one rollup-config tinycolor reference are removed. Note: yarn.lock will need to be regenerated by running `yarn install` locally after merge to drop the now-unused entries. --- packages/zorb-web-component/package.json | 6 +- packages/zorb-web-component/rollup.config.js | 8 +- packages/zorb-web-component/src/lib.ts | 79 +++++++++++++++++--- 3 files changed, 72 insertions(+), 21 deletions(-) diff --git a/packages/zorb-web-component/package.json b/packages/zorb-web-component/package.json index c73d08e..f320972 100644 --- a/packages/zorb-web-component/package.json +++ b/packages/zorb-web-component/package.json @@ -25,9 +25,5 @@ "rollup-plugin-svelte": "^7.1.0", "rollup-plugin-typescript": "^1.0.1" }, - "dependencies": { - "@ethersproject/bytes": "^5.5.0", - "@types/tinycolor2": "^1.4.3", - "tinycolor2": "^1.4.2" - } + "dependencies": {} } diff --git a/packages/zorb-web-component/rollup.config.js b/packages/zorb-web-component/rollup.config.js index 0999b9a..89afe36 100644 --- a/packages/zorb-web-component/rollup.config.js +++ b/packages/zorb-web-component/rollup.config.js @@ -5,15 +5,9 @@ import resolve from "rollup-plugin-node-resolve"; import commonjs from "rollup-plugin-commonjs"; import dts from "rollup-plugin-dts"; -import tinycolor from "tinycolor2"; - const commonPlugins = [ resolve({ node: true }), - commonjs({ - namedExports: { - tinycolor2: Object.keys(tinycolor), - }, - }), + commonjs(), typescript({ tsconfig: false, esModuleInterop: true, diff --git a/packages/zorb-web-component/src/lib.ts b/packages/zorb-web-component/src/lib.ts index 1646b66..63e9ee3 100644 --- a/packages/zorb-web-component/src/lib.ts +++ b/packages/zorb-web-component/src/lib.ts @@ -1,5 +1,71 @@ -import { arrayify } from "@ethersproject/bytes"; -import tinycolor, { ColorInput } from "tinycolor2"; +// Inline replacement for `arrayify` from @ethersproject/bytes. +// We only ever consume 0x-prefixed even-length hex strings here; the +// ethers helper supported a wider input space we don't need. +const arrayify = (hex: string): Uint8Array => { + if (typeof hex !== "string" || !/^0x[0-9a-fA-F]*$/.test(hex) || hex.length % 2 !== 0) { + throw new Error(`invalid hex string: ${hex}`); + } + const out = new Uint8Array((hex.length - 2) / 2); + for (let i = 0; i < out.length; i++) { + out[i] = parseInt(hex.slice(2 + i * 2, 4 + i * 2), 16); + } + return out; +}; + +// Inline replacement for the single tinycolor2 call this file made: +// tinycolor({h, s, l}).toHslString() +// tinycolor doesn't just format the input — it pushes HSL → RGB → HSL, +// clamps RGB to [0,255] (without rounding), then rounds h*360, s*100, l*100 +// to integers. We reproduce that exact pipeline so output stays +// byte-for-byte identical. +const _bound01 = (n: number, max: number): number => { + const c = Math.min(max, Math.max(0, n)); + if (Math.abs(c - max) < 0.000001) return 1; + return (c % max) / max; +}; +const _hueChannel = (p: number, q: number, t: number): number => { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1 / 6) return p + (q - p) * 6 * t; + if (t < 1 / 2) return q; + if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; + return p; +}; +const hslString = (h: number, s: number, l: number): string => { + const hN = _bound01(h, 360); + const sN = _bound01(s, 100); + const lN = _bound01(l, 100); + let r: number, g: number, b: number; + if (sN === 0) { + r = g = b = lN * 255; + } else { + const q = lN < 0.5 ? lN * (1 + sN) : lN + sN - lN * sN; + const p = 2 * lN - q; + r = _hueChannel(p, q, hN + 1 / 3) * 255; + g = _hueChannel(p, q, hN) * 255; + b = _hueChannel(p, q, hN - 1 / 3) * 255; + } + // tinycolor clamps RGB to [0,255] without rounding before round-tripping. + const rC = Math.min(255, Math.max(0, r)); + const gC = Math.min(255, Math.max(0, g)); + const bC = Math.min(255, Math.max(0, b)); + const rN = rC / 255, gN = gC / 255, bN = bC / 255; + const max = Math.max(rN, gN, bN); + const min = Math.min(rN, gN, bN); + const lOut = (max + min) / 2; + let hOut: number, sOut: number; + if (max === min) { + hOut = 0; sOut = 0; + } else { + const d = max - min; + sOut = lOut > 0.5 ? d / (2 - max - min) : d / (max + min); + if (max === rN) hOut = (gN - bN) / d + (gN < bN ? 6 : 0); + else if (max === gN) hOut = (bN - rN) / d + 2; + else hOut = (rN - gN) / d + 4; + hOut /= 6; + } + return `hsl(${Math.round(hOut * 360)}, ${Math.round(sOut * 100)}%, ${Math.round(lOut * 100)}%)`; +}; const linear = (p: number) => p; @@ -120,7 +186,7 @@ export const gradientForAddress = (address: string) => { const lightnessShiftFn = lerpLightnessFn(bytes[5] % 2); const saturationShiftFn = lerpSaturationFn(bytes[3] % 2); - const inputs: ColorInput[] = [ + const inputs = [ { h: hueShiftFn(startHue, 0), s: saturationShiftFn(startSaturation, endSaturation, 1), @@ -148,10 +214,5 @@ export const gradientForAddress = (address: string) => { } ]; - //return inputs; - - return inputs - .map((c: ColorInput) => tinycolor(c)) - .map((tc: tinycolor.Instance) => tc.toHslString()) - + return inputs.map(c => hslString(c.h, c.s, c.l)); };