Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@ethernauta/utils

Package Overview
Dependencies
Maintainers
1
Versions
14
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ethernauta/utils - npm Package Compare versions

Comparing version
0.0.43
to
0.0.44
+5
dist/bigint-to-hex.d.ts
//#region src/bigint-to-hex.d.ts
declare function bigint_to_hex(value: bigint): `0x${string}`;
//#endregion
export { bigint_to_hex };
//# sourceMappingURL=bigint-to-hex.d.ts.map
{"version":3,"file":"bigint-to-hex.d.ts","names":[],"sources":["../src/bigint-to-hex.ts"],"mappings":";iBAAgB,aAAA"}
//#region src/bigint-to-hex.ts
function bigint_to_hex(value) {
return value === 0n ? "0x0" : `0x${value.toString(16)}`;
}
//#endregion
export { bigint_to_hex };
//# sourceMappingURL=bigint-to-hex.js.map
{"version":3,"file":"bigint-to-hex.js","names":[],"sources":["../src/bigint-to-hex.ts"],"sourcesContent":["export function bigint_to_hex(\n value: bigint,\n): `0x${string}` {\n return value === 0n ? \"0x0\" : `0x${value.toString(16)}`\n}\n"],"mappings":";AAAA,SAAgB,cACd,OACe;CACf,OAAO,UAAU,KAAK,QAAQ,KAAK,MAAM,SAAS,EAAE;AACtD"}
//#region src/bytes-to-uint.d.ts
declare function bytes_to_uint(bytes: Uint8Array): `0x${string}`;
//#endregion
export { bytes_to_uint };
//# sourceMappingURL=bytes-to-uint.d.ts.map
{"version":3,"file":"bytes-to-uint.d.ts","names":[],"sources":["../src/bytes-to-uint.ts"],"mappings":";iBAEgB,aAAA,QACP"}
import { bytes_to_hex } from "./bytes-to-hex.js";
//#region src/bytes-to-uint.ts
function bytes_to_uint(bytes) {
if (bytes.length === 0) return "0x0";
return `0x${BigInt(bytes_to_hex(bytes)).toString(16)}`;
}
//#endregion
export { bytes_to_uint };
//# sourceMappingURL=bytes-to-uint.js.map
{"version":3,"file":"bytes-to-uint.js","names":[],"sources":["../src/bytes-to-uint.ts"],"sourcesContent":["import { bytes_to_hex } from \"./bytes-to-hex\"\n\nexport function bytes_to_uint(\n bytes: Uint8Array,\n): `0x${string}` {\n if (bytes.length === 0) return \"0x0\"\n const n = BigInt(bytes_to_hex(bytes))\n return `0x${n.toString(16)}`\n}\n"],"mappings":";;;AAEA,SAAgB,cACd,OACe;CACf,IAAI,MAAM,WAAW,GAAG,OAAO;CAE/B,OAAO,KADG,OAAO,aAAa,KAAK,CACvB,EAAE,SAAS,EAAE;AAC3B"}
//#region src/hex-to-bigint.d.ts
declare function hex_to_bigint(hex: `0x${string}`): bigint;
//#endregion
export { hex_to_bigint };
//# sourceMappingURL=hex-to-bigint.d.ts.map
{"version":3,"file":"hex-to-bigint.d.ts","names":[],"sources":["../src/hex-to-bigint.ts"],"mappings":";iBAAgB,aAAA"}
//#region src/hex-to-bigint.ts
function hex_to_bigint(hex) {
return BigInt(hex);
}
//#endregion
export { hex_to_bigint };
//# sourceMappingURL=hex-to-bigint.js.map
{"version":3,"file":"hex-to-bigint.js","names":[],"sources":["../src/hex-to-bigint.ts"],"sourcesContent":["export function hex_to_bigint(hex: `0x${string}`): bigint {\n return BigInt(hex)\n}\n"],"mappings":";AAAA,SAAgB,cAAc,KAA4B;CACxD,OAAO,OAAO,GAAG;AACnB"}
//#region src/rlp.d.ts
type RlpInput = string | number | bigint | Uint8Array | RlpInput[];
declare function rlp_encode(input: RlpInput): Uint8Array;
type RlpDecoded = Uint8Array | RlpDecoded[];
declare function rlp_decode(bytes: Uint8Array): RlpDecoded;
//#endregion
export { RlpDecoded, RlpInput, rlp_decode, rlp_encode };
//# sourceMappingURL=rlp.d.ts.map
{"version":3,"file":"rlp.d.ts","names":[],"sources":["../src/rlp.ts"],"mappings":";KAMY,QAAA,8BAIR,aACA;AALQ,iBAOI,UAAA,CAPI,KAAA,EAOc,QAPd,CAAA,EAOyB,UAPzB;AAAA,KAuGR,UAAA,GAAa,UAvGL,GAuGkB,UAvGlB,EAAA;AAIhB,iBAqGY,UAAA,CArGZ,KAAA,EAqG8B,UArG9B,CAAA,EAqG2C,UArG3C"}
import { hex_to_bytes } from "./hex-to-bytes.js";
//#region src/rlp.ts
function rlp_encode(input) {
if (Array.isArray(input)) return encode_list(input);
return encode_item(input);
}
function encode_item(item) {
const bytes = to_bytes(item);
const first = bytes[0];
if (bytes.length === 1 && first !== void 0 && first < 128) return bytes;
if (bytes.length <= 55) {
const out = new Uint8Array(1 + bytes.length);
out[0] = 128 + bytes.length;
out.set(bytes, 1);
return out;
}
const length_bytes = encode_length(bytes.length);
const out = new Uint8Array(1 + length_bytes.length + bytes.length);
out[0] = 183 + length_bytes.length;
out.set(length_bytes, 1);
out.set(bytes, 1 + length_bytes.length);
return out;
}
function encode_list(list) {
const items = list.map(rlp_encode);
const total = items.reduce((s, i) => s + i.length, 0);
if (total <= 55) {
const out = new Uint8Array(1 + total);
out[0] = 192 + total;
let offset = 1;
for (const item of items) {
out.set(item, offset);
offset += item.length;
}
return out;
}
const length_bytes = encode_length(total);
const out = new Uint8Array(1 + length_bytes.length + total);
out[0] = 247 + length_bytes.length;
out.set(length_bytes, 1);
let offset = 1 + length_bytes.length;
for (const item of items) {
out.set(item, offset);
offset += item.length;
}
return out;
}
function to_bytes(input) {
if (input instanceof Uint8Array) return input;
if (typeof input === "string") {
if (input.startsWith("0x")) return hex_to_bytes(input);
return new TextEncoder().encode(input);
}
return number_to_bytes(BigInt(input));
}
function number_to_bytes(big) {
if (big === 0n) return new Uint8Array([]);
const hex = big.toString(16);
return hex_to_bytes(hex.padStart(hex.length + hex.length % 2, "0"));
}
function encode_length(length) {
if (length === 0) return new Uint8Array([]);
const out = [];
let temp = length;
while (temp > 0) {
out.unshift(temp & 255);
temp >>= 8;
}
return new Uint8Array(out);
}
function rlp_decode(bytes) {
const [value, consumed] = decode_item(bytes, 0);
if (consumed !== bytes.length) throw new Error(`rlp_decode: ${bytes.length - consumed} trailing byte(s)`);
return value;
}
function decode_item(bytes, offset) {
if (offset >= bytes.length) throw new Error("rlp_decode: unexpected end of input");
const prefix = bytes[offset];
if (prefix === void 0) throw new Error("rlp_decode: unexpected end of input");
if (prefix < 128) return [bytes.slice(offset, offset + 1), 1];
if (prefix <= 183) {
const length = prefix - 128;
const start = offset + 1;
const end = start + length;
assert_in_bounds(bytes, end);
return [bytes.slice(start, end), 1 + length];
}
if (prefix <= 191) {
const length_of_length = prefix - 183;
const length_start = offset + 1;
const length_end = length_start + length_of_length;
assert_in_bounds(bytes, length_end);
const length = read_length(bytes.subarray(length_start, length_end));
const start = length_end;
const end = start + length;
assert_in_bounds(bytes, end);
return [bytes.slice(start, end), 1 + length_of_length + length];
}
if (prefix <= 247) {
const length = prefix - 192;
const start = offset + 1;
const end = start + length;
assert_in_bounds(bytes, end);
return [decode_list_payload(bytes, start, end), 1 + length];
}
const length_of_length = prefix - 247;
const length_start = offset + 1;
const length_end = length_start + length_of_length;
assert_in_bounds(bytes, length_end);
const length = read_length(bytes.subarray(length_start, length_end));
const start = length_end;
const end = start + length;
assert_in_bounds(bytes, end);
return [decode_list_payload(bytes, start, end), 1 + length_of_length + length];
}
function decode_list_payload(bytes, start, end) {
const items = [];
let cursor = start;
while (cursor < end) {
const [value, consumed] = decode_item(bytes, cursor);
items.push(value);
cursor += consumed;
}
if (cursor !== end) throw new Error("rlp_decode: list payload length mismatch");
return items;
}
function read_length(bytes) {
let length = 0;
for (const byte of bytes) length = length * 256 + byte;
return length;
}
function assert_in_bounds(bytes, end) {
if (end > bytes.length) throw new Error("rlp_decode: payload overruns input");
}
//#endregion
export { rlp_decode, rlp_encode };
//# sourceMappingURL=rlp.js.map
{"version":3,"file":"rlp.js","names":[],"sources":["../src/rlp.ts"],"sourcesContent":["// https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/\n// Recursive Length Prefix encoding. Used by every Ethereum\n// transaction type for the canonical serialized form.\n\nimport { hex_to_bytes } from \"./hex-to-bytes\"\n\nexport type RlpInput =\n | string\n | number\n | bigint\n | Uint8Array\n | RlpInput[]\n\nexport function rlp_encode(input: RlpInput): Uint8Array {\n if (Array.isArray(input)) return encode_list(input)\n return encode_item(input)\n}\n\nfunction encode_item(\n item: string | number | bigint | Uint8Array,\n): Uint8Array {\n const bytes = to_bytes(item)\n const first = bytes[0]\n if (\n bytes.length === 1 &&\n first !== undefined &&\n first < 0x80\n ) {\n return bytes\n }\n if (bytes.length <= 55) {\n const out = new Uint8Array(1 + bytes.length)\n out[0] = 0x80 + bytes.length\n out.set(bytes, 1)\n return out\n }\n const length_bytes = encode_length(bytes.length)\n const out = new Uint8Array(\n 1 + length_bytes.length + bytes.length,\n )\n out[0] = 0xb7 + length_bytes.length\n out.set(length_bytes, 1)\n out.set(bytes, 1 + length_bytes.length)\n return out\n}\n\nfunction encode_list(list: RlpInput[]): Uint8Array {\n const items = list.map(rlp_encode)\n const total = items.reduce((s, i) => s + i.length, 0)\n if (total <= 55) {\n const out = new Uint8Array(1 + total)\n out[0] = 0xc0 + total\n let offset = 1\n for (const item of items) {\n out.set(item, offset)\n offset += item.length\n }\n return out\n }\n const length_bytes = encode_length(total)\n const out = new Uint8Array(\n 1 + length_bytes.length + total,\n )\n out[0] = 0xf7 + length_bytes.length\n out.set(length_bytes, 1)\n let offset = 1 + length_bytes.length\n for (const item of items) {\n out.set(item, offset)\n offset += item.length\n }\n return out\n}\n\nfunction to_bytes(\n input: string | number | bigint | Uint8Array,\n): Uint8Array {\n if (input instanceof Uint8Array) return input\n if (typeof input === \"string\") {\n if (input.startsWith(\"0x\")) return hex_to_bytes(input)\n return new TextEncoder().encode(input)\n }\n return number_to_bytes(BigInt(input))\n}\n\nfunction number_to_bytes(big: bigint): Uint8Array {\n if (big === 0n) return new Uint8Array([])\n const hex = big.toString(16)\n const padded = hex.padStart(\n hex.length + (hex.length % 2),\n \"0\",\n )\n return hex_to_bytes(padded)\n}\n\nfunction encode_length(length: number): Uint8Array {\n if (length === 0) return new Uint8Array([])\n const out: number[] = []\n let temp = length\n while (temp > 0) {\n out.unshift(temp & 0xff)\n temp >>= 8\n }\n return new Uint8Array(out)\n}\n\n// RLP only preserves bytes and nesting — numbers and strings\n// fed into `rlp_encode` come back as byte payloads. Callers\n// reapply meaning (number, address, hash, …) from the\n// surrounding schema.\nexport type RlpDecoded = Uint8Array | RlpDecoded[]\n\nexport function rlp_decode(bytes: Uint8Array): RlpDecoded {\n const [value, consumed] = decode_item(bytes, 0)\n if (consumed !== bytes.length) {\n throw new Error(\n `rlp_decode: ${bytes.length - consumed} trailing byte(s)`,\n )\n }\n return value\n}\n\nfunction decode_item(\n bytes: Uint8Array,\n offset: number,\n): [RlpDecoded, number] {\n if (offset >= bytes.length) {\n throw new Error(\"rlp_decode: unexpected end of input\")\n }\n const prefix = bytes[offset]\n if (prefix === undefined) {\n throw new Error(\"rlp_decode: unexpected end of input\")\n }\n if (prefix < 0x80) {\n return [bytes.slice(offset, offset + 1), 1]\n }\n if (prefix <= 0xb7) {\n const length = prefix - 0x80\n const start = offset + 1\n const end = start + length\n assert_in_bounds(bytes, end)\n return [bytes.slice(start, end), 1 + length]\n }\n if (prefix <= 0xbf) {\n const length_of_length = prefix - 0xb7\n const length_start = offset + 1\n const length_end = length_start + length_of_length\n assert_in_bounds(bytes, length_end)\n const length = read_length(\n bytes.subarray(length_start, length_end),\n )\n const start = length_end\n const end = start + length\n assert_in_bounds(bytes, end)\n return [\n bytes.slice(start, end),\n 1 + length_of_length + length,\n ]\n }\n if (prefix <= 0xf7) {\n const length = prefix - 0xc0\n const start = offset + 1\n const end = start + length\n assert_in_bounds(bytes, end)\n return [\n decode_list_payload(bytes, start, end),\n 1 + length,\n ]\n }\n const length_of_length = prefix - 0xf7\n const length_start = offset + 1\n const length_end = length_start + length_of_length\n assert_in_bounds(bytes, length_end)\n const length = read_length(\n bytes.subarray(length_start, length_end),\n )\n const start = length_end\n const end = start + length\n assert_in_bounds(bytes, end)\n return [\n decode_list_payload(bytes, start, end),\n 1 + length_of_length + length,\n ]\n}\n\nfunction decode_list_payload(\n bytes: Uint8Array,\n start: number,\n end: number,\n): RlpDecoded[] {\n const items: RlpDecoded[] = []\n let cursor = start\n while (cursor < end) {\n const [value, consumed] = decode_item(bytes, cursor)\n items.push(value)\n cursor += consumed\n }\n if (cursor !== end) {\n throw new Error(\n \"rlp_decode: list payload length mismatch\",\n )\n }\n return items\n}\n\nfunction read_length(bytes: Uint8Array): number {\n let length = 0\n for (const byte of bytes) {\n length = length * 0x100 + byte\n }\n return length\n}\n\nfunction assert_in_bounds(\n bytes: Uint8Array,\n end: number,\n): void {\n if (end > bytes.length) {\n throw new Error(\"rlp_decode: payload overruns input\")\n }\n}\n"],"mappings":"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"}
//#region src/time.d.ts
declare function seconds_to_big(seconds: number): bigint;
declare function now_to_big(): bigint;
declare function deadline_in(seconds: number): bigint;
//#endregion
export { deadline_in, now_to_big, seconds_to_big };
//# sourceMappingURL=time.d.ts.map
{"version":3,"file":"time.d.ts","names":[],"sources":["../src/time.ts"],"mappings":";iBAAgB,cAAA;AAAA,iBAIA,UAAA,CAAA,CAJc,EAAA,MAAA;AAId,iBAIA,WAAA,CAJU,OAAA,EAAA,MAAA,CAAA,EAAA,MAAA"}
//#region src/time.ts
function seconds_to_big(seconds) {
return BigInt(Math.floor(seconds));
}
function now_to_big() {
return seconds_to_big(Date.now() / 1e3);
}
function deadline_in(seconds) {
return seconds_to_big(Date.now() / 1e3 + seconds);
}
//#endregion
export { deadline_in, now_to_big, seconds_to_big };
//# sourceMappingURL=time.js.map
{"version":3,"file":"time.js","names":[],"sources":["../src/time.ts"],"sourcesContent":["export function seconds_to_big(seconds: number): bigint {\n return BigInt(Math.floor(seconds))\n}\n\nexport function now_to_big(): bigint {\n return seconds_to_big(Date.now() / 1000)\n}\n\nexport function deadline_in(seconds: number): bigint {\n return seconds_to_big(Date.now() / 1000 + seconds)\n}\n"],"mappings":";AAAA,SAAgB,eAAe,SAAyB;CACtD,OAAO,OAAO,KAAK,MAAM,OAAO,CAAC;AACnC;AAEA,SAAgB,aAAqB;CACnC,OAAO,eAAe,KAAK,IAAI,IAAI,GAAI;AACzC;AAEA,SAAgB,YAAY,SAAyB;CACnD,OAAO,eAAe,KAAK,IAAI,IAAI,MAAO,OAAO;AACnD"}
//#region src/unit.d.ts
declare function format_unit(value: bigint, decimals?: number): string;
declare function format_ether(value: bigint): string;
declare function format_gwei(value: bigint): string;
declare function parse_ether(value: string): bigint;
declare function parse_gwei(value: string): bigint;
declare function parse_unit(value: string, decimals?: number): bigint;
//#endregion
export { format_ether, format_gwei, format_unit, parse_ether, parse_gwei, parse_unit };
//# sourceMappingURL=unit.d.ts.map
{"version":3,"file":"unit.d.ts","names":[],"sources":["../src/unit.ts"],"mappings":";iBAAgB,WAAA;AAAA,iBAuBA,YAAA,CAvBW,KAAA,EAAA,MAAA,CAAA,EAAA,MAAA;AAuBX,iBAIA,WAAA,CAJY,KAAA,EAAA,MAAA,CAAA,EAAA,MAAA;AAIZ,iBAIA,WAAA,CAJW,KAAA,EAAA,MAAA,CAAA,EAAA,MAAA;AAIX,iBAIA,UAAA,CAJW,KAAA,EAAA,MAAA,CAAA,EAAA,MAAA;AAIX,iBAIA,UAAA,CAJU,KAAA,EAAA,MAAA,EAAA,QAAA,CAAA,EAAA,MAAA,CAAA,EAAA,MAAA"}
//#region src/unit.ts
function format_unit(value, decimals = 18) {
if (value === 0n) return "0";
const negative = value < 0n;
const abs = negative ? -value : value;
const base = 10n ** BigInt(decimals);
const integer_part = abs / base;
const fraction_part = abs % base;
const sign = negative ? "-" : "";
if (fraction_part === 0n) return `${sign}${integer_part.toString()}`;
const fraction = fraction_part.toString().padStart(decimals, "0").replace(/0+$/, "");
return `${sign}${integer_part.toString()}.${fraction}`;
}
function format_ether(value) {
return format_unit(value, 18);
}
function format_gwei(value) {
return format_unit(value, 9);
}
function parse_ether(value) {
return parse_unit(value, 18);
}
function parse_gwei(value) {
return parse_unit(value, 9);
}
function parse_unit(value, decimals = 18) {
if (value === "") throw new Error("parse_unit: empty string");
const negative = value.startsWith("-");
const unsigned = negative ? value.slice(1) : value;
if (!/^(\d+\.?\d*|\.\d+)$/.test(unsigned)) throw new Error(`parse_unit: invalid number "${value}"`);
const [integer_str, fraction_str = ""] = unsigned.split(".");
if (fraction_str.length > decimals) throw new Error(`parse_unit: "${value}" has more than ${decimals} fractional digits`);
const integer = !integer_str || integer_str === "" ? "0" : integer_str;
const padded = fraction_str.padEnd(decimals, "0");
const base = 10n ** BigInt(decimals);
const fraction_bigint = padded === "" ? 0n : BigInt(padded);
const result = BigInt(integer) * base + fraction_bigint;
return negative ? -result : result;
}
//#endregion
export { format_ether, format_gwei, format_unit, parse_ether, parse_gwei, parse_unit };
//# sourceMappingURL=unit.js.map
{"version":3,"file":"unit.js","names":[],"sources":["../src/unit.ts"],"sourcesContent":["export function format_unit(\n value: bigint,\n decimals = 18,\n): string {\n if (value === 0n) {\n return \"0\"\n }\n const negative = value < 0n\n const abs = negative ? -value : value\n const base = 10n ** BigInt(decimals)\n const integer_part = abs / base\n const fraction_part = abs % base\n const sign = negative ? \"-\" : \"\"\n if (fraction_part === 0n) {\n return `${sign}${integer_part.toString()}`\n }\n const fraction = fraction_part\n .toString()\n .padStart(decimals, \"0\")\n .replace(/0+$/, \"\")\n return `${sign}${integer_part.toString()}.${fraction}`\n}\n\nexport function format_ether(value: bigint): string {\n return format_unit(value, 18)\n}\n\nexport function format_gwei(value: bigint): string {\n return format_unit(value, 9)\n}\n\nexport function parse_ether(value: string): bigint {\n return parse_unit(value, 18)\n}\n\nexport function parse_gwei(value: string): bigint {\n return parse_unit(value, 9)\n}\n\nexport function parse_unit(\n value: string,\n decimals = 18,\n): bigint {\n if (value === \"\") {\n throw new Error(\"parse_unit: empty string\")\n }\n const negative = value.startsWith(\"-\")\n const unsigned = negative ? value.slice(1) : value\n if (!/^(\\d+\\.?\\d*|\\.\\d+)$/.test(unsigned)) {\n throw new Error(`parse_unit: invalid number \"${value}\"`)\n }\n const [integer_str, fraction_str = \"\"] =\n unsigned.split(\".\")\n if (fraction_str.length > decimals) {\n throw new Error(\n `parse_unit: \"${value}\" has more than ${decimals} fractional digits`,\n )\n }\n const integer =\n !integer_str || integer_str === \"\" ? \"0\" : integer_str\n const padded = fraction_str.padEnd(decimals, \"0\")\n const base = 10n ** BigInt(decimals)\n const fraction_bigint =\n padded === \"\" ? 0n : BigInt(padded)\n const result = BigInt(integer) * base + fraction_bigint\n return negative ? -result : result\n}\n"],"mappings":";AAAA,SAAgB,YACd,OACA,WAAW,IACH;CACR,IAAI,UAAU,IACZ,OAAO;CAET,MAAM,WAAW,QAAQ;CACzB,MAAM,MAAM,WAAW,CAAC,QAAQ;CAChC,MAAM,OAAO,OAAO,OAAO,QAAQ;CACnC,MAAM,eAAe,MAAM;CAC3B,MAAM,gBAAgB,MAAM;CAC5B,MAAM,OAAO,WAAW,MAAM;CAC9B,IAAI,kBAAkB,IACpB,OAAO,GAAG,OAAO,aAAa,SAAS;CAEzC,MAAM,WAAW,cACd,SAAS,EACT,SAAS,UAAU,GAAG,EACtB,QAAQ,OAAO,EAAE;CACpB,OAAO,GAAG,OAAO,aAAa,SAAS,EAAE,GAAG;AAC9C;AAEA,SAAgB,aAAa,OAAuB;CAClD,OAAO,YAAY,OAAO,EAAE;AAC9B;AAEA,SAAgB,YAAY,OAAuB;CACjD,OAAO,YAAY,OAAO,CAAC;AAC7B;AAEA,SAAgB,YAAY,OAAuB;CACjD,OAAO,WAAW,OAAO,EAAE;AAC7B;AAEA,SAAgB,WAAW,OAAuB;CAChD,OAAO,WAAW,OAAO,CAAC;AAC5B;AAEA,SAAgB,WACd,OACA,WAAW,IACH;CACR,IAAI,UAAU,IACZ,MAAM,IAAI,MAAM,0BAA0B;CAE5C,MAAM,WAAW,MAAM,WAAW,GAAG;CACrC,MAAM,WAAW,WAAW,MAAM,MAAM,CAAC,IAAI;CAC7C,IAAI,CAAC,sBAAsB,KAAK,QAAQ,GACtC,MAAM,IAAI,MAAM,+BAA+B,MAAM,EAAE;CAEzD,MAAM,CAAC,aAAa,eAAe,MACjC,SAAS,MAAM,GAAG;CACpB,IAAI,aAAa,SAAS,UACxB,MAAM,IAAI,MACR,gBAAgB,MAAM,kBAAkB,SAAS,mBACnD;CAEF,MAAM,UACJ,CAAC,eAAe,gBAAgB,KAAK,MAAM;CAC7C,MAAM,SAAS,aAAa,OAAO,UAAU,GAAG;CAChD,MAAM,OAAO,OAAO,OAAO,QAAQ;CACnC,MAAM,kBACJ,WAAW,KAAK,KAAK,OAAO,MAAM;CACpC,MAAM,SAAS,OAAO,OAAO,IAAI,OAAO;CACxC,OAAO,WAAW,CAAC,SAAS;AAC9B"}
export function bigint_to_hex(
value: bigint,
): `0x${string}` {
return value === 0n ? "0x0" : `0x${value.toString(16)}`
}
import { bytes_to_hex } from "./bytes-to-hex"
export function bytes_to_uint(
bytes: Uint8Array,
): `0x${string}` {
if (bytes.length === 0) return "0x0"
const n = BigInt(bytes_to_hex(bytes))
return `0x${n.toString(16)}`
}
export function hex_to_bigint(hex: `0x${string}`): bigint {
return BigInt(hex)
}
// Vectors from the Ethereum yellow paper / RLP spec.
import { describe, expect, it } from "vitest"
import { bytes_to_hex } from "./bytes-to-hex"
import { hex_to_bytes } from "./hex-to-bytes"
import {
type RlpDecoded,
rlp_decode,
rlp_encode,
} from "./rlp"
function hex(
input: Parameters<typeof rlp_encode>[0],
): string {
return bytes_to_hex(rlp_encode(input))
}
function decode_hex(input: `0x${string}`): RlpDecoded {
return rlp_decode(hex_to_bytes(input))
}
type HexTree = string | HexTree[]
function to_hex_tree(value: RlpDecoded): HexTree {
if (value instanceof Uint8Array)
return bytes_to_hex(value)
return value.map(to_hex_tree)
}
describe("rlp.ts", () => {
it("should encode the empty string as 0x80", () => {
expect(hex(new Uint8Array(0))).toBe("0x80")
})
it("should encode the empty list as 0xc0", () => {
expect(hex([])).toBe("0xc0")
})
it("should encode a single byte < 0x80 as itself", () => {
expect(hex(new Uint8Array([0x00]))).toBe("0x00")
expect(hex(new Uint8Array([0x7f]))).toBe("0x7f")
})
it("should encode the short string 'dog'", () => {
expect(hex("dog")).toBe("0x83646f67")
})
it("should encode the list ['cat', 'dog']", () => {
expect(hex(["cat", "dog"])).toBe("0xc88363617483646f67")
})
it("should encode the integer 0 as the empty byte string", () => {
expect(hex(0)).toBe("0x80")
expect(hex(0n)).toBe("0x80")
})
it("should encode the integer 15 as 0x0f", () => {
expect(hex(15)).toBe("0x0f")
})
it("should encode the integer 1024 as 0x820400", () => {
expect(hex(1024)).toBe("0x820400")
})
it("should encode a long string with the 0xb7+length prefix", () => {
const long =
"Lorem ipsum dolor sit amet, consectetur adipisicing elit"
expect(hex(long).startsWith("0xb8")).toBe(true)
expect(hex(long).slice(0, 6)).toBe("0xb838")
})
it("should encode a nested set of lists per the spec", () => {
expect(hex([[], [[]], [[], [[]]]])).toBe(
"0xc7c0c1c0c3c0c1c0",
)
})
it("should encode a 32-byte bigint with 0xa0 prefix", () => {
// 2^248 — exactly 32 bytes → prefix is 0x80 + 32 = 0xa0
expect(hex(1n << 248n).slice(0, 4)).toBe("0xa0")
})
it("should decode 0x80 as the empty byte string", () => {
expect(to_hex_tree(decode_hex("0x80"))).toBe("0x")
})
it("should decode 0xc0 as the empty list", () => {
expect(to_hex_tree(decode_hex("0xc0"))).toEqual([])
})
it("should decode a single byte < 0x80 as itself", () => {
expect(to_hex_tree(decode_hex("0x00"))).toBe("0x00")
expect(to_hex_tree(decode_hex("0x7f"))).toBe("0x7f")
})
it("should decode 0x83646f67 as 'dog' bytes", () => {
expect(to_hex_tree(decode_hex("0x83646f67"))).toBe(
"0x646f67",
)
})
it("should decode the list ['cat', 'dog']", () => {
expect(
to_hex_tree(decode_hex("0xc88363617483646f67")),
).toEqual(["0x636174", "0x646f67"])
})
it("should decode the integer 1024 (0x820400) as 0x0400", () => {
expect(to_hex_tree(decode_hex("0x820400"))).toBe(
"0x0400",
)
})
it("should decode a long string (0xb7+ prefix)", () => {
const long =
"Lorem ipsum dolor sit amet, consectetur adipisicing elit"
const encoded = rlp_encode(long)
const decoded = rlp_decode(encoded)
expect(decoded).toBeInstanceOf(Uint8Array)
if (decoded instanceof Uint8Array) {
expect(new TextDecoder().decode(decoded)).toBe(long)
}
})
it("should decode the nested-lists vector 0xc7c0c1c0c3c0c1c0", () => {
expect(
to_hex_tree(decode_hex("0xc7c0c1c0c3c0c1c0")),
).toEqual([[], [[]], [[], [[]]]])
})
it("should round-trip a long list (0xf8+ prefix)", () => {
// 30 two-byte items encode to 3 bytes each → 90-byte
// payload → long-list prefix (0xf8+).
const items = Array.from(
{ length: 30 },
(_, i) => new Uint8Array([0x80, i & 0xff]),
)
const encoded = rlp_encode(items)
expect(encoded[0]).toBeGreaterThanOrEqual(0xf8)
const decoded = rlp_decode(encoded)
expect(Array.isArray(decoded)).toBe(true)
if (Array.isArray(decoded)) {
expect(decoded).toHaveLength(30)
for (let i = 0; i < 30; i++) {
const item = decoded[i]
expect(item).toBeInstanceOf(Uint8Array)
if (item instanceof Uint8Array) {
expect(item[1]).toBe(i & 0xff)
}
}
}
})
it("should round-trip a 32-byte bigint", () => {
const big = 1n << 248n
const encoded = rlp_encode(big)
const decoded = rlp_decode(encoded)
expect(decoded).toBeInstanceOf(Uint8Array)
if (decoded instanceof Uint8Array) {
expect(decoded.length).toBe(32)
expect(decoded[0]).toBe(0x01)
}
})
it("should throw on trailing bytes", () => {
expect(() =>
rlp_decode(hex_to_bytes("0x8000")),
).toThrow(/trailing/)
})
it("should throw when the payload overruns the input", () => {
expect(() =>
rlp_decode(hex_to_bytes("0x83ab")),
).toThrow(/overruns/)
})
it("should throw on empty input", () => {
expect(() => rlp_decode(new Uint8Array(0))).toThrow(
/unexpected end/,
)
})
})
// https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/
// Recursive Length Prefix encoding. Used by every Ethereum
// transaction type for the canonical serialized form.
import { hex_to_bytes } from "./hex-to-bytes"
export type RlpInput =
| string
| number
| bigint
| Uint8Array
| RlpInput[]
export function rlp_encode(input: RlpInput): Uint8Array {
if (Array.isArray(input)) return encode_list(input)
return encode_item(input)
}
function encode_item(
item: string | number | bigint | Uint8Array,
): Uint8Array {
const bytes = to_bytes(item)
const first = bytes[0]
if (
bytes.length === 1 &&
first !== undefined &&
first < 0x80
) {
return bytes
}
if (bytes.length <= 55) {
const out = new Uint8Array(1 + bytes.length)
out[0] = 0x80 + bytes.length
out.set(bytes, 1)
return out
}
const length_bytes = encode_length(bytes.length)
const out = new Uint8Array(
1 + length_bytes.length + bytes.length,
)
out[0] = 0xb7 + length_bytes.length
out.set(length_bytes, 1)
out.set(bytes, 1 + length_bytes.length)
return out
}
function encode_list(list: RlpInput[]): Uint8Array {
const items = list.map(rlp_encode)
const total = items.reduce((s, i) => s + i.length, 0)
if (total <= 55) {
const out = new Uint8Array(1 + total)
out[0] = 0xc0 + total
let offset = 1
for (const item of items) {
out.set(item, offset)
offset += item.length
}
return out
}
const length_bytes = encode_length(total)
const out = new Uint8Array(
1 + length_bytes.length + total,
)
out[0] = 0xf7 + length_bytes.length
out.set(length_bytes, 1)
let offset = 1 + length_bytes.length
for (const item of items) {
out.set(item, offset)
offset += item.length
}
return out
}
function to_bytes(
input: string | number | bigint | Uint8Array,
): Uint8Array {
if (input instanceof Uint8Array) return input
if (typeof input === "string") {
if (input.startsWith("0x")) return hex_to_bytes(input)
return new TextEncoder().encode(input)
}
return number_to_bytes(BigInt(input))
}
function number_to_bytes(big: bigint): Uint8Array {
if (big === 0n) return new Uint8Array([])
const hex = big.toString(16)
const padded = hex.padStart(
hex.length + (hex.length % 2),
"0",
)
return hex_to_bytes(padded)
}
function encode_length(length: number): Uint8Array {
if (length === 0) return new Uint8Array([])
const out: number[] = []
let temp = length
while (temp > 0) {
out.unshift(temp & 0xff)
temp >>= 8
}
return new Uint8Array(out)
}
// RLP only preserves bytes and nesting — numbers and strings
// fed into `rlp_encode` come back as byte payloads. Callers
// reapply meaning (number, address, hash, …) from the
// surrounding schema.
export type RlpDecoded = Uint8Array | RlpDecoded[]
export function rlp_decode(bytes: Uint8Array): RlpDecoded {
const [value, consumed] = decode_item(bytes, 0)
if (consumed !== bytes.length) {
throw new Error(
`rlp_decode: ${bytes.length - consumed} trailing byte(s)`,
)
}
return value
}
function decode_item(
bytes: Uint8Array,
offset: number,
): [RlpDecoded, number] {
if (offset >= bytes.length) {
throw new Error("rlp_decode: unexpected end of input")
}
const prefix = bytes[offset]
if (prefix === undefined) {
throw new Error("rlp_decode: unexpected end of input")
}
if (prefix < 0x80) {
return [bytes.slice(offset, offset + 1), 1]
}
if (prefix <= 0xb7) {
const length = prefix - 0x80
const start = offset + 1
const end = start + length
assert_in_bounds(bytes, end)
return [bytes.slice(start, end), 1 + length]
}
if (prefix <= 0xbf) {
const length_of_length = prefix - 0xb7
const length_start = offset + 1
const length_end = length_start + length_of_length
assert_in_bounds(bytes, length_end)
const length = read_length(
bytes.subarray(length_start, length_end),
)
const start = length_end
const end = start + length
assert_in_bounds(bytes, end)
return [
bytes.slice(start, end),
1 + length_of_length + length,
]
}
if (prefix <= 0xf7) {
const length = prefix - 0xc0
const start = offset + 1
const end = start + length
assert_in_bounds(bytes, end)
return [
decode_list_payload(bytes, start, end),
1 + length,
]
}
const length_of_length = prefix - 0xf7
const length_start = offset + 1
const length_end = length_start + length_of_length
assert_in_bounds(bytes, length_end)
const length = read_length(
bytes.subarray(length_start, length_end),
)
const start = length_end
const end = start + length
assert_in_bounds(bytes, end)
return [
decode_list_payload(bytes, start, end),
1 + length_of_length + length,
]
}
function decode_list_payload(
bytes: Uint8Array,
start: number,
end: number,
): RlpDecoded[] {
const items: RlpDecoded[] = []
let cursor = start
while (cursor < end) {
const [value, consumed] = decode_item(bytes, cursor)
items.push(value)
cursor += consumed
}
if (cursor !== end) {
throw new Error(
"rlp_decode: list payload length mismatch",
)
}
return items
}
function read_length(bytes: Uint8Array): number {
let length = 0
for (const byte of bytes) {
length = length * 0x100 + byte
}
return length
}
function assert_in_bounds(
bytes: Uint8Array,
end: number,
): void {
if (end > bytes.length) {
throw new Error("rlp_decode: payload overruns input")
}
}
import {
afterEach,
beforeEach,
describe,
expect,
it,
vi,
} from "vitest"
import {
deadline_in,
now_to_big,
seconds_to_big,
} from "./time"
describe("seconds_to_big", () => {
it("floors fractional seconds", () => {
expect(seconds_to_big(1716207600.999)).toBe(1716207600n)
})
it("passes integer seconds through", () => {
expect(seconds_to_big(0)).toBe(0n)
expect(seconds_to_big(3600)).toBe(3600n)
})
})
describe("clock-based helpers", () => {
const FAKE_NOW_MS = Date.UTC(2026, 4, 20, 0, 0, 0)
const FAKE_NOW_S = BigInt(Math.floor(FAKE_NOW_MS / 1000))
beforeEach(() => {
vi.useFakeTimers()
vi.setSystemTime(new Date(FAKE_NOW_MS))
})
afterEach(() => {
vi.useRealTimers()
})
it("now_to_big returns the current unix second", () => {
expect(now_to_big()).toBe(FAKE_NOW_S)
})
it("deadline_in adds the offset to now", () => {
expect(deadline_in(3600)).toBe(FAKE_NOW_S + 3600n)
})
})
export function seconds_to_big(seconds: number): bigint {
return BigInt(Math.floor(seconds))
}
export function now_to_big(): bigint {
return seconds_to_big(Date.now() / 1000)
}
export function deadline_in(seconds: number): bigint {
return seconds_to_big(Date.now() / 1000 + seconds)
}
import { describe, expect, it } from "vitest"
import { format_unit, parse_unit } from "./unit"
describe("format_unit", () => {
it('returns "0" for 0n', () => {
expect(format_unit(0n)).toBe("0")
})
it("formats 1 ether (default 18 decimals)", () => {
expect(format_unit(10n ** 18n)).toBe("1")
})
it("formats 1.5 ether", () => {
expect(format_unit(15n * 10n ** 17n)).toBe("1.5")
})
it("formats 1 wei at full precision", () => {
expect(format_unit(1n)).toBe("0.000000000000000001")
})
it("strips trailing zeros from the fraction", () => {
expect(format_unit(1234567000000n)).toBe(
"0.000001234567",
)
})
it("formats USDC amounts with decimals=6", () => {
expect(format_unit(1500000n, 6)).toBe("1.5")
})
it('returns "0" for 0n with custom decimals', () => {
expect(format_unit(0n, 6)).toBe("0")
})
it("formats negative values", () => {
expect(format_unit(-15n * 10n ** 17n)).toBe("-1.5")
})
it("formats whole-unit values without trailing dot", () => {
expect(format_unit(5n * 10n ** 18n)).toBe("5")
})
})
describe("parse_unit", () => {
it('parses "0" to 0n', () => {
expect(parse_unit("0")).toBe(0n)
})
it("parses 1 ether (default 18 decimals)", () => {
expect(parse_unit("1")).toBe(10n ** 18n)
})
it("parses 1.5 ether", () => {
expect(parse_unit("1.5")).toBe(15n * 10n ** 17n)
})
it("parses smallest wei (full 18 fractional digits)", () => {
expect(parse_unit("0.000000000000000001")).toBe(1n)
})
it("parses USDC amounts with decimals=6", () => {
expect(parse_unit("1.5", 6)).toBe(1500000n)
})
it('parses leading-dot inputs like ".5"', () => {
expect(parse_unit(".5")).toBe(5n * 10n ** 17n)
})
it('parses trailing-dot inputs like "1."', () => {
expect(parse_unit("1.")).toBe(10n ** 18n)
})
it("parses negative values", () => {
expect(parse_unit("-1.5")).toBe(-15n * 10n ** 17n)
})
it("throws on empty input", () => {
expect(() => parse_unit("")).toThrow()
})
it("throws on garbage input", () => {
expect(() => parse_unit("abc")).toThrow()
})
it("throws on multiple dots", () => {
expect(() => parse_unit("1.2.3")).toThrow()
})
it("throws when fraction exceeds decimals", () => {
expect(() => parse_unit("1.234567", 2)).toThrow()
})
})
describe("round-trip", () => {
it("format_unit ∘ parse_unit is identity for typical values", () => {
const cases = ["0", "1", "1.5", "0.000000000000000001"]
for (const c of cases) {
expect(format_unit(parse_unit(c))).toBe(c)
}
})
it("parse_unit ∘ format_unit is identity for typical bigints", () => {
const cases = [0n, 1n, 10n ** 18n, 15n * 10n ** 17n]
for (const c of cases) {
expect(parse_unit(format_unit(c))).toBe(c)
}
})
it("round-trips USDC amounts with decimals=6", () => {
const human = "1234.56789"
expect(format_unit(parse_unit(human, 6), 6)).toBe(human)
})
})
export function format_unit(
value: bigint,
decimals = 18,
): string {
if (value === 0n) {
return "0"
}
const negative = value < 0n
const abs = negative ? -value : value
const base = 10n ** BigInt(decimals)
const integer_part = abs / base
const fraction_part = abs % base
const sign = negative ? "-" : ""
if (fraction_part === 0n) {
return `${sign}${integer_part.toString()}`
}
const fraction = fraction_part
.toString()
.padStart(decimals, "0")
.replace(/0+$/, "")
return `${sign}${integer_part.toString()}.${fraction}`
}
export function format_ether(value: bigint): string {
return format_unit(value, 18)
}
export function format_gwei(value: bigint): string {
return format_unit(value, 9)
}
export function parse_ether(value: string): bigint {
return parse_unit(value, 18)
}
export function parse_gwei(value: string): bigint {
return parse_unit(value, 9)
}
export function parse_unit(
value: string,
decimals = 18,
): bigint {
if (value === "") {
throw new Error("parse_unit: empty string")
}
const negative = value.startsWith("-")
const unsigned = negative ? value.slice(1) : value
if (!/^(\d+\.?\d*|\.\d+)$/.test(unsigned)) {
throw new Error(`parse_unit: invalid number "${value}"`)
}
const [integer_str, fraction_str = ""] =
unsigned.split(".")
if (fraction_str.length > decimals) {
throw new Error(
`parse_unit: "${value}" has more than ${decimals} fractional digits`,
)
}
const integer =
!integer_str || integer_str === "" ? "0" : integer_str
const padded = fraction_str.padEnd(decimals, "0")
const base = 10n ** BigInt(decimals)
const fraction_bigint =
padded === "" ? 0n : BigInt(padded)
const result = BigInt(integer) * base + fraction_bigint
return negative ? -result : result
}
+3
-5
//#region src/bytes-to-hex.d.ts
declare function bytes_to_hex(
data: Uint8Array,
): `0x${string}`
declare function bytes_to_hex(data: Uint8Array): `0x${string}`;
//#endregion
export { bytes_to_hex }
//# sourceMappingURL=bytes-to-hex.d.ts.map
export { bytes_to_hex };
//# sourceMappingURL=bytes-to-hex.d.ts.map
//#region src/bytes-to-hex.ts
const HEX_ALPHABET = "0123456789abcdef"
const HEX_ALPHABET = "0123456789abcdef";
function bytes_to_hex(data) {
let result = ""
for (let i = 0; i < data.length; i++) {
const byte = data[i]
result += HEX_ALPHABET[byte >> 4]
result += HEX_ALPHABET[byte & 15]
}
return `0x${result}`
let result = "";
for (let i = 0; i < data.length; i++) {
const byte = data[i];
result += HEX_ALPHABET[byte >> 4];
result += HEX_ALPHABET[byte & 15];
}
return `0x${result}`;
}
//#endregion
export { bytes_to_hex }
//# sourceMappingURL=bytes-to-hex.js.map
export { bytes_to_hex };
//# sourceMappingURL=bytes-to-hex.js.map
//#region src/camel-to-kebab.d.ts
declare function camel_to_kebab(input: string): string
declare function camel_to_kebab(input: string): string;
//#endregion
export { camel_to_kebab }
//# sourceMappingURL=camel-to-kebab.d.ts.map
export { camel_to_kebab };
//# sourceMappingURL=camel-to-kebab.d.ts.map
//#region src/camel-to-kebab.ts
function camel_to_kebab(input) {
return input
.replace(/([a-z0-9])([A-Z])/g, "$1-$2")
.replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2")
.toLowerCase()
return input.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").toLowerCase();
}
//#endregion
export { camel_to_kebab }
//# sourceMappingURL=camel-to-kebab.js.map
export { camel_to_kebab };
//# sourceMappingURL=camel-to-kebab.js.map
//#region src/hex-to-bytes.d.ts
declare function hex_to_bytes(hex: string): Uint8Array
declare function hex_to_bytes(hex: string): Uint8Array;
//#endregion
export { hex_to_bytes }
//# sourceMappingURL=hex-to-bytes.d.ts.map
export { hex_to_bytes };
//# sourceMappingURL=hex-to-bytes.d.ts.map

