necjs
Version:
NECJS SDK for NCOG Earth Chain RPC
1,332 lines (1,323 loc) • 2.38 MB
JavaScript
'use strict';
var axios = require('axios');
var fs = require('fs');
require('path');
var node_crypto = require('node:crypto');
var require$$0$3 = require('events');
var require$$1$1 = require('https');
var require$$2 = require('http');
var require$$3 = require('net');
var require$$4 = require('tls');
var require$$1 = require('crypto');
var require$$0$2 = require('stream');
var require$$7 = require('url');
var require$$0 = require('zlib');
var require$$0$1 = require('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 axios.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 requir