@neuraiproject/neurai-key
Version:
Generate Neurai addresses from mnemonic code. BIP32, BIP39, BIP44
930 lines (921 loc) • 35.3 kB
JavaScript
import { validateMnemonic, generateMnemonic as generateMnemonic$1, entropyToMnemonic as entropyToMnemonic$1, mnemonicToSeedSync } from '@scure/bip39';
import { wordlist } from '@scure/bip39/wordlists/czech.js';
import { wordlist as wordlist$1 } from '@scure/bip39/wordlists/english.js';
import { wordlist as wordlist$3 } from '@scure/bip39/wordlists/french.js';
import { wordlist as wordlist$4 } from '@scure/bip39/wordlists/italian.js';
import { wordlist as wordlist$5 } from '@scure/bip39/wordlists/japanese.js';
import { wordlist as wordlist$6 } from '@scure/bip39/wordlists/korean.js';
import { wordlist as wordlist$7 } from '@scure/bip39/wordlists/portuguese.js';
import { wordlist as wordlist$2 } from '@scure/bip39/wordlists/spanish.js';
import { wordlist as wordlist$8 } from '@scure/bip39/wordlists/simplified-chinese.js';
import { ripemd160 } from '@noble/hashes/legacy.js';
import { hmac } from '@noble/hashes/hmac.js';
import { sha512, sha256 } from '@noble/hashes/sha2.js';
import { utf8ToBytes, concatBytes as concatBytes$1, bytesToHex as bytesToHex$1, hexToBytes as hexToBytes$1 } from '@noble/hashes/utils.js';
import { secp256k1 } from '@noble/curves/secp256k1.js';
import { bech32m } from 'bech32';
import { ml_dsa44 } from '@noble/post-quantum/ml-dsa.js';
/*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) */
function isBytes(a) {
// Plain `instanceof Uint8Array` is too strict for some Buffer / proxy / cross-realm cases. The
// fallback still requires a real ArrayBuffer view, so plain JSON-deserialized
// `{ constructor: ... }` spoofing is rejected. `BYTES_PER_ELEMENT === 1` keeps the
// fallback on byte-oriented views.
return (a instanceof Uint8Array ||
(ArrayBuffer.isView(a) &&
a.constructor.name === 'Uint8Array' &&
'BYTES_PER_ELEMENT' in a &&
a.BYTES_PER_ELEMENT === 1));
}
function isArrayOf(isString, arr) {
if (!Array.isArray(arr))
return false;
if (arr.length === 0)
return true;
if (isString) {
return arr.every((item) => typeof item === 'string');
}
else {
return arr.every((item) => Number.isSafeInteger(item));
}
}
function astr(label, input) {
if (typeof input !== 'string')
throw new TypeError(`${label}: string expected`);
return true;
}
function anumber(n) {
if (typeof n !== 'number')
throw new TypeError(`number expected, got ${typeof n}`);
if (!Number.isSafeInteger(n))
throw new RangeError(`invalid integer: ${n}`);
}
function aArr(input) {
if (!Array.isArray(input))
throw new TypeError('array expected');
}
function astrArr(label, input) {
if (!isArrayOf(true, input))
throw new TypeError(`${label}: array of strings expected`);
}
function anumArr(label, input) {
if (!isArrayOf(false, input))
throw new TypeError(`${label}: array of numbers expected`);
}
/**
* @__NO_SIDE_EFFECTS__
*/
function chain(...args) {
const id = (a) => a;
// Wrap call in closure so JIT can inline calls
const wrap = (a, b) => (c) => a(b(c));
// Construct chain of args[-1].encode(args[-2].encode([...]))
const encode = args.map((x) => x.encode).reduceRight(wrap, id);
// Construct chain of args[0].decode(args[1].decode(...))
const decode = args.map((x) => x.decode).reduce(wrap, id);
return { encode, decode };
}
/**
* Encodes integer radix representation to array of strings using alphabet and back.
* Could also be array of strings.
* @__NO_SIDE_EFFECTS__
*/
function alphabet(letters) {
// mapping 1 to "b"
const lettersA = typeof letters === 'string' ? letters.split('') : letters;
const len = lettersA.length;
astrArr('alphabet', lettersA);
// mapping "b" to 1
const indexes = new Map(lettersA.map((l, i) => [l, i]));
return {
encode: (digits) => {
aArr(digits);
return digits.map((i) => {
if (!Number.isSafeInteger(i) || i < 0 || i >= len)
throw new Error(`alphabet.encode: digit index outside alphabet "${i}". Allowed: ${letters}`);
return lettersA[i];
});
},
decode: (input) => {
aArr(input);
return input.map((letter) => {
astr('alphabet.decode', letter);
const i = indexes.get(letter);
if (i === undefined)
throw new Error(`Unknown letter: "${letter}". Allowed: ${letters}`);
return i;
});
},
};
}
/**
* @__NO_SIDE_EFFECTS__
*/
function join(separator = '') {
astr('join', separator);
// join('') is only lossless when each chunk is already unambiguous, such as single-symbol alphabets.
// Multi-character tokens need a separator that cannot appear inside the chunks.
return {
encode: (from) => {
astrArr('join.decode', from);
return from.join(separator);
},
decode: (to) => {
astr('join.decode', to);
return to.split(separator);
},
};
}
/**
* Slow: O(n^2) time complexity
*/
function convertRadix(data, from, to) {
// base 1 is impossible
if (from < 2)
throw new RangeError(`convertRadix: invalid from=${from}, base cannot be less than 2`);
if (to < 2)
throw new RangeError(`convertRadix: invalid to=${to}, base cannot be less than 2`);
aArr(data);
if (!data.length)
return [];
let pos = 0;
const res = [];
const digits = Array.from(data, (d) => {
anumber(d);
if (d < 0 || d >= from)
throw new Error(`invalid integer: ${d}`);
return d;
});
const dlen = digits.length;
while (true) {
let carry = 0;
let done = true;
for (let i = pos; i < dlen; i++) {
const digit = digits[i];
const fromCarry = from * carry;
const digitBase = fromCarry + digit;
if (!Number.isSafeInteger(digitBase) ||
fromCarry / from !== carry ||
digitBase - digit !== fromCarry) {
throw new Error('convertRadix: carry overflow');
}
const div = digitBase / to;
carry = digitBase % to;
const rounded = Math.floor(div);
digits[i] = rounded;
if (!Number.isSafeInteger(rounded) || rounded * to + carry !== digitBase)
throw new Error('convertRadix: carry overflow');
if (!done)
continue;
else if (!rounded)
pos = i;
else
done = false;
}
res.push(carry);
if (done)
break;
}
// Preserve explicit leading zero digits so callers like base58 keep zero-prefix semantics.
for (let i = 0; i < data.length - 1 && data[i] === 0; i++)
res.push(0);
return res.reverse();
}
/**
* @__NO_SIDE_EFFECTS__
*/
function radix(num) {
anumber(num);
const _256 = 2 ** 8;
// Base-range and carry-overflow checks live in convertRadix so encode/decode reject unsupported bases symmetrically.
return {
encode: (bytes) => {
if (!isBytes(bytes))
throw new TypeError('radix.encode input should be Uint8Array');
return convertRadix(Array.from(bytes), _256, num);
},
decode: (digits) => {
anumArr('radix.decode', digits);
return Uint8Array.from(convertRadix(digits, num, _256));
},
};
}
// base58 code
// -----------
const genBase58 = /* @__NO_SIDE_EFFECTS__ */ (abc) => chain(radix(58), alphabet(abc), join(''));
/**
* base58: base64 without ambigous characters +, /, 0, O, I, l.
* Quadratic (O(n^2)) - so, can't be used on large inputs.
* @example
* ```js
* const text = base58.encode(Uint8Array.from([0, 1, 2]));
* base58.decode(text);
* // => Uint8Array.from([0, 1, 2])
* ```
*/
const base58 = /* @__PURE__ */ Object.freeze(genBase58('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'));
const HARDENED_OFFSET = 0x80000000;
const SECP256K1_ORDER = BigInt("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141");
function bytesToHex(bytes) {
return bytesToHex$1(bytes);
}
function concatBytes(...arrays) {
return concatBytes$1(...arrays);
}
function hexToBytes(hex) {
return hexToBytes$1(hex);
}
function ensureBytes(input) {
return typeof input === "string" ? hexToBytes(input) : Uint8Array.from(input);
}
function numberToBytesBE(value, length) {
const bytes = new Uint8Array(length);
let current = value;
for (let index = length - 1; index >= 0; index -= 1) {
bytes[index] = Number(current & 0xffn);
current >>= 8n;
}
return bytes;
}
function bytesToNumberBE(bytes) {
let value = 0n;
for (const byte of bytes) {
value = (value << 8n) + BigInt(byte);
}
return value;
}
function uint32ToBytesBE(value) {
const bytes = new Uint8Array(4);
const view = new DataView(bytes.buffer);
view.setUint32(0, value, false);
return bytes;
}
function hash160(data) {
return ripemd160(sha256(data));
}
function sha256Hash(data) {
return sha256(data);
}
function taggedHash(tag, data) {
const tagHash = sha256(utf8ToBytes(tag));
return sha256(concatBytes(tagHash, tagHash, data));
}
function doubleSha256(data) {
return sha256(sha256(data));
}
function hmacSha512(key, data) {
return hmac(sha512, key, data);
}
function base58CheckEncode(payload) {
const checksum = doubleSha256(payload).slice(0, 4);
return base58.encode(concatBytes(payload, checksum));
}
function base58CheckDecode(value) {
const decoded = Uint8Array.from(base58.decode(value));
if (decoded.length < 5) {
throw new Error("Invalid Base58Check payload");
}
const payload = decoded.slice(0, -4);
const checksum = decoded.slice(-4);
const expected = doubleSha256(payload).slice(0, 4);
for (let index = 0; index < 4; index += 1) {
if (checksum[index] !== expected[index]) {
throw new Error("Invalid Base58Check checksum");
}
}
return payload;
}
function bigIntMod(value, modulo) {
const remainder = value % modulo;
return remainder >= 0n ? remainder : remainder + modulo;
}
function isValidPrivateKey(privateKey) {
if (privateKey.length !== 32) {
return false;
}
const value = bytesToNumberBE(privateKey);
return value > 0n && value < SECP256K1_ORDER;
}
function mnemonicToSeedBytes(mnemonicToSeedSync, mnemonic, passphrase) {
return Uint8Array.from(mnemonicToSeedSync(mnemonic, passphrase));
}
const BITCOIN_SEED_KEY = utf8ToBytes("Bitcoin seed");
const AUTHSCRIPT_TAG = "NeuraiAuthScript";
const AUTHSCRIPT_VERSION = 0x01;
const NOAUTH_TYPE = 0x00;
const PQ_AUTH_TYPE = 0x01;
const LEGACY_AUTH_TYPE = 0x02;
const PQ_PUBLIC_KEY_HEADER$1 = Uint8Array.from([0x05]);
const DEFAULT_WITNESS_SCRIPT = Uint8Array.from([0x51]);
function encodeWIF(privateKey, version, compressed = true) {
const payload = compressed
? concatBytes(Uint8Array.from([version]), privateKey, Uint8Array.from([0x01]))
: concatBytes(Uint8Array.from([version]), privateKey);
return base58CheckEncode(payload);
}
function decodeWIF(wif) {
const payload = base58CheckDecode(wif);
if (payload.length !== 33 && payload.length !== 34) {
throw new Error("Invalid WIF length");
}
const version = payload[0];
const compressed = payload.length === 34;
if (compressed && payload[payload.length - 1] !== 0x01) {
throw new Error("Invalid compressed WIF payload");
}
return {
version,
privateKey: payload.slice(1, 33),
compressed,
};
}
function getCompressedPublicKey(privateKey) {
return secp256k1.getPublicKey(privateKey, true);
}
function publicKeyToAddressBytes(publicKey, versions) {
return base58CheckEncode(concatBytes(Uint8Array.from([versions.public]), hash160(publicKey)));
}
function privateKeyToAddressObject(privateKey, versions, path) {
const publicKey = getCompressedPublicKey(privateKey);
return {
address: publicKeyToAddressBytes(publicKey, versions),
path,
publicKey: bytesToHex(publicKey),
privateKey: bytesToHex(privateKey),
WIF: encodeWIF(privateKey, versions.private),
};
}
function addressObjectFromWIF(wif, versions) {
const decoded = decodeWIF(wif);
const publicKey = decoded.compressed
? secp256k1.getPublicKey(decoded.privateKey, true)
: secp256k1.getPublicKey(decoded.privateKey, false);
return {
address: publicKeyToAddressBytes(publicKey, versions),
privateKey: bytesToHex(decoded.privateKey),
WIF: encodeWIF(decoded.privateKey, versions.private, decoded.compressed),
};
}
function publicKeyHexFromWIF(wif, compressed = true) {
const decoded = decodeWIF(wif);
return bytesToHex(secp256k1.getPublicKey(decoded.privateKey, compressed && decoded.compressed));
}
function bech32mEncode(hrp, witnessVersion, hash) {
return bech32m.encode(hrp, [witnessVersion, ...bech32m.toWords(hash)]);
}
function normalizeWitnessScript(input) {
return input ? ensureBytes(input) : Uint8Array.from(DEFAULT_WITNESS_SCRIPT);
}
function buildAuthDescriptor(authType, publicKey) {
if (authType === NOAUTH_TYPE) {
return Uint8Array.from([NOAUTH_TYPE]);
}
if (!publicKey) {
throw new Error(`Auth type 0x${authType.toString(16).padStart(2, "0")} requires a public key`);
}
if (authType === PQ_AUTH_TYPE) {
return concatBytes(Uint8Array.from([PQ_AUTH_TYPE]), hash160(concatBytes(PQ_PUBLIC_KEY_HEADER$1, publicKey)));
}
if (authType === LEGACY_AUTH_TYPE) {
return concatBytes(Uint8Array.from([LEGACY_AUTH_TYPE]), hash160(publicKey));
}
throw new Error(`Unsupported authType: 0x${String(authType).padStart(2, "0")}`);
}
function pqPublicKeyToAuthDescriptor(publicKey) {
return buildAuthDescriptor(PQ_AUTH_TYPE, publicKey);
}
function pqPublicKeyToCommitment(publicKey, options = {}) {
return pqPublicKeyToCommitmentParts(publicKey, options).commitment;
}
function authScriptCommitmentParts(authType, publicKey, options = {}) {
const witnessScript = normalizeWitnessScript(options.witnessScript);
const authDescriptor = buildAuthDescriptor(authType, publicKey);
const witnessScriptHash = sha256Hash(witnessScript);
const commitment = taggedHash(AUTHSCRIPT_TAG, concatBytes(Uint8Array.from([AUTHSCRIPT_VERSION]), authDescriptor, witnessScriptHash));
return {
authDescriptor,
authType,
commitment,
witnessScript,
};
}
function pqPublicKeyToCommitmentParts(publicKey, options = {}) {
return authScriptCommitmentParts(PQ_AUTH_TYPE, publicKey, options);
}
function pqPublicKeyToAddressBytes(publicKey, network, options = {}) {
return bech32mEncode(network.hrp, network.witnessVersion, pqPublicKeyToCommitment(publicKey, options));
}
function noAuthToAddressBytes(network, options = {}) {
return bech32mEncode(network.hrp, network.witnessVersion, authScriptCommitmentParts(NOAUTH_TYPE, null, options).commitment);
}
function legacyAuthScriptToAddressBytes(publicKey, network, options = {}) {
return bech32mEncode(network.hrp, network.witnessVersion, authScriptCommitmentParts(LEGACY_AUTH_TYPE, publicKey, options).commitment);
}
function normalizePublicKey(input) {
return ensureBytes(input);
}
function ensureValidTweak(tweak) {
const tweakValue = bytesToNumberBE(tweak);
if (tweakValue === 0n || tweakValue >= SECP256K1_ORDER) {
throw new Error("Invalid BIP32 tweak");
}
return tweakValue;
}
function serializeExtendedKey(version, depth, parentFingerprint, index, chainCode, keyData) {
return concatBytes(uint32ToBytesBE(version), Uint8Array.from([depth]), uint32ToBytesBE(parentFingerprint), uint32ToBytesBE(index), chainCode, keyData);
}
class HDKey {
constructor(versions, chainCode, publicKey, privateKey, depth = 0, index = 0, parentFingerprint = 0) {
this.versions = versions;
this.depth = depth;
this.index = index;
this.chainCode = chainCode;
this.parentFingerprint = parentFingerprint;
this.privateKey = privateKey;
this.publicKey = publicKey;
}
static fromMasterSeed(seed, versions) {
const I = hmacSha512(BITCOIN_SEED_KEY, seed);
const IL = I.slice(0, 32);
const IR = I.slice(32);
if (!isValidPrivateKey(IL)) {
throw new Error("Invalid master seed");
}
const publicKey = secp256k1.getPublicKey(IL, true);
return new HDKey(versions, IR, publicKey, IL);
}
get fingerprint() {
return new DataView(hash160(this.publicKey).buffer, hash160(this.publicKey).byteOffset, 4).getUint32(0, false);
}
get privateExtendedKey() {
if (!this.privateKey) {
return null;
}
const keyData = concatBytes(Uint8Array.from([0x00]), this.privateKey);
return base58CheckEncode(serializeExtendedKey(this.versions.private, this.depth, this.parentFingerprint, this.index, this.chainCode, keyData));
}
get publicExtendedKey() {
return base58CheckEncode(serializeExtendedKey(this.versions.public, this.depth, this.parentFingerprint, this.index, this.chainCode, this.publicKey));
}
derive(path) {
if (path === "m" || path === "M" || path === "m'" || path === "M'") {
return this;
}
const entries = path.split("/");
let current = this;
entries.forEach((entry, index) => {
if (index === 0) {
if (!/^[mM]{1}/.test(entry)) {
throw new Error('Path must start with "m" or "M"');
}
return;
}
const hardened = entry.endsWith("'");
const childIndex = Number.parseInt(entry, 10);
if (!Number.isFinite(childIndex) || childIndex >= HARDENED_OFFSET) {
throw new Error("Invalid index");
}
current = current.deriveChild(hardened ? childIndex + HARDENED_OFFSET : childIndex);
});
return current;
}
deriveChild(index) {
const hardened = index >= HARDENED_OFFSET;
const indexBytes = uint32ToBytesBE(index);
const data = hardened
? (() => {
if (!this.privateKey) {
throw new Error("Could not derive hardened child key");
}
return concatBytes(Uint8Array.from([0x00]), this.privateKey, indexBytes);
})()
: concatBytes(this.publicKey, indexBytes);
const I = hmacSha512(this.chainCode, data);
const IL = I.slice(0, 32);
const IR = I.slice(32);
let tweak;
try {
tweak = ensureValidTweak(IL);
}
catch {
return this.deriveChild(index + 1);
}
if (this.privateKey) {
const childKey = bigIntMod(bytesToNumberBE(this.privateKey) + tweak, SECP256K1_ORDER);
if (childKey === 0n) {
return this.deriveChild(index + 1);
}
const privateKey = numberToBytesBE(childKey, 32);
const publicKey = secp256k1.getPublicKey(privateKey, true);
return new HDKey(this.versions, IR, publicKey, privateKey, this.depth + 1, index, this.fingerprint);
}
const tweakPoint = secp256k1.Point.BASE.multiply(tweak);
const parentPoint = secp256k1.Point.fromHex(bytesToHex(this.publicKey));
const childPoint = tweakPoint.add(parentPoint);
if (childPoint.equals(secp256k1.Point.ZERO)) {
return this.deriveChild(index + 1);
}
return new HDKey(this.versions, IR, childPoint.toBytes(true), undefined, this.depth + 1, index, this.fingerprint);
}
}
const PQ_SEED_KEY = utf8ToBytes("Neurai PQ seed");
const PQ_PUBLIC_KEY_HEADER = 0x05;
// 74-byte layout, padded to match BIP32 xprv so base58check yields "xpqp..."/"tpqp...":
// depth(1) + fingerprint(4) + child(4) + chaincode(32) + padding(1=0x00) + pq_seed(32)
const BIP32_PQ_EXTKEY_SIZE = 74;
class PQHDKey {
constructor(depth, index, parentFingerprint, chainCode, pqSeed) {
this.depth = depth;
this.index = index;
this.parentFingerprint = parentFingerprint;
this.chainCode = chainCode;
this.pqSeed = pqSeed;
}
static fromMasterSeed(seed) {
const I = hmacSha512(PQ_SEED_KEY, seed);
return new PQHDKey(0, 0, new Uint8Array(4), I.slice(32, 64), I.slice(0, 32));
}
ensureKeypair() {
if (!this._publicKey || !this._secretKey) {
const { publicKey, secretKey } = ml_dsa44.keygen(this.pqSeed);
this._publicKey = publicKey;
this._secretKey = secretKey;
}
}
get publicKey() {
this.ensureKeypair();
return this._publicKey;
}
get secretKey() {
this.ensureKeypair();
return this._secretKey;
}
get fingerprint() {
return hash160(concatBytes(Uint8Array.from([PQ_PUBLIC_KEY_HEADER]), this.publicKey)).slice(0, 4);
}
deriveChild(index) {
if ((index & HARDENED_OFFSET) === 0) {
throw new Error("PQ-HD (NIP-022) requires hardened derivation at every level");
}
const data = concatBytes(Uint8Array.from([0x00]), this.pqSeed, uint32ToBytesBE(index >>> 0));
const I = hmacSha512(this.chainCode, data);
return new PQHDKey(this.depth + 1, index >>> 0, this.fingerprint, I.slice(32, 64), I.slice(0, 32));
}
encode() {
const out = new Uint8Array(BIP32_PQ_EXTKEY_SIZE);
out[0] = this.depth & 0xff;
out.set(this.parentFingerprint, 1);
out[5] = (this.index >>> 24) & 0xff;
out[6] = (this.index >>> 16) & 0xff;
out[7] = (this.index >>> 8) & 0xff;
out[8] = this.index & 0xff;
out.set(this.chainCode, 9);
out[41] = 0x00; // padding byte (aligns layout with BIP32 xprv)
out.set(this.pqSeed, 42);
return out;
}
encodeBase58Check(version) {
const versionBytes = Uint8Array.from([
(version >>> 24) & 0xff,
(version >>> 16) & 0xff,
(version >>> 8) & 0xff,
version & 0xff,
]);
return base58CheckEncode(concatBytes(versionBytes, this.encode()));
}
static decode(raw, parentFingerprint) {
if (raw.length !== BIP32_PQ_EXTKEY_SIZE) {
throw new Error(`PQ extended key payload must be ${BIP32_PQ_EXTKEY_SIZE} bytes`);
}
if (raw[41] !== 0x00) {
throw new Error("PQ extended key padding byte (offset 41) must be 0x00");
}
const depth = raw[0];
const fingerprint = parentFingerprint ?? raw.slice(1, 5);
const index = ((raw[5] << 24) | (raw[6] << 16) | (raw[7] << 8) | raw[8]) >>> 0;
const chainCode = raw.slice(9, 41);
const pqSeed = raw.slice(42, 74);
return new PQHDKey(depth, index, Uint8Array.from(fingerprint.slice(0, 4)), chainCode, pqSeed);
}
static decodeBase58Check(extKey, expectedVersion) {
const payload = base58CheckDecode(extKey);
if (payload.length !== 4 + BIP32_PQ_EXTKEY_SIZE) {
throw new Error("Invalid PQ extended key length");
}
const version = ((payload[0] << 24) | (payload[1] << 16) | (payload[2] << 8) | payload[3]) >>> 0;
if (version !== (expectedVersion >>> 0)) {
throw new Error(`PQ extended key version mismatch (expected 0x${expectedVersion.toString(16)}, got 0x${version.toString(16)})`);
}
return PQHDKey.decode(payload.slice(4));
}
derive(path) {
const entries = path.split("/");
const root = entries[0];
if (root !== "m" && root !== "M" && root !== "m_pq" && root !== "M_pq") {
throw new Error('PQ path must start with "m_pq" (or "m")');
}
if (entries.length === 1) {
return this;
}
let current = this;
for (let i = 1; i < entries.length; i += 1) {
const entry = entries[i];
const hardened = entry.endsWith("'");
if (!hardened) {
throw new Error(`PQ-HD path requires hardened indices (got "${entry}")`);
}
const raw = Number.parseInt(entry.slice(0, -1), 10);
if (!Number.isFinite(raw) || raw < 0 || raw >= HARDENED_OFFSET) {
throw new Error(`Invalid PQ-HD index "${entry}"`);
}
current = current.deriveChild(raw + HARDENED_OFFSET);
}
return current;
}
}
const currentNetworks = {
xna: {
versions: {
bip32: { private: 76066276, public: 76067358 },
bip44: 1900,
private: 128,
public: 53,
scripthash: 117,
},
},
"xna-test": {
versions: {
bip32: { private: 70615956, public: 70617039 },
bip44: 1,
private: 239,
public: 127,
scripthash: 196,
},
},
"xna-legacy": {
versions: {
bip32: { private: 76066276, public: 76067358 },
bip44: 0,
private: 128,
public: 53,
scripthash: 117,
},
},
"xna-legacy-test": {
versions: {
bip32: { private: 70615956, public: 70617039 },
bip44: 1,
private: 239,
public: 127,
scripthash: 196,
},
},
};
const pqNetworks = {
"xna-pq": {
hrp: "nq",
witnessVersion: 1,
purpose: 100,
coinType: 1900,
changeIndex: 0,
pqExtPrivVersion: 0x0488ac24, // "xpqp..." prefix, matches Neurai node chainparams.cpp
},
"xna-pq-test": {
hrp: "tnq",
witnessVersion: 1,
purpose: 100,
coinType: 1,
changeIndex: 0,
pqExtPrivVersion: 0x043581d5, // "tpqp..." prefix, matches Neurai node chainparams.cpp
},
};
function getNetwork(name) {
const network = currentNetworks[name];
if (!network) {
throw new Error(`network must be of value ${Object.keys(currentNetworks).toString()}`);
}
return network.versions;
}
function getPQNetwork(name) {
const network = pqNetworks[name];
if (!network) {
throw new Error("PQ network must be 'xna-pq' or 'xna-pq-test'");
}
return network;
}
const mnemonicWordlists = [
wordlist,
wordlist$1,
wordlist$2,
wordlist$3,
wordlist$4,
wordlist$5,
wordlist$6,
wordlist$7,
wordlist$8,
];
function getCoinType(network) {
return getNetwork(network).bip44;
}
function getAddressPair(network, mnemonic, account, position, passphrase = "") {
const hdKey = getHDKey(network, mnemonic, passphrase);
const coinType = getCoinType(network);
const externalPath = `m/44'/${coinType}'/${account}'/0/${position}`;
const internalPath = `m/44'/${coinType}'/${account}'/1/${position}`;
return {
internal: getAddressByPath(network, hdKey, internalPath),
external: getAddressByPath(network, hdKey, externalPath),
position,
};
}
function getHDKey(network, mnemonic, passphrase = "") {
const chain = getNetwork(network);
const seed = mnemonicToSeedBytes(mnemonicToSeedSync, mnemonic, passphrase);
return HDKey.fromMasterSeed(seed, chain.bip32);
}
function getAddressByPath(network, hdKey, path) {
const chain = getNetwork(network);
const derived = hdKey.derive(path);
if (!derived.privateKey) {
throw new Error("Could not derive private key for path");
}
return privateKeyToAddressObject(derived.privateKey, chain, path);
}
function generateMnemonic() {
return generateMnemonic$1(wordlist$1);
}
function isMnemonicValid(mnemonic) {
return mnemonicWordlists.some((wordlist) => validateMnemonic(mnemonic, wordlist));
}
function getAddressByWIF(network, privateKeyWIF) {
return addressObjectFromWIF(privateKeyWIF, getNetwork(network));
}
function getPubkeyByWIF(_network, privateKeyWIF) {
return publicKeyHexFromWIF(privateKeyWIF);
}
function entropyToMnemonic(entropy) {
const normalized = typeof entropy === "string" ? ensureBytes(entropy) : entropy;
return entropyToMnemonic$1(normalized, wordlist$1);
}
function generateAddressObject(network = "xna", passphrase = "") {
const mnemonic = generateMnemonic();
const addressObject = getAddressPair(network, mnemonic, 0, 0, passphrase).external;
return {
...addressObject,
mnemonic,
network,
};
}
function publicKeyToAddress(network, publicKey) {
const keyBytes = normalizePublicKey(publicKey);
if (keyBytes.length !== 33 && keyBytes.length !== 65) {
throw new Error("Public key must be 33 or 65 bytes");
}
return publicKeyToAddressBytes(keyBytes, getNetwork(network));
}
function generateAddress(network = "xna") {
return generateAddressObject(network);
}
function getPQHDKey(_network, mnemonic, passphrase = "") {
const seed = mnemonicToSeedBytes(mnemonicToSeedSync, mnemonic, passphrase);
return PQHDKey.fromMasterSeed(seed);
}
function pqExtendedPrivateKey(network, hdKey) {
return hdKey.encodeBase58Check(getPQNetwork(network).pqExtPrivVersion);
}
function pqHDKeyFromExtended(network, extKey) {
return PQHDKey.decodeBase58Check(extKey, getPQNetwork(network).pqExtPrivVersion);
}
function getPQAddressByPath(network, hdKey, path, options = {}) {
const chain = getPQNetwork(network);
const derived = hdKey.derive(path);
const publicKey = derived.publicKey;
const secretKey = derived.secretKey;
const authScript = pqPublicKeyToCommitmentParts(publicKey, options);
return {
address: pqPublicKeyToAddressBytes(publicKey, chain, options),
authType: 0x01,
authDescriptor: bytesToHex(authScript.authDescriptor),
commitment: bytesToHex(authScript.commitment),
path,
publicKey: bytesToHex(publicKey),
privateKey: bytesToHex(secretKey),
seedKey: bytesToHex(derived.pqSeed),
witnessScript: bytesToHex(authScript.witnessScript),
};
}
function getNoAuthAddress(network, options = {}) {
const chain = getPQNetwork(network);
const parts = authScriptCommitmentParts(0x00, null, options);
return {
address: noAuthToAddressBytes(chain, options),
authType: 0x00,
commitment: bytesToHex(parts.commitment),
witnessScript: bytesToHex(parts.witnessScript),
};
}
function getLegacyAuthScriptAddress(network, legacyNetwork, mnemonic, account, index, passphrase = "", options = {}) {
const pqChain = getPQNetwork(network);
const legacyChain = getNetwork(legacyNetwork);
const coinType = legacyChain.bip44;
const hdKey = getHDKey(legacyNetwork, mnemonic, passphrase);
const path = `m/44'/${coinType}'/${account}'/0/${index}`;
const derived = hdKey.derive(path);
if (!derived.privateKey) {
throw new Error("Could not derive private key for path");
}
const legacyObject = privateKeyToAddressObject(derived.privateKey, legacyChain, path);
const publicKeyBytes = ensureBytes(legacyObject.publicKey);
const parts = authScriptCommitmentParts(0x02, publicKeyBytes, options);
return {
address: legacyAuthScriptToAddressBytes(publicKeyBytes, pqChain, options),
path,
publicKey: legacyObject.publicKey,
privateKey: legacyObject.privateKey,
WIF: legacyObject.WIF,
authType: 0x02,
authDescriptor: bytesToHex(parts.authDescriptor),
commitment: bytesToHex(parts.commitment),
witnessScript: bytesToHex(parts.witnessScript),
};
}
function getLegacyAuthScriptAddressByWIF(network, wif, options = {}) {
const pqChain = getPQNetwork(network);
const publicKeyHex = publicKeyHexFromWIF(wif);
const publicKeyBytes = ensureBytes(publicKeyHex);
const parts = authScriptCommitmentParts(0x02, publicKeyBytes, options);
return {
address: legacyAuthScriptToAddressBytes(publicKeyBytes, pqChain, options),
publicKey: publicKeyHex,
privateKey: "",
WIF: wif,
authType: 0x02,
authDescriptor: bytesToHex(parts.authDescriptor),
commitment: bytesToHex(parts.commitment),
witnessScript: bytesToHex(parts.witnessScript),
};
}
function getPQAddress(network, mnemonic, account, index, passphrase = "", options = {}) {
const chain = getPQNetwork(network);
const hdKey = getPQHDKey(network, mnemonic, passphrase);
const path = `m_pq/${chain.purpose}'/${chain.coinType}'/${account}'/${chain.changeIndex}'/${index}'`;
return getPQAddressByPath(network, hdKey, path, options);
}
function pqPublicKeyToAddress(network, publicKey, options = {}) {
const keyBytes = ensureBytes(publicKey);
if (keyBytes.length !== 1312) {
throw new Error("ML-DSA-44 public key must be 1312 bytes");
}
normalizeWitnessScript(options.witnessScript);
return pqPublicKeyToAddressBytes(keyBytes, getPQNetwork(network), options);
}
function pqPublicKeyToCommitmentHex(publicKey, options = {}) {
const keyBytes = ensureBytes(publicKey);
if (keyBytes.length !== 1312) {
throw new Error("ML-DSA-44 public key must be 1312 bytes");
}
return bytesToHex(pqPublicKeyToCommitment(keyBytes, options));
}
function pqPublicKeyToAuthDescriptorHex(publicKey) {
const keyBytes = ensureBytes(publicKey);
if (keyBytes.length !== 1312) {
throw new Error("ML-DSA-44 public key must be 1312 bytes");
}
return bytesToHex(pqPublicKeyToAuthDescriptor(keyBytes));
}
function generatePQAddressObject(network = "xna-pq", passphrase = "", options = {}) {
const mnemonic = generateMnemonic();
const addressObj = getPQAddress(network, mnemonic, 0, 0, passphrase, options);
return {
...addressObj,
mnemonic,
};
}
const NeuraiKey = {
entropyToMnemonic,
generateAddress,
generateAddressObject,
generateMnemonic,
getAddressByPath,
getAddressByWIF,
getPubkeyByWIF,
getAddressPair,
getCoinType,
getHDKey,
isMnemonicValid,
publicKeyToAddress,
getPQAddress,
getPQAddressByPath,
getPQHDKey,
pqExtendedPrivateKey,
pqHDKeyFromExtended,
getNoAuthAddress,
getLegacyAuthScriptAddress,
getLegacyAuthScriptAddressByWIF,
pqPublicKeyToAddress,
pqPublicKeyToAuthDescriptorHex,
pqPublicKeyToCommitmentHex,
generatePQAddressObject,
};
export { BIP32_PQ_EXTKEY_SIZE, HDKey, PQHDKey, NeuraiKey as default, entropyToMnemonic, generateAddress, generateAddressObject, generateMnemonic, generatePQAddressObject, getAddressByPath, getAddressByWIF, getAddressPair, getCoinType, getHDKey, getLegacyAuthScriptAddress, getLegacyAuthScriptAddressByWIF, getNoAuthAddress, getPQAddress, getPQAddressByPath, getPQHDKey, getPubkeyByWIF, isMnemonicValid, pqExtendedPrivateKey, pqHDKeyFromExtended, pqPublicKeyToAddress, pqPublicKeyToAuthDescriptorHex, pqPublicKeyToCommitmentHex, publicKeyToAddress };
//# sourceMappingURL=index.js.map