@@ -1,45 +0,43 @@

import { strip_hex_prefix } from "./strip-hex-prefix.js"
import { strip_hex_prefix } from "./strip-hex-prefix.js";
//#region src/hex-to-bytes.ts
const HEX_DECODE = {
0: 0,
1: 1,
2: 2,
3: 3,
4: 4,
5: 5,
6: 6,
7: 7,
8: 8,
9: 9,
a: 10,
A: 10,
b: 11,
B: 11,
c: 12,
C: 12,
d: 13,
D: 13,
e: 14,
E: 14,
f: 15,
F: 15,
}
"0": 0,
"1": 1,
"2": 2,
"3": 3,
"4": 4,
"5": 5,
"6": 6,
"7": 7,
"8": 8,
"9": 9,
a: 10,
A: 10,
b: 11,
B: 11,
c: 12,
C: 12,
d: 13,
D: 13,
e: 14,
E: 14,
f: 15,
F: 15
};
function hex_to_bytes(hex) {
const data = strip_hex_prefix(hex)
if (data.length % 2 !== 0)
throw new Error("Invalid hex string")
const result = new Uint8Array(data.length / 2)
for (let i = 0; i < data.length; i += 2) {
const hi = HEX_DECODE[data[i]]
const lo = HEX_DECODE[data[i + 1]]
if (hi === void 0 || lo === void 0)
throw new Error("Invalid hex character")
result[i / 2] = (hi << 4) | lo
}
return result
const data = strip_hex_prefix(hex);
if (data.length % 2 !== 0) throw new Error("Invalid hex string");
const result = new Uint8Array(data.length / 2);
for (let i = 0; i < data.length; i += 2) {
const hi = HEX_DECODE[data[i]];
const lo = HEX_DECODE[data[i + 1]];
if (hi === void 0 || lo === void 0) throw new Error("Invalid hex character");
result[i / 2] = hi << 4 | lo;
}
return result;
}
//#endregion
export { hex_to_bytes }
//# sourceMappingURL=hex-to-bytes.js.map
export { hex_to_bytes };
//# sourceMappingURL=hex-to-bytes.js.map
//#region src/hex-to-number.d.ts
declare function hex_to_number(hex: `0x${string}`): number
declare function hex_to_number(hex: `0x${string}`): number;
//#endregion
export { hex_to_number }
//# sourceMappingURL=hex-to-number.d.ts.map
export { hex_to_number };
//# sourceMappingURL=hex-to-number.d.ts.map
//#region src/hex-to-number.ts
function hex_to_number(hex) {
return Number(hex)
return Number(hex);
}
//#endregion
export { hex_to_number }
//# sourceMappingURL=hex-to-number.js.map
export { hex_to_number };
//# sourceMappingURL=hex-to-number.js.map

