@nawab_kibria/bitcoin-lib
Version:
A comprehensive Bitcoin HD wallet library with BIP84, BIP44, BIP49 support, mnemonic generation and restoration
310 lines (309 loc) • 12.2 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertToBip84Format = convertToBip84Format;
exports.getBip84ExtendedKeys = getBip84ExtendedKeys;
exports.getCoinType = getCoinType;
exports.getDerivationPath = getDerivationPath;
exports.importAndDerive = importAndDerive;
exports.importAndDeriveBatch = importAndDeriveBatch;
exports.importExtendedKey = importExtendedKey;
exports.quickDerive = quickDerive;
const bitcoin = __importStar(require("bitcoinjs-lib"));
const bip32_1 = require("bip32");
const ecpair_1 = require("ecpair");
const ecc = __importStar(require("tiny-secp256k1"));
const bs58check = require("bs58check");
const bip32 = (0, bip32_1.BIP32Factory)(ecc);
const ECPair = (0, ecpair_1.ECPairFactory)(ecc);
// Initialize ECC library for bitcoinjs-lib Taproot operations
bitcoin.initEccLib(ecc);
/**
* Convert standard extended key to BIP84-specific format with proper version bytes
*/
function convertToBip84Format(extendedKey, network, isPrivate = false) {
// Version bytes for different key formats
const versions = {
// Mainnet
xpub: 0x0488b21e, xprv: 0x0488ade4, // Legacy
ypub: 0x049d7cb2, yprv: 0x049d7878, // SegWit
zpub: 0x04b24746, zprv: 0x04b2430c, // Native SegWit
// Testnet
tpub: 0x043587cf, tprv: 0x04358394, // Legacy
upub: 0x044a5262, uprv: 0x044a4e28, // SegWit
vpub: 0x045f1cf6, vprv: 0x045f18bc, // Native SegWit
};
try {
// Decode the extended key to get the raw data
const decoded = bs58check.decode(extendedKey);
// Determine target version bytes based on network and key type
let targetVersion;
if (network === bitcoin.networks.bitcoin) {
targetVersion = isPrivate ? versions.zprv : versions.zpub;
}
else {
// For testnet/regtest
targetVersion = isPrivate ? versions.vprv : versions.vpub;
}
// Create new buffer with target version bytes
const newBuffer = Buffer.alloc(decoded.length);
// Write the new version bytes (4 bytes, big endian)
newBuffer.writeUInt32BE(targetVersion, 0);
// Copy the rest of the data (depth, fingerprint, child number, chain code, key data)
Buffer.from(decoded).copy(newBuffer, 4, 4);
// Encode with base58check
return bs58check.encode(newBuffer);
}
catch (error) {
// Fallback to simple prefix replacement if proper conversion fails
if (network === bitcoin.networks.bitcoin) {
if (isPrivate) {
return extendedKey.replace(/^xprv/, 'zprv');
}
else {
return extendedKey.replace(/^xpub/, 'zpub');
}
}
else {
// For testnet/regtest
if (isPrivate) {
return extendedKey.replace(/^tprv/, 'vprv');
}
else {
return extendedKey.replace(/^tpub/, 'vpub');
}
}
}
}
/**
* Get BIP84-specific extended keys for a given seed and network
* This generates proper BIP84 extended keys with correct version bytes
*/
function getBip84ExtendedKeys(seed, network) {
// Create custom network config with BIP84 version bytes
const bip84Network = Object.assign(Object.assign({}, network), { bip32: {
public: network === bitcoin.networks.bitcoin ? 0x04b24746 : 0x045f1cf6, // zpub/vpub
private: network === bitcoin.networks.bitcoin ? 0x04b2430c : 0x045f18bc // zprv/vprv
} });
const root = bip32.fromSeed(seed, bip84Network);
const coinType = network === bitcoin.networks.bitcoin ? 0 : 1;
const account = root.derivePath(`m/84'/${coinType}'/0'`);
return {
zpub: account.neutered().toBase58(),
zprv: account.toBase58()
};
}
/**
* Get coin type for a given network (BIP44 standard)
*/
function getCoinType(network) {
return network === bitcoin.networks.bitcoin ? 0 : 1;
}
/**
* Get derivation path for different BIP standards
*/
function getDerivationPath(purpose, network, account = 0, change = 0, addressIndex = 0) {
const coinType = getCoinType(network);
return `m/${purpose}'/${coinType}'/${account}'/${change}/${addressIndex}`;
}
/**
* Import and derive address and keys for any index from mnemonic or root key
* This is a flexible utility for wallet recovery and address generation
*/
function importAndDerive(options) {
const { mnemonic, rootKey, purpose, network = bitcoin.networks.bitcoin, account = 0, change = 0, index, addressType } = options;
// Validation
if (!mnemonic && !rootKey) {
throw new Error('Either mnemonic or rootKey must be provided');
}
if (mnemonic && rootKey) {
throw new Error('Provide either mnemonic or rootKey, not both');
}
// Determine address type from purpose if not provided
const finalAddressType = addressType || (() => {
switch (purpose) {
case 44: return 'legacy';
case 49: return 'segwit';
case 84: return 'native-segwit';
case 86: return 'native-segwit'; // Default to native-segwit for BIP86
default: throw new Error(`Unsupported purpose: ${purpose}`);
}
})();
// Create derivation path
const coinType = getCoinType(network);
const derivationPath = `m/${purpose}'/${coinType}'/${account}'/${change}/${index}`;
let root;
try {
if (mnemonic) {
// Import from mnemonic
const bip39 = require('bip39');
if (!bip39.validateMnemonic(mnemonic)) {
throw new Error('Invalid mnemonic provided');
}
const seed = Buffer.from(bip39.mnemonicToSeedSync(mnemonic));
root = bip32.fromSeed(seed, network);
}
else {
// Import from root key
root = bip32.fromBase58(rootKey, network);
}
// Derive the specific address
// If the root key is already at account level or deeper, adjust the derivation path
let child;
try {
child = root.derivePath(derivationPath);
}
catch (error) {
// If full derivation fails, try relative path from the imported key
if (error instanceof Error && error.message.includes('master')) {
// Imported key might be account-level, try relative derivation
const relativePath = `${change}/${index}`;
child = root.derivePath(relativePath);
}
else {
throw error;
}
}
const privateKey = Buffer.from(child.privateKey);
const publicKey = Buffer.from(child.publicKey);
// Generate address based on type
let address;
let internalKey;
let signingKey;
switch (finalAddressType) {
case 'legacy':
const legacyPayment = bitcoin.payments.p2pkh({
pubkey: publicKey,
network,
});
address = legacyPayment.address;
break;
case 'segwit':
const segwitPayment = bitcoin.payments.p2sh({
redeem: bitcoin.payments.p2wpkh({
pubkey: publicKey,
network,
}),
network,
});
address = segwitPayment.address;
break;
case 'native-segwit':
const nativeSegwitPayment = bitcoin.payments.p2wpkh({
pubkey: publicKey,
network,
});
address = nativeSegwitPayment.address;
break;
default:
throw new Error(`Unsupported address type: ${finalAddressType}`);
}
return {
address,
publicKey: publicKey.toString('hex'),
privateKey: privateKey.toString('hex'),
derivationPath,
addressType: finalAddressType,
internalKey,
signingKey
};
}
catch (error) {
throw new Error(`Failed to import and derive: ${error}`);
}
}
/**
* Batch import and derive multiple addresses from mnemonic or root key
*/
function importAndDeriveBatch(options) {
const { startIndex = 0, count } = options, baseOptions = __rest(options, ["startIndex", "count"]);
const results = [];
for (let i = 0; i < count; i++) {
const index = startIndex + i;
const result = importAndDerive(Object.assign(Object.assign({}, baseOptions), { index }));
results.push(Object.assign(Object.assign({}, result), { index }));
}
return results;
}
/**
* Import from extended key and get account-level information
*/
function importExtendedKey(extendedKey, network) {
try {
const targetNetwork = network || bitcoin.networks.bitcoin;
const node = bip32.fromBase58(extendedKey, targetNetwork);
return {
isPrivate: !!node.privateKey,
depth: node.depth,
parentFingerprint: node.parentFingerprint,
childNumber: node.index,
chainCode: node.chainCode.toString('hex'),
key: node.privateKey ? node.privateKey.toString('hex') : node.publicKey.toString('hex'),
xpub: node.neutered().toBase58(),
xprv: node.privateKey ? node.toBase58() : undefined,
network: targetNetwork
};
}
catch (error) {
throw new Error(`Failed to import extended key: ${error}`);
}
}
/**
* Quick address lookup - derive single address from any source
*/
function quickDerive(source, // mnemonic or extended key
purpose, index, network) {
const targetNetwork = network || bitcoin.networks.bitcoin;
// Auto-detect if source is mnemonic or extended key
const isMnemonic = source.split(' ').length >= 12; // Assume mnemonic if 12+ words
const result = importAndDerive(Object.assign(Object.assign({}, (isMnemonic ? { mnemonic: source } : { rootKey: source })), { purpose,
index, network: targetNetwork }));
return {
address: result.address,
derivationPath: result.derivationPath,
addressType: result.addressType
};
}