@ethernauta/utils
Advanced tools
| //#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"} |
+139
| 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"} |
+14
| //#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"} |
+43
| //#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) | ||
| } |
+180
| // 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/, | ||
| ) | ||
| }) | ||
| }) |
+220
| // 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) | ||
| }) | ||
| }) |
+11
| 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) | ||
| } |
+113
| 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) | ||
| }) | ||
| }) |
+67
| 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 | ||
| } |
| //#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 |
+10
-10
| //#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 |
+36
-38
@@ -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 |
+14
-16
@@ -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 }; |
+14
-16
@@ -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 |
+3
-6
@@ -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 @@ [](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 | ||
| ``` |
+6
-0
@@ -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" |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
58965
293.94%1
-66.67%73
82.5%1045
311.42%108
33.33%- Removed
- Removed
- Removed
- Removed