@@ -1,16 +0,14 @@

import { bytes_to_hex } from "./bytes-to-hex.js"
import { camel_to_kebab } from "./camel-to-kebab.js"
import { hex_to_bytes } from "./hex-to-bytes.js"
import { hex_to_number } from "./hex-to-number.js"
import { invariant } from "./invariant.js"
import { number_to_hex } from "./number-to-hex.js"
import { strip_hex_prefix } from "./strip-hex-prefix.js"
export {
bytes_to_hex,
camel_to_kebab,
hex_to_bytes,
hex_to_number,
invariant,
number_to_hex,
strip_hex_prefix,
}
import { bigint_to_hex } from "./bigint-to-hex.js";
import { bytes_to_hex } from "./bytes-to-hex.js";
import { bytes_to_uint } from "./bytes-to-uint.js";
import { camel_to_kebab } from "./camel-to-kebab.js";
import { hex_to_bigint } from "./hex-to-bigint.js";
import { hex_to_bytes } from "./hex-to-bytes.js";
import { hex_to_number } from "./hex-to-number.js";
import { invariant } from "./invariant.js";
import { number_to_hex } from "./number-to-hex.js";
import { RlpDecoded, RlpInput, rlp_decode, rlp_encode } from "./rlp.js";
import { strip_hex_prefix } from "./strip-hex-prefix.js";
import { deadline_in, now_to_big, seconds_to_big } from "./time.js";
import { format_ether, format_gwei, format_unit, parse_ether, parse_gwei, parse_unit } from "./unit.js";
export { RlpDecoded, RlpInput, bigint_to_hex, bytes_to_hex, bytes_to_uint, camel_to_kebab, deadline_in, format_ether, format_gwei, format_unit, hex_to_bigint, hex_to_bytes, hex_to_number, invariant, now_to_big, number_to_hex, parse_ether, parse_gwei, parse_unit, rlp_decode, rlp_encode, seconds_to_big, strip_hex_prefix };

