UNPKG

necjs

Version:
1,330 lines (1,322 loc) 2.38 MB
import axios, { AxiosError } from 'axios'; import fs from 'fs'; import 'path'; import { webcrypto } from 'node:crypto'; import require$$0$3 from 'events'; import require$$1$1 from 'https'; import require$$2 from 'http'; import require$$3 from 'net'; import require$$4 from 'tls'; import require$$1 from 'crypto'; import require$$0$2 from 'stream'; import require$$7 from 'url'; import require$$0 from 'zlib'; import require$$0$1 from 'buffer'; function _mergeNamespaces(n, m) { m.forEach(function (e) { e && typeof e !== 'string' && !Array.isArray(e) && Object.keys(e).forEach(function (k) { if (k !== 'default' && !(k in n)) { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); }); return Object.freeze(n); } /** * Internal helpers for u64. BigUint64Array is too slow as per 2025, so we implement it using Uint32Array. * @todo re-check https://issues.chromium.org/issues/42212588 * @module */ const U32_MASK64$1 = /* @__PURE__ */ BigInt(2 ** 32 - 1); const _32n$1 = /* @__PURE__ */ BigInt(32); function fromBig$1(n, le = false) { if (le) return { h: Number(n & U32_MASK64$1), l: Number((n >> _32n$1) & U32_MASK64$1) }; return { h: Number((n >> _32n$1) & U32_MASK64$1) | 0, l: Number(n & U32_MASK64$1) | 0 }; } function split$1(lst, le = false) { const len = lst.length; let Ah = new Uint32Array(len); let Al = new Uint32Array(len); for (let i = 0; i < len; i++) { const { h, l } = fromBig$1(lst[i], le); [Ah[i], Al[i]] = [h, l]; } return [Ah, Al]; } // Left rotate for Shift in [1, 32) const rotlSH$1 = (h, l, s) => (h << s) | (l >>> (32 - s)); const rotlSL$1 = (h, l, s) => (l << s) | (h >>> (32 - s)); // Left rotate for Shift in (32, 64), NOTE: 32 is special case. const rotlBH$1 = (h, l, s) => (l << (s - 32)) | (h >>> (64 - s)); const rotlBL$1 = (h, l, s) => (h << (s - 32)) | (l >>> (64 - s)); /** * Utilities for hex, bytes, CSPRNG. * @module */ /*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */ // We use WebCrypto aka globalThis.crypto, which exists in browsers and node.js 16+. // node.js versions earlier than v19 don't declare it in global scope. // For node.js, package.json#exports field mapping rewrites import // from `crypto` to `cryptoNode`, which imports native module. // Makes the utils un-importable in browsers without a bundler. // Once node.js 18 is deprecated (2025-04-30), we can just drop the import. /** Checks if something is Uint8Array. Be careful: nodejs Buffer will return true. */ function isBytes(a) { return a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array'); } /** Asserts something is positive integer. */ function anumber(n) { if (!Number.isSafeInteger(n) || n < 0) throw new Error('positive integer expected, got ' + n); } /** Asserts something is Uint8Array. */ function abytes(b, ...lengths) { if (!isBytes(b)) throw new Error('Uint8Array expected'); if (lengths.length > 0 && !lengths.includes(b.length)) throw new Error('Uint8Array expected of length ' + lengths + ', got length=' + b.length); } /** Asserts a hash instance has not been destroyed / finished */ function aexists(instance, checkFinished = true) { if (instance.destroyed) throw new Error('Hash instance has been destroyed'); if (checkFinished && instance.finished) throw new Error('Hash#digest() has already been called'); } /** Asserts output is properly-sized byte array */ function aoutput(out, instance) { abytes(out); const min = instance.outputLen; if (out.length < min) { throw new Error('digestInto() expects output buffer of length at least ' + min); } } /** Cast u8 / u16 / u32 to u32. */ function u32$1(arr) { return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4)); } /** Zeroize a byte array. Warning: JS provides no guarantees. */ function clean(...arrays) { for (let i = 0; i < arrays.length; i++) { arrays[i].fill(0); } } /** Is current platform little-endian? Most are. Big-Endian platform: IBM */ const isLE$1 = /* @__PURE__ */ (() => new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44)(); /** The byte swap operation for uint32 */ function byteSwap(word) { return (((word << 24) & 0xff000000) | ((word << 8) & 0xff0000) | ((word >>> 8) & 0xff00) | ((word >>> 24) & 0xff)); } /** In place byte swap for Uint32Array */ function byteSwap32(arr) { for (let i = 0; i < arr.length; i++) { arr[i] = byteSwap(arr[i]); } return arr; } const swap32IfBE = isLE$1 ? (u) => u : byteSwap32; // Built-in hex conversion https://caniuse.com/mdn-javascript_builtins_uint8array_fromhex const hasHexBuiltin = /* @__PURE__ */ (() => // @ts-ignore typeof Uint8Array.from([]).toHex === 'function' && typeof Uint8Array.fromHex === 'function')(); // Array where index 0xf0 (240) is mapped to string 'f0' const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0')); /** * Convert byte array to hex string. Uses built-in function, when available. * @example bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])) // 'cafe0123' */ function bytesToHex(bytes) { abytes(bytes); // @ts-ignore if (hasHexBuiltin) return bytes.toHex(); // pre-caching improves the speed 6x let hex = ''; for (let i = 0; i < bytes.length; i++) { hex += hexes[bytes[i]]; } return hex; } /** * Converts string to bytes using UTF8 encoding. * @example utf8ToBytes('abc') // Uint8Array.from([97, 98, 99]) */ function utf8ToBytes$1(str) { if (typeof str !== 'string') throw new Error('string expected'); return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809 } /** * Normalizes (non-hex) string or Uint8Array to Uint8Array. * Warning: when Uint8Array is passed, it would NOT get copied. * Keep in mind for future mutable operations. */ function toBytes$1(data) { if (typeof data === 'string') data = utf8ToBytes$1(data); abytes(data); return data; } /** For runtime check if class implements interface */ let Hash$1 = class Hash { }; /** Wraps hash function, creating an interface on top of it */ function createHasher(hashCons) { const hashC = (msg) => hashCons().update(toBytes$1(msg)).digest(); const tmp = hashCons(); hashC.outputLen = tmp.outputLen; hashC.blockLen = tmp.blockLen; hashC.create = () => hashCons(); return hashC; } /** * SHA3 (keccak) hash function, based on a new "Sponge function" design. * Different from older hashes, the internal state is bigger than output size. * * Check out [FIPS-202](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf), * [Website](https://keccak.team/keccak.html), * [the differences between SHA-3 and Keccak](https://crypto.stackexchange.com/questions/15727/what-are-the-key-differences-between-the-draft-sha-3-standard-and-the-keccak-sub). * * Check out `sha3-addons` module for cSHAKE, k12, and others. * @module */ // No __PURE__ annotations in sha3 header: // EVERYTHING is in fact used on every export. // Various per round constants calculations const _0n$1 = BigInt(0); const _1n$1 = BigInt(1); const _2n$1 = BigInt(2); const _7n$1 = BigInt(7); const _256n$1 = BigInt(256); const _0x71n$1 = BigInt(0x71); const SHA3_PI$1 = []; const SHA3_ROTL$1 = []; const _SHA3_IOTA$1 = []; for (let round = 0, R = _1n$1, x = 1, y = 0; round < 24; round++) { // Pi [x, y] = [y, (2 * x + 3 * y) % 5]; SHA3_PI$1.push(2 * (5 * y + x)); // Rotational SHA3_ROTL$1.push((((round + 1) * (round + 2)) / 2) % 64); // Iota let t = _0n$1; for (let j = 0; j < 7; j++) { R = ((R << _1n$1) ^ ((R >> _7n$1) * _0x71n$1)) % _256n$1; if (R & _2n$1) t ^= _1n$1 << ((_1n$1 << /* @__PURE__ */ BigInt(j)) - _1n$1); } _SHA3_IOTA$1.push(t); } const IOTAS = split$1(_SHA3_IOTA$1, true); const SHA3_IOTA_H$1 = IOTAS[0]; const SHA3_IOTA_L$1 = IOTAS[1]; // Left rotation (without 0, 32, 64) const rotlH$1 = (h, l, s) => (s > 32 ? rotlBH$1(h, l, s) : rotlSH$1(h, l, s)); const rotlL$1 = (h, l, s) => (s > 32 ? rotlBL$1(h, l, s) : rotlSL$1(h, l, s)); /** `keccakf1600` internal function, additionally allows to adjust round count. */ function keccakP$1(s, rounds = 24) { const B = new Uint32Array(5 * 2); // NOTE: all indices are x2 since we store state as u32 instead of u64 (bigints to slow in js) for (let round = 24 - rounds; round < 24; round++) { // Theta θ for (let x = 0; x < 10; x++) B[x] = s[x] ^ s[x + 10] ^ s[x + 20] ^ s[x + 30] ^ s[x + 40]; for (let x = 0; x < 10; x += 2) { const idx1 = (x + 8) % 10; const idx0 = (x + 2) % 10; const B0 = B[idx0]; const B1 = B[idx0 + 1]; const Th = rotlH$1(B0, B1, 1) ^ B[idx1]; const Tl = rotlL$1(B0, B1, 1) ^ B[idx1 + 1]; for (let y = 0; y < 50; y += 10) { s[x + y] ^= Th; s[x + y + 1] ^= Tl; } } // Rho (ρ) and Pi (π) let curH = s[2]; let curL = s[3]; for (let t = 0; t < 24; t++) { const shift = SHA3_ROTL$1[t]; const Th = rotlH$1(curH, curL, shift); const Tl = rotlL$1(curH, curL, shift); const PI = SHA3_PI$1[t]; curH = s[PI]; curL = s[PI + 1]; s[PI] = Th; s[PI + 1] = Tl; } // Chi (χ) for (let y = 0; y < 50; y += 10) { for (let x = 0; x < 10; x++) B[x] = s[y + x]; for (let x = 0; x < 10; x++) s[y + x] ^= ~B[(x + 2) % 10] & B[(x + 4) % 10]; } // Iota (ι) s[0] ^= SHA3_IOTA_H$1[round]; s[1] ^= SHA3_IOTA_L$1[round]; } clean(B); } /** Keccak sponge function. */ let Keccak$1 = class Keccak extends Hash$1 { // NOTE: we accept arguments in bytes instead of bits here. constructor(blockLen, suffix, outputLen, enableXOF = false, rounds = 24) { super(); this.pos = 0; this.posOut = 0; this.finished = false; this.destroyed = false; this.enableXOF = false; this.blockLen = blockLen; this.suffix = suffix; this.outputLen = outputLen; this.enableXOF = enableXOF; this.rounds = rounds; // Can be passed from user as dkLen anumber(outputLen); // 1600 = 5x5 matrix of 64bit. 1600 bits === 200 bytes // 0 < blockLen < 200 if (!(0 < blockLen && blockLen < 200)) throw new Error('only keccak-f1600 function is supported'); this.state = new Uint8Array(200); this.state32 = u32$1(this.state); } clone() { return this._cloneInto(); } keccak() { swap32IfBE(this.state32); keccakP$1(this.state32, this.rounds); swap32IfBE(this.state32); this.posOut = 0; this.pos = 0; } update(data) { aexists(this); data = toBytes$1(data); abytes(data); const { blockLen, state } = this; const len = data.length; for (let pos = 0; pos < len;) { const take = Math.min(blockLen - this.pos, len - pos); for (let i = 0; i < take; i++) state[this.pos++] ^= data[pos++]; if (this.pos === blockLen) this.keccak(); } return this; } finish() { if (this.finished) return; this.finished = true; const { state, suffix, pos, blockLen } = this; // Do the padding state[pos] ^= suffix; if ((suffix & 0x80) !== 0 && pos === blockLen - 1) this.keccak(); state[blockLen - 1] ^= 0x80; this.keccak(); } writeInto(out) { aexists(this, false); abytes(out); this.finish(); const bufferOut = this.state; const { blockLen } = this; for (let pos = 0, len = out.length; pos < len;) { if (this.posOut >= blockLen) this.keccak(); const take = Math.min(blockLen - this.posOut, len - pos); out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos); this.posOut += take; pos += take; } return out; } xofInto(out) { // Sha3/Keccak usage with XOF is probably mistake, only SHAKE instances can do XOF if (!this.enableXOF) throw new Error('XOF is not possible for this instance'); return this.writeInto(out); } xof(bytes) { anumber(bytes); return this.xofInto(new Uint8Array(bytes)); } digestInto(out) { aoutput(out, this); if (this.finished) throw new Error('digest() was already called'); this.writeInto(out); this.destroy(); return out; } digest() { return this.digestInto(new Uint8Array(this.outputLen)); } destroy() { this.destroyed = true; clean(this.state); } _cloneInto(to) { const { blockLen, suffix, outputLen, rounds, enableXOF } = this; to || (to = new Keccak(blockLen, suffix, outputLen, enableXOF, rounds)); to.state32.set(this.state32); to.pos = this.pos; to.posOut = this.posOut; to.finished = this.finished; to.rounds = rounds; // Suffix can change in cSHAKE to.suffix = suffix; to.outputLen = outputLen; to.enableXOF = enableXOF; to.destroyed = this.destroyed; return to; } }; const gen$1 = (suffix, blockLen, outputLen) => createHasher(() => new Keccak$1(blockLen, suffix, outputLen)); /** SHA3-512 hash function. */ const sha3_512 = /* @__PURE__ */ (() => gen$1(0x06, 72, 512 / 8))(); /** keccak-256 hash function. Different from SHA3-256. */ const keccak_256$1 = /* @__PURE__ */ (() => gen$1(0x01, 136, 256 / 8))(); // src/utils.ts // Ensure BigInt is available (polyfill check) if (typeof BigInt === 'undefined') { throw new Error('BigInt is not supported in this environment. Please use a polyfill or upgrade your JavaScript engine.'); } // Check for BigInt ** operator support let BIGINT_EXPONENTIATION_SUPPORTED = true; try { BigInt(2) ** BigInt(3); } catch (error) { BIGINT_EXPONENTIATION_SUPPORTED = false; } // Generic base for decimal handling const TEN = BigInt(10); // Default number of decimals (e.g., for Ether or NEC token) const DEFAULT_DECIMALS = 18; // NEC token decimals (replace if different) const NEC_DECIMALS = 18; // Safe BigInt exponentiation function to handle browser compatibility issues function safeBigIntPow(base, exponent) { if (BIGINT_EXPONENTIATION_SUPPORTED) { try { return base ** exponent; } catch (error) { // Fall through to manual implementation } } // Manual implementation for browsers that don't support BigInt ** operator if (exponent === BigInt(0)) return BigInt(1); if (exponent === BigInt(1)) return base; if (exponent < BigInt(0)) { throw new Error('Negative exponents not supported for BigInt'); } let result = BigInt(1); let currentBase = base; let currentExponent = exponent; while (currentExponent > BigInt(0)) { if (currentExponent % BigInt(2) === BigInt(1)) { result = result * currentBase; } currentBase = currentBase * currentBase; currentExponent = currentExponent / BigInt(2); } return result; } // BigInt factor for converting whole units ↔ base units const WEI_FACTOR = safeBigIntPow(TEN, BigInt(18)); /** * Convert a hex string to a decimal (string or number). * Assumes input is already in base units. */ function hexToDecimalString(hex) { if (typeof hex !== 'string') { throw new TypeError(`hexToDecimalString: expected a string, got ${typeof hex}`); } const raw = hex.trim().toLowerCase(); if (raw === '0x') return 0; const normalized = raw.startsWith('0x') ? raw : `0x${raw}`; if (!/^0x[0-9a-f]+$/.test(normalized)) { throw new Error(`hexToDecimalString: invalid hex string "${hex}"`); } if (normalized === '0x0') return 0; // handle zero case const asDec = BigInt(normalized).toString(10); return isNaN(Number(asDec)) ? asDec : Number(asDec); } /** * Convert a hex string to a decimal-string (no extra multiplication). * Use for normalizing RPC response fields already in base units. */ function normalizeHexField(key, hex) { if (hex === '0x') return '0'; return BigInt(hex).toString(10); } /** * Serialize a decimal (number, numeric-string, or bigint) to hex-with-0x. * Assumes input is already in base units. */ function decimalToHex(value) { var _a; if (!value) return ''; // handle null/undefined gracefully if ((_a = value === null || value === void 0 ? void 0 : value.toString()) === null || _a === void 0 ? void 0 : _a.startsWith('0x')) { return value; // already hex } return '0x' + BigInt(value).toString(16); } /** * Generic: parse whole- or fractional-unit amount into base-unit hex. * Accepts number|string|bigint, handles fractional up to `decimals`. */ function parseUnits(value, decimals = DEFAULT_DECIMALS) { let str; if (typeof value === 'number') { str = value.toFixed(decimals); } else { str = value.toString(); } if (str === null || str === void 0 ? void 0 : str.startsWith('0x')) { return str; // already hex } const [wholePart, fracPart = ''] = str.split('.'); if (!/^\d+$/.test(wholePart) || !/^\d*$/.test(fracPart)) { throw new Error(`parseUnits: invalid numeric value "${value}"`); } if (fracPart.length > decimals) { throw new Error(`parseUnits: too many decimal places (max ${decimals}) in "${value}"`); } const factor = decimals === DEFAULT_DECIMALS ? WEI_FACTOR : safeBigIntPow(TEN, BigInt(Number(decimals))); const whole = BigInt(wholePart) * factor; const frac = BigInt(fracPart.padEnd(decimals, '0')); const combined = whole + frac; return '0x' + combined.toString(16); } /** * Convert an Ether value (number|string|bigint), including fractional, * → Wei → hex-with-0x. */ function etherToWeiHex(value) { return parseUnits(value, DEFAULT_DECIMALS); } /** * Convert a Wei-hex (or bigint or numeric string) into an Ether decimal string. */ function hexToEther(value) { return formatUnits(value, DEFAULT_DECIMALS); } /** * Generic: format a base-unit amount (hex, number, or bigint) * into a human-readable decimal string. */ function formatUnits(value, decimals = DEFAULT_DECIMALS) { const big = typeof value === 'string' && value.startsWith('0x') ? BigInt(value) : BigInt(value); const factor = decimals === DEFAULT_DECIMALS ? WEI_FACTOR : safeBigIntPow(TEN, BigInt(Number(decimals))); const integer = big / factor; const fraction = big % factor; let fracStr = fraction .toString() .padStart(decimals, '0') .replace(/0+$/, ''); return fracStr ? `${integer.toString()}.${fracStr}` : integer.toString(); } /** * Convert a NEC base-unit amount (hex, number, or bigint) into a NEC decimal string. */ function hexToNec(value) { return formatUnits(value, NEC_DECIMALS); } /** * Convert a whole-NEC amount (number|string|bigint) into base-unit hex. */ function necToHex(value) { return parseUnits(value, NEC_DECIMALS); } /** * Convert a Wei (number, bigint, or hex string) directly into a NEC decimal string. * Useful when NEC is pegged 1:1 with Ether base units. */ function weiToNec(value) { return formatUnits(value, NEC_DECIMALS); } /** * Walk and serialize all fields in TxParams for JSON-RPC. */ function serializeForRpc(payload) { const out = {}; for (const [key, val] of Object.entries(payload)) { if (typeof val === 'number' || (/^([0-9]*\.?[0-9]+)$/.test(val))) { if (key === 'value') { out[key] = etherToWeiHex(val); } else if (key.toLowerCase().includes('amount') || key.toLowerCase().includes('balance')) { out[key] = parseUnits(val); } else { out[key] = decimalToHex(val); } } else { out[key] = val; } } return out; } /** * Walk and normalize JSON-RPC response (hex → decimal string or number). */ function normalizeResponse(resp) { if (resp === null) return {}; if (typeof resp === 'boolean') return resp; if (typeof resp === 'string') { if (/^0x[a-fA-F0-9]{40,}$/.test(resp)) return resp; return hexToDecimalString(resp); } if (Array.isArray(resp)) { return resp.map((v) => typeof v === 'object' ? normalizeResponse(v) : v); } const out = {}; for (const [key, val] of Object.entries(resp)) { if (typeof val === 'string' && val.startsWith('0x')) { if (/^0x[0-9a-fA-F]{64}$/.test(val) || // 32-byte hash /^0x[0-9a-fA-F]{40}$/.test(val) // 20-byte address ) { out[key] = val; } else { out[key] = normalizeHexField(key, val); } } else if (Array.isArray(val)) { out[key] = val.map((v) => typeof v === 'object' ? normalizeResponse(v) : v); } else if (val && typeof val === 'object') { out[key] = normalizeResponse(val); } else { out[key] = val; } } return out; } /** * Checks if a string is a valid Ethereum/EVM address. */ function isValidAddress(address) { return (typeof address === 'string' && address.startsWith('0x') && address.length === 42 && /^[0-9a-fA-F]{40}$/.test(address.slice(2))); } /** * Convert a decimal Ether value (number|string|bigint) to a Wei value as a string (base 10, not hex). * E.g., 1.23 -> '1230000000000000000' */ function decimalToWei(value, decimals = DEFAULT_DECIMALS) { // Use parseUnits to get the hex, then convert to BigInt and return as string if (typeof value === 'number' || typeof value === 'string' || typeof value === 'bigint') { const str = value.toString(); const hex = parseUnits(str, decimals); // returns hex string return BigInt(hex).toString(10); } throw new Error('decimalToWei: invalid value'); } /** * Extract a Kyber public key (ek) from a secret key (dk) hex string. * If input already appears to be a public key (800/1184/1568 bytes), it is returned as-is. * Returns a lowercase hex string without 0x prefix. */ function kyberPrivateKeyToEncryptedPublicKeyAddress(skHex) { if (!skHex || !skHex.trim()) { throw new Error('Kyber key is required'); } const hex = skHex.startsWith('0x') ? skHex.slice(2) : skHex; const bytes = hexToUint8Array(hex); const len = bytes.length; // Already a public key? Return as hex if (len === 800 || len === 1184 || len === 1568) { return toHexString(bytes); } // Map secret key lengths to parameter k const kByLen = { 1632: 2, 2400: 3, 3168: 4 }; const k = kByLen[len]; if (!k) { throw new Error(`Unsupported Kyber secret key length: ${len}`); } const ekOffset = 384 * k; // byte offset where ek starts const ekLen = 384 * k + 32; // length of ek const ek = bytes.slice(ekOffset, ekOffset + ekLen); return toHexString(ek); } function kyberPrivateKeyToPublicKeyAddress(skHex) { const ek = kyberPrivateKeyToEncryptedPublicKeyAddress(skHex); return generateAccountAddress(ek); } function generateWalletAddress(publicKey) { const uint8PublicKey = hexToUint8Array(publicKey); const hash = keccak_256$1(uint8PublicKey); const addrBuf = hash.slice(12); return '0x' + bytesToHex(addrBuf); } function mldsaPublicKeyToAddress(publicKey) { return generateWalletAddress(publicKey); } /** * Sign a message using MLDSA with a private key. * @param message - The message to sign * @param privateKey - The private key to sign with (hex string) * @param algorithm - The MLDSA algorithm to use ('ml_dsa44', 'ml_dsa65', or 'ml_dsa87') * @returns Promise<string> - The signature as a hex string */ async function signMessageMLDSA(message, privateKey, algorithm = 'ml_dsa87') { try { // Import the noble-post-quantum library // @ts-ignore - noble-post-quantum.js is a bundled JS file without TypeScript declarations const noblePQ = await Promise.resolve().then(function () { return noblePostQuantum$1; }); const algorithms = noblePQ.default || noblePQ; // Get the MLDSA algorithm const mldsa = algorithms[algorithm]; if (!mldsa) { throw new Error(`Unsupported MLDSA algorithm: ${algorithm}`); } // Convert hex private key to Uint8Array const privateKeyBytes = hexToUint8Array(privateKey); // Convert message to Uint8Array const messageBytes = new TextEncoder().encode(message); // Sign the message const signature = mldsa.sign(privateKeyBytes, messageBytes); // Convert signature to hex string return toHexString(signature); } catch (error) { console.error('Error signing message with MLDSA:', error); throw error; } } /** * Verify an MLDSA signature against a message and public key. * @param message - The original message that was signed * @param signature - The MLDSA signature (hex string) * @param publicKey - The public key to verify against (hex string) * @param algorithm - The MLDSA algorithm to use ('ml_dsa44', 'ml_dsa65', or 'ml_dsa87') * @returns Promise<boolean> - True if signature is valid, false otherwise */ async function verifyMLDSASignature(message, signature, publicKey, algorithm = 'ml_dsa87') { try { // Import the noble-post-quantum library // @ts-ignore - noble-post-quantum.js is a bundled JS file without TypeScript declarations const noblePQ = await Promise.resolve().then(function () { return noblePostQuantum$1; }); const algorithms = noblePQ.default || noblePQ; // Get the MLDSA algorithm const mldsa = algorithms[algorithm]; if (!mldsa) { throw new Error(`Unsupported MLDSA algorithm: ${algorithm}`); } // Convert hex strings to Uint8Arrays const publicKeyBytes = hexToUint8Array(publicKey); const signatureBytes = hexToUint8Array(signature); const messageBytes = new TextEncoder().encode(message); // Verify the signature return mldsa.verify(publicKeyBytes, messageBytes, signatureBytes); } catch (error) { console.error('Error verifying MLDSA signature:', error); return false; } } /** * Generate a new MLDSA key pair. * @param algorithm - The MLDSA algorithm to use ('ml_dsa44', 'ml_dsa65', or 'ml_dsa87') * @returns Promise<{publicKey: string, privateKey: string}> - The key pair as hex strings */ async function generateMLDSAKeyPair(algorithm = 'ml_dsa87') { try { // Import the noble-post-quantum library // @ts-ignore - noble-post-quantum.js is a bundled JS file without TypeScript declarations const noblePQ = await Promise.resolve().then(function () { return noblePostQuantum$1; }); const algorithms = noblePQ.default || noblePQ; // Get the MLDSA algorithm const mldsa = algorithms[algorithm]; if (!mldsa) { throw new Error(`Unsupported MLDSA algorithm: ${algorithm}`); } // Generate key pair const keyPair = mldsa.keygen(); // Convert to hex strings return { publicKey: toHexString(keyPair.publicKey), privateKey: toHexString(keyPair.secretKey) }; } catch (error) { console.error('Error generating MLDSA key pair:', error); throw error; } } /** * Derive or validate an MLDSA public key from a provided key hex. * - If the provided hex already looks like a public key for the given algorithm, it is returned (lowercase, no 0x). * - If it looks like a secret key for the given algorithm, deriving the public key is not supported by the * current bundled API. In this case, an error is thrown with guidance to persist the public key at keygen time. * * Note: MLDSA secret keys (Dilithium) in this bundle do not embed the public key; reconstructing requires internal * primitives that are not exported. Persist the `publicKey` from `generateMLDSAKeyPair` alongside the `privateKey`. */ async function mldsaPrivateKeyToPublicKey(keyHex, algorithm = 'ml_dsa87') { if (!keyHex || !keyHex.trim()) throw new Error('mldsaPrivateKeyToPublicKey: key is required'); const hex = keyHex.startsWith('0x') ? keyHex.slice(2) : keyHex; if (!/^[0-9a-fA-F]+$/.test(hex) || hex.length % 2 !== 0) { throw new Error('mldsaPrivateKeyToPublicKey: invalid hex'); } // Known MLDSA (Dilithium) key lengths (bytes) for common modes const PUBLIC_KEY_LENGTHS = { ml_dsa44: 1312, // Dilithium2 ml_dsa65: 1952, // Dilithium3 ml_dsa87: 2592 // Dilithium5 }; const SECRET_KEY_LENGTHS = { // Lengths reflect this bundle's encoding (TR_BYTES = 64), which differ from some specs ml_dsa44: 2560, // Dilithium2 ml_dsa65: 4032, // Dilithium3 ml_dsa87: 4896 // Dilithium5 }; const byteLen = hex.length / 2; if (byteLen === PUBLIC_KEY_LENGTHS[algorithm]) { return hex; } if (byteLen === SECRET_KEY_LENGTHS[algorithm]) { // Try deriving via non-standard helper exposed by bundled implementation // @ts-ignore - bundled JS without types const noblePQ = await Promise.resolve().then(function () { return noblePostQuantum$1; }); const algorithms = noblePQ.default || noblePQ; const mldsa = algorithms[algorithm]; if (!mldsa || typeof mldsa.derivePublicKey !== 'function') { throw new Error('mldsaPrivateKeyToPublicKey: derivation not available in bundled implementation'); } const sk = hexToUint8Array(hex); const pkBytes = mldsa.derivePublicKey(sk); const pkHex = toHexString(pkBytes); return pkHex; } throw new Error(`mldsaPrivateKeyToPublicKey: unexpected key length (${byteLen} bytes) for ${algorithm}. ` + 'Provide a valid MLDSA public key or the original keypair output.'); } function generateAccountAddress(publicKey) { try { const hash = sha3_512(new TextEncoder().encode(publicKey)); const hashHex = bytesToHex(hash); const accountAddress = `0x${hashHex.slice(-40)}`; return accountAddress; } catch (error) { console.error('Error generating account address:', error); return ''; } } // Local helpers for hex/bytes without Node Buffer dependency function hexToUint8Array(hexString) { const normalized = hexString.trim().toLowerCase(); if (!/^[0-9a-f]*$/.test(normalized) || normalized.length % 2 !== 0) { throw new Error('Invalid hexadecimal string'); } const result = new Uint8Array(normalized.length / 2); for (let i = 0; i < normalized.length; i += 2) { result[i / 2] = parseInt(normalized.substr(i, 2), 16); } return result; } function toHexString(byteArray) { return Array.from(byteArray) .map((b) => b.toString(16).padStart(2, '0')) .join(''); } /** * Represents a structured error returned from a JSON-RPC call. */ class RpcError extends Error { constructor(message, code, data) { super(`RPC Error: ${message} (code: ${code})`); this.name = message; this.code = code; this.data = data; } } /** * The Provider class is a low-level wrapper for making JSON-RPC requests to an NCOG chain node. * It handles request creation, error parsing, and provides convenience methods for all standard RPC calls. */ class Provider { /** * Register a request middleware function. Called before sending each request. */ useRequest(middleware) { this.requestMiddleware.push(middleware); } /** * Register a response middleware function. Called after receiving each response. */ useResponse(middleware) { this.responseMiddleware.push(middleware); } /** * @param url The URL of the JSON-RPC endpoint (e.g., "http://localhost:8545"). */ constructor(url) { this.idCounter = 1; this.requestMiddleware = []; this.responseMiddleware = []; if (url.includes('http')) { let leftPart = url.split('//')[1]; if (leftPart.startsWith('wsapi')) { url = url + '/api'; } } this.url = url; } /** * Performs a raw JSON-RPC request. This is the core private method used by all others. * @param method The RPC method name. * @param params An array of parameters for the RPC method. * @returns The result from the RPC call. * @throws {RpcError} if the RPC call returns a JSON-RPC error object. * @throws {Error} for network or other request-level errors. */ async rpc(method, params = []) { var _a, _b, _c; if (!this.url) { throw new Error('Provider URL is not set'); } let payload = { jsonrpc: '2.0', id: this.idCounter++, method, params }; // Apply request middleware for (const mw of this.requestMiddleware) { payload = await mw(payload); } try { const { data } = await axios.post(this.url, payload); let response = data; // Apply response middleware for (const mw of this.responseMiddleware) { response = await mw(response, payload); } if (response === null || response === void 0 ? void 0 : response.error) { throw new RpcError((_a = response === null || response === void 0 ? void 0 : response.error) === null || _a === void 0 ? void 0 : _a.message, (_b = response === null || response === void 0 ? void 0 : response.error) === null || _b === void 0 ? void 0 : _b.code, (_c = response === null || response === void 0 ? void 0 : response.error) === null || _c === void 0 ? void 0 : _c.data); } return normalizeResponse((response === null || response === void 0 ? void 0 : response.result) || response); } catch (error) { if (error instanceof AxiosError) { throw new Error(`RPC request failed for method "${method}": ${error.message}`); } throw error; } } /** * Performs a batch of JSON-RPC requests. Returns an array of results/errors in the same order. * @param calls Array of { method, params } objects. * @returns Array of results or errors (in order). */ async batchRpc(calls) { if (!this.url) { throw new Error('Provider URL is not set'); } let payloads = calls.map((call, i) => ({ jsonrpc: '2.0', id: this.idCounter + i, method: call.method, params: call.params || [] })); // Apply request middleware to each payload for (const mw of this.requestMiddleware) { payloads = await Promise.all(payloads.map(p => mw(p))); } try { const { data } = await axios.post(this.url, payloads); let results = Array.isArray(data) ? data : [data]; // Apply response middleware to each result for (const mw of this.responseMiddleware) { results = await Promise.all(results.map((r, i) => mw(r, payloads[i]))); } results.sort((a, b) => a.id - b.id); return results.map(res => { if (res.error) { return { error: res.error }; } return normalizeResponse(res.result || res); }); } catch (error) { return calls.map(() => ({ error: error.message || error })); } } /** * Provides a public way to make any RPC call, for methods not explicitly wrapped. * @param method The RPC method name. * @param params An array of parameters for the RPC method. */ async callRpc(method, params = []) { // Serialize all params for RPC const serializedParams = params.map(p => typeof p === 'object' && p !== null ? serializeForRpc(p) : p); return this.rpc(method, serializedParams); } // --- web3 --- /** * Returns the client version of the node. */ async clientVersion() { return this.rpc('web3_clientVersion'); } // --- net --- /** * Returns the current network ID. */ async netVersion() { return this.rpc('net_version'); } /** * Returns true if the client is actively listening for network connections. */ async listening() { return this.rpc('net_listening'); } /** * Returns the number of peers currently connected to the client. */ async peerCount() { return this.rpc('net_peerCount'); } // --- eth --- /** * Returns the current protocol version. */ async protocolVersion() { return this.rpc('eth_protocolVersion'); } /** * Returns an object with data about the sync status or `false` if not syncing. */ async syncing() { return this.rpc('eth_syncing'); } /** * Returns the coinbase address of the client. */ async coinbase() { return this.rpc('eth_coinbase'); } /** * Returns the number of hashes per second that the node is mining with. */ async hashrate() { return this.rpc('eth_hashrate'); } /** * Returns the current chain ID. */ async getChainId() { return await this.rpc('eth_chainId'); } /** * Returns the current price per gas in wei. */ async getGasPrice() { return this.rpc('eth_gasPrice'); } /** * Returns a list of accounts owned by the client. */ async accounts() { return this.rpc('eth_accounts'); } /** * Returns the number of the most recent block. */ async getBlockNumber() { return await this.rpc('eth_blockNumber'); } /** * Returns the balance of an account in wei. * @param address The address to get the balance of. * @param tag The block tag (e.g., "latest", "earliest", "pending", or a block number). Defaults to "latest". */ async getBalance(address, tag = 'latest') { const balance = await this.rpc('eth_getBalance', [address, tag]); const convertedBalance = weiToNec(balance); return isNaN(Number(convertedBalance)) ? 0 : Number(convertedBalance); } /** * Returns the value from a storage position at a given address. * @param address Address of the storage. * @param position Hex of the position in storage. * @param tag Block tag. Defaults to "latest". */ async getStorageAt(address, position, tag = 'latest') { return this.rpc('eth_getStorageAt', [address, position, tag]); } /** * Returns the number of transactions sent from an address. * @param address The address. * @param tag The block tag. Defaults to "latest". */ async getTransactionCount(address, tag = 'latest') { return await this.rpc('eth_getTransactionCount', [address, tag]); } /** * Returns the number of transactions in a block from a block matching the given block number. * @param tag The block tag. */ async getBlockTransactionCountByNumber(tag) { return await this.rpc('eth_getBlockTransactionCountByNumber', [tag]); } /** * Returns the code at a given address. * @param address The address. * @param tag The block tag. Defaults to "latest". */ async getCode(address, tag = 'latest') { return this.rpc('eth_getCode', [address, tag]); } /** * Returns a block matching the given block number. * @param tag The block tag or number. * @param full If true, returns full transaction objects; otherwise, only transaction hashes. */ async getBlockByNumber(tag, full = false) { return this.rpc('eth_getBlockByNumber', [tag, full]); } /** * Returns a block matching the given block hash. * @param hash The hash of the block. * @param full If true, returns full transaction objects; otherwise, only transaction hashes. */ async getBlockByHash(hash, full = false) { return this.rpc('eth_getBlockByHash', [hash, full]); } /** * Calculates a signature for data, using a specific account. * The account must be unlocked on the node. * @param address The address to sign with. * @param data The data to sign. */ async sign(address, data) { return this.rpc('eth_sign', [address, data]); } /** * Asks the remote node to sign a transaction with an unlocked account. * @param txObj The transaction object to sign. * @returns An object containing the raw signed transaction and the decoded transaction fields. */ async signTransaction(txObj) { const rpcParams = serializeForRpc(txObj); return this.rpc('eth_signTransaction', [rpcParams]); } /** * Submits a transaction to be signed and broadcasted by the remote node. * The `from` account must be unlocked. * @param obj The transaction object. */ async sendTransaction(obj) { const rpcParams = serializeForRpc(obj); return this.rpc('eth_sendTransaction', [rpcParams]); } /** * Submits a pre-signed transaction to the network. * @param signedTx The hex-encoded signed transaction. * @returns The transaction hash. */ async sendRawTransaction(signedTx) { return this.rpc('eth_sendRawTransaction', [signedTx]); } /** * Executes a message call immediately without creating a transaction on the block-chain (read-only). * @param tx The transaction call object. * @param tag The block tag. Defaults to "latest". */ async call(tx, tag = 'latest') { const rpcTx = serializeForRpc(tx); return this.rpc('eth_call', [rpcTx, tag]); } /** * Estimates the gas necessary to execute a specific transaction. * @param obj The transaction object. */ async estimateGas(obj) { const rpcObj = serializeForRpc(obj); return await this.rpc('eth_estimateGas', [rpcObj]); } /** * Returns a transaction by its hash. * @param hash The hash of the transaction. */ async getTransactionByHash(hash) { return this.rpc('eth_getTransactionByHash', [hash]); } /** * Returns the receipt of a transaction by its hash. * @param hash The hash of the transaction. */ async getTransactionReceipt(hash) { return this.rpc('eth_getTransactionReceipt', [hash]); } /** * Returns an array of all logs matching a given filter object. * @param filter The filter object. */ async getLogs(filter) { return this.rpc('eth_getLogs', [filter]); } // --- Mining --- /** * Used for submitting a proof-of-work solution. */ async submitWork(nonce, powHash, mixDigest) { return this.rpc('eth_submitWork', [nonce, powHash, mixDigest]); } /** * Used for obtaining a proof-of-work problem. */ async getWork() { return this.rpc('eth_getWork'); } // --- personal --- /** * Creates a new account in the node's keystore. * @param password The password to protect the account with. */ async newAccount(password) { return this.rpc('personal_newAccount', [password]); } /** * Imports an unencrypted private key into the node's keystore. * @param privateKey The raw private key. * @param password The password to encrypt the key with. */ async importRawKey(privateKey, password) { return this.rpc('personal_importRawKey', [privateKey, password]); } /** * Signs data with a specific account. * The account must be unlocked on the node. * @param data The data to sign. * @param address The address to sign with. * @param password The password for the account. */ async personalSign(data, address, password) { return this.rpc('personal_sign', [data, address, password]); } /** * Recovers the address that signed a piece of data. * @param data The original data. * @param signature The signature. */ async ecRecover(data, signature) { return this.rpc('personal_ecRecover', [data, signature]); } /** * Unlocks a specified account for a given duration. * @param address The address to unlock. * @param password The account's password. * @param duration The duration in seconds to keep the account unlocked. Defaults to 300. */ async unlockAccount(address, password, duration) { return this.rpc('personal_unlockAccount', [address, password, duration]); } /** * Locks a specified account. * @param address The address to lock. */ async lockAccount(address) { return this.rpc('personal_lockAccount', [address]); } /** * Sends a transaction from an account in the node's keystore. * @param tx The transaction object. * @param password The password for the `from` account. */ async sendPersonalTransaction(tx, password) { return this.rpc('personal_sendTransaction', [tx, password]); } /** * Resolves an ENS name to an Ethereum address using the ENS registry contract. * @param ensName The ENS name to resolve (e.g., 'vitalik.eth'). * @param registryAddress The ENS registry contract address (optional, defaults to mainnet address). * @returns The resolved Ethereum address, or null if not found. */ async resolveEnsName(ensName, registryAddress = '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e') { try { const { namehash } = require('ethers'); const node = namehash(ensName); // ENS registry ABI: function resolver(bytes32 node) external view returns (address) const data = '0x0178b8bf' + node.replace(/^0x/, ''); // resolver(bytes32) selector + node const callObj = { to: registryAddress, data }; const resolverAddr = await this.call(callObj); if (!resolverAddr || resolverAddr === '0x' || /^0x0+$/.test(resolverAddr)) return null; // ENS resolver ABI: function addr(bytes32 node) external view returns (address) const addrSelector = '0x3b3b57de'; const data2 = addrSelector + node.replace(/^0x/, ''); const callObj2 = { to: resolverAddr, data: data2 }; const address = await this.call(callObj2); if (!address || address === '0x' || /^0x0+$/.test(address)) return null; return address; } catch (err) { return null; } } } var provider = /*#__PURE__*/Object.freeze({ __proto__: null, Provider: Provider, RpcError: RpcError }); // Unified MLKEM interface for both Node.js and browser environments class WasmError extends Error { constructor(message, context) { super(`WASM Error: ${message}`); this.name = 'WasmError'; this.context = context; } } // Environment detection const isNode$1 = typeof process !== 'undefined' && process.versions && process.versions.node; const isBrowser$1 = typeof window !== 'undefined' && typeof document !== 'undefined'; const isReactNative$1 = typeof navigator !== 'undefined' && navigator.product === 'ReactNative'; /** * Load and initialize the MLKEM Go WebAssembly module. * Automatically detects environment and uses appropriate loader. */ async function loadWasm$2() { if (isNode$1) { // Use Node.js loader const { loadWasm: loadNodeWasm } = await Promise.resolve().then(function () { return mlkemNode; }); return await loadNodeWasm(); } else if (isBrowser$1 || isReactNative$1) { // Use browser loader for browser and React Native const { loadWasm: loadBrowserWasm } = await Promise.resolve().then(function () { return mlkemBrowser; }); return await loadBrowserWasm(); } else { throw new WasmError('Unsupported environment. This package requires Node.js, browser, or React Native.', { env: { isNode: isNode$1, isBrowser: isBrowser$1, isReactNative: isReactNative$1 } }); } } /** * Load WASM from buffer (useful for bundlers that inline WASM) */ async function loadWasmFromBuffer$1(wasmBuffer) { if (isNode$1) { throw new WasmError('loadWasmFromBuffer is not supported in Node.js environment. Use loadWasm() instead.', { env: 'node' }); } else if (isBrowser$1 || isReactNative$1) { const { loadWasmFromBuffer: loadBrowserWasmFromBuffer } = await Promise.resolve().then(function () { return mlkemBrowser; }); return await loadBrowserWasmFromBuffer(wasmBuffer); } else { throw new WasmError('Unsupported environment. This package requires Node.js, browser, or React Native.', { en