@@ -1,17 +0,15 @@

import { bytes_to_hex } from "./bytes-to-hex.js"
import { camel_to_kebab } from "./camel-to-kebab.js"
import { strip_hex_prefix } from "./strip-hex-prefix.js"
import { hex_to_bytes } from "./hex-to-bytes.js"
import { hex_to_number } from "./hex-to-number.js"
import { invariant } from "./invariant.js"
import { number_to_hex } from "./number-to-hex.js"
import { bigint_to_hex } from "./bigint-to-hex.js";
import { bytes_to_hex } from "./bytes-to-hex.js";
import { bytes_to_uint } from "./bytes-to-uint.js";
import { camel_to_kebab } from "./camel-to-kebab.js";
import { hex_to_bigint } from "./hex-to-bigint.js";
import { strip_hex_prefix } from "./strip-hex-prefix.js";
import { hex_to_bytes } from "./hex-to-bytes.js";
import { hex_to_number } from "./hex-to-number.js";
import { invariant } from "./invariant.js";
import { number_to_hex } from "./number-to-hex.js";
import { rlp_decode, rlp_encode } from "./rlp.js";
import { deadline_in, now_to_big, seconds_to_big } from "./time.js";
import { format_ether, format_gwei, format_unit, parse_ether, parse_gwei, parse_unit } from "./unit.js";
export {
bytes_to_hex,
camel_to_kebab,
hex_to_bytes,
hex_to_number,
invariant,
number_to_hex,
strip_hex_prefix,
}
export { bigint_to_hex, bytes_to_hex, bytes_to_uint, camel_to_kebab, deadline_in, format_ether, format_gwei, format_unit, hex_to_bigint, hex_to_bytes, hex_to_number, invariant, now_to_big, number_to_hex, parse_ether, parse_gwei, parse_unit, rlp_decode, rlp_encode, seconds_to_big, strip_hex_prefix };
//#region src/invariant.d.ts
declare function invariant(
condition: unknown,
message: string,
): asserts condition
declare function invariant(condition: unknown, message: string): asserts condition;
//#endregion
export { invariant }
//# sourceMappingURL=invariant.d.ts.map
export { invariant };
//# sourceMappingURL=invariant.d.ts.map
//#region src/invariant.ts
function invariant(condition, message) {
if (condition) return
throw new Error(`message: ${message}`)
if (condition) return;
throw new Error(`message: ${message}`);
}
//#endregion
export { invariant }
//# sourceMappingURL=invariant.js.map
export { invariant };
//# sourceMappingURL=invariant.js.map
//#region src/number-to-hex.d.ts
declare function number_to_hex(
number: number,
): `0x${string}`
declare function number_to_hex(number: number): `0x${string}`;
//#endregion
export { number_to_hex }
//# sourceMappingURL=number-to-hex.d.ts.map
export { number_to_hex };
//# sourceMappingURL=number-to-hex.d.ts.map
//#region src/number-to-hex.ts
function number_to_hex(number) {
return `0x${number.toString(16)}`
return `0x${number.toString(16)}`;
}
//#endregion
export { number_to_hex }
//# sourceMappingURL=number-to-hex.js.map
export { number_to_hex };
//# sourceMappingURL=number-to-hex.js.map
//#region src/strip-hex-prefix.d.ts
declare function strip_hex_prefix(hex: string): string
declare function strip_hex_prefix(hex: string): string;
//#endregion
export { strip_hex_prefix }
//# sourceMappingURL=strip-hex-prefix.d.ts.map
export { strip_hex_prefix };
//# sourceMappingURL=strip-hex-prefix.d.ts.map
//#region src/strip-hex-prefix.ts
function strip_hex_prefix(hex) {
return hex.startsWith("0x") ? hex.substring(2) : hex
return hex.startsWith("0x") ? hex.substring(2) : hex;
}
//#endregion
export { strip_hex_prefix }
//# sourceMappingURL=strip-hex-prefix.js.map
export { strip_hex_prefix };
//# sourceMappingURL=strip-hex-prefix.js.map

@@ -5,3 +5,3 @@ {

"type": "module",
"version": "0.0.43",
"version": "0.0.44",
"publishConfig": {

@@ -21,6 +21,2 @@ "access": "public"

],
"dependencies": {
"@ethernauta/chain": "0.0.43",
"@ethernauta/transport": "0.0.43"
},
"peerDependencies": {

@@ -36,6 +32,7 @@ "valibot": "^1.1.0"

"dev": "tsdown --watch",
"build": "tsdown && biome format --write dist",
"build": "tsdown",
"lint": "biome check .",
"lint:fix": "biome check --write --unsafe .",
"format": "biome format --write .",
"typecheck": "tsc --noEmit",
"test:unit": "vitest",

@@ -42,0 +39,0 @@ "test:cli": "bun src/compiler/cli.ts --abi src/erc/165/IERC165.abi.json --out src/erc/165"

+53
-26

@@ -5,3 +5,3 @@ [![bundlejs](https://deno.bundlejs.com/badge?q=@ethernauta/utils&treeshake=[*])](https://deno.bundlejs.com/?q=@ethernauta/utils&treeshake=[*])

This module is a set of small, dependency-free utilities used across the other packages. It is intentionally generic — nothing here knows about Ethereum, chains, or transactions.
This module is a set of small, dependency-free utilities used across the other packages. It is intentionally generic — nothing here knows about Ethereum, chains, or transactions. Pure, side-effect-free, no third-party runtime dependencies. **No new dependencies in `@ethernauta/utils`** is a hard rule of the monorepo.

@@ -13,5 +13,9 @@ ## Modules

- [cli](https://github.com/niconiahi/ethernauta/tree/main/packages/cli) [[NPM](https://www.npmjs.com/package/@ethernauta/cli)]
- [core](https://github.com/niconiahi/ethernauta/tree/main/packages/core) [[NPM](https://www.npmjs.com/package/@ethernauta/core)]
- [crypto](https://github.com/niconiahi/ethernauta/tree/main/packages/crypto) [[NPM](https://www.npmjs.com/package/@ethernauta/crypto)]
- [eip](https://github.com/niconiahi/ethernauta/tree/main/packages/eip) [[NPM](https://www.npmjs.com/package/@ethernauta/eip)]
- [ens](https://github.com/niconiahi/ethernauta/tree/main/packages/ens) [[NPM](https://www.npmjs.com/package/@ethernauta/ens)]
- [erc](https://github.com/niconiahi/ethernauta/tree/main/packages/erc) [[NPM](https://www.npmjs.com/package/@ethernauta/erc)]
- [eth](https://github.com/niconiahi/ethernauta/tree/main/packages/eth) [[NPM](https://www.npmjs.com/package/@ethernauta/eth)]
- [react](https://github.com/niconiahi/ethernauta/tree/main/packages/react) [[NPM](https://www.npmjs.com/package/@ethernauta/react)]
- [transaction](https://github.com/niconiahi/ethernauta/tree/main/packages/transaction) [[NPM](https://www.npmjs.com/package/@ethernauta/transaction)]

@@ -24,60 +28,83 @@ - [transport](https://github.com/niconiahi/ethernauta/tree/main/packages/transport) [[NPM](https://www.npmjs.com/package/@ethernauta/transport)]

### `camel_to_kebab`
### Hex ↔ bytes
```ts
import { camel_to_kebab } from "@ethernauta/utils"
import { bytes_to_hex, hex_to_bytes, strip_hex_prefix } from "@ethernauta/utils"
const kebab = camel_to_kebab("helloWorld") // "hello-world"
const hex = bytes_to_hex(new Uint8Array([0xde, 0xad, 0xbe, 0xef])) // "0xdeadbeef"
const bytes = hex_to_bytes("0xdeadbeef") // Uint8Array
const stripped = strip_hex_prefix("0xdeadbeef") // "deadbeef"
```
### `invariant`
### Hex ↔ number
```ts
import { invariant } from "@ethernauta/utils"
import { hex_to_number, number_to_hex } from "@ethernauta/utils"
const input: string | null = "helloWorld"
invariant(typeof input === "string", "input must be a string")
// input is narrowed to `string` from here on
const hex = number_to_hex(255) // "0xff"
const value = hex_to_number("0xff") // 255
```
### `number_to_hex`
### Bytes ↔ unsigned integer
```ts
import { number_to_hex } from "@ethernauta/utils"
import { bytes_to_uint } from "@ethernauta/utils"
const hex = number_to_hex(255) // "0xff"
const n = bytes_to_uint(new Uint8Array([0x01, 0x00])) // 256n
```
### `hex_to_number`
### RLP encoding
```ts
import { hex_to_number } from "@ethernauta/utils"
import { rlp_encode, type RlpInput } from "@ethernauta/utils"
const value = hex_to_number("0xff") // 255
const encoded = rlp_encode([
new Uint8Array([0x01]),
new Uint8Array([0x02, 0x03]),
])
```
### `bytes_to_hex`
### Wei ↔ string formatting
```ts
import { bytes_to_hex } from "@ethernauta/utils"
import {
format_ether, format_gwei, format_unit,
parse_ether, parse_gwei, parse_unit,
} from "@ethernauta/utils"
const hex = bytes_to_hex(new Uint8Array([0xde, 0xad, 0xbe, 0xef]))
// "0xdeadbeef"
format_ether(1_000_000_000_000_000_000n) // "1"
format_gwei(2_000_000_000n) // "2"
format_unit(123_456n, 4) // "12.3456"
parse_ether("1.5") // 1500000000000000000n
parse_gwei("2") // 2000000000n
parse_unit("12.34", 4) // 123400n
```
### `hex_to_bytes`
### Time helpers
```ts
import { hex_to_bytes } from "@ethernauta/utils"
import { seconds_to_big, now_to_big, deadline_in } from "@ethernauta/utils"
const bytes = hex_to_bytes("0xdeadbeef")
// Uint8Array([0xde, 0xad, 0xbe, 0xef])
const now = now_to_big() // BigInt seconds since epoch
const in_one_minute = deadline_in(60) // now + 60
const ms_as_bigint = seconds_to_big(30) // 30n
```
### `strip_hex_prefix`
### Case conversion
```ts
import { strip_hex_prefix } from "@ethernauta/utils"
import { camel_to_kebab } from "@ethernauta/utils"
const stripped = strip_hex_prefix("0xdeadbeef") // "deadbeef"
camel_to_kebab("transferFrom") // "transfer-from"
```
### Type narrowing — `invariant`
```ts
import { invariant } from "@ethernauta/utils"
const input: string | null = "helloWorld"
invariant(typeof input === "string", "input must be a string")
// input is narrowed to `string` from here on
```

@@ -0,3 +1,6 @@

export * from "./bigint-to-hex"
export * from "./bytes-to-hex"
export * from "./bytes-to-uint"
export * from "./camel-to-kebab"
export * from "./hex-to-bigint"
export * from "./hex-to-bytes"

@@ -7,2 +10,5 @@ export * from "./hex-to-number"

export * from "./number-to-hex"
export * from "./rlp"
export * from "./strip-hex-prefix"
export * from "./time"
export * from "./unit"