UNPKG

@okxweb3/coin-bitcoin

Version:

@okxweb3/coin-bitcoin is a Bitcoin SDK for building Web3 wallets and applications. It supports BTC, BSV, DOGE, LTC, and TBTC, enabling private key management, transaction signing, address generation, and inscriptions like BRC-20, Runes, CAT, and Atomicals

269 lines 11.8 kB
"use strict"; 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 (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.srcInscribe = exports.SrcInscriptionTool = void 0; const bitcoin = __importStar(require("./bitcoinjs-lib")); const crypto_lib_1 = require("@okxweb3/crypto-lib"); const coin_base_1 = require("@okxweb3/coin-base"); const taproot = __importStar(require("./taproot")); const bcrypto = __importStar(require("./bitcoinjs-lib/crypto")); const txBuild_1 = require("./txBuild"); const bitcoinjs_lib_1 = require("./bitcoinjs-lib"); const sigcost_1 = require("./sigcost"); const schnorr = crypto_lib_1.signUtil.schnorr.secp256k1.schnorr; const defaultTxVersion = 2; const PART_LEN = 31; const defaultSequenceNum = 0xfffffffd; const defaultRevealOutValue = 7800; const defaultMinChangeValue = 7800; const maxStandardTxWeight = 4000000 / 10; class SrcInscriptionTool { constructor() { this.network = bitcoin.networks.bitcoin; this.revealTxs = []; this.commitTx = new bitcoin.Transaction(); this.commitTxPrevOutputFetcher = []; this.revealTxPrevOutputFetcher = []; this.mustCommitTxFee = 0; this.mustRevealTxFees = []; this.commitAddrs = []; } static newSrcInscriptionTool(network, request) { const tool = new SrcInscriptionTool(); tool.network = network; const revealOutValue = request.revealOutValue || defaultRevealOutValue; const minChangeValue = request.minChangeValue || defaultMinChangeValue; const insufficient = tool.buildCommitTx(network, request.inscriptionData, revealOutValue, request.commitTxPrevOutputList, request.changeAddress, request.commitFeeRate, minChangeValue); if (insufficient) { return tool; } tool.signCommitTx(request.commitTxPrevOutputList); return tool; } buildCommitTx(network, inscriptionData, revealOutValue, commitTxPrevOutputList, changeAddress, commitFeeRate, minChangeValue) { let prefix = Buffer.from(inscriptionData.contentType); let body = Buffer.from(inscriptionData.body); while (body[body.length - 1] == 0) { body = body.slice(0, body.length - 1); } let l = 2 + prefix.length + body.length; let total = l % 62 == 0 ? l : (l + 62 - l % 62); let bufferWriter = bitcoinjs_lib_1.BufferWriter.withCapacity(total); bufferWriter.writeSlice(Buffer.from([(prefix.length + body.length) / 256, (prefix.length + body.length) % 256])); bufferWriter.writeSlice(prefix); bufferWriter.writeSlice(body); if (total > l) { bufferWriter.writeSlice(Buffer.alloc(total - l)); } let data = bufferWriter.end(); let buf = coin_base_1.base.fromHex(xcp_rc4(commitTxPrevOutputList[0].txId, data.toString("hex"))); let totalSenderAmount = 0; let totalRevealPrevOutputValue = 0; const tx = new bitcoin.Transaction(); tx.version = defaultTxVersion; totalRevealPrevOutputValue += revealOutValue; tx.addOutput(bitcoin.address.toOutputScript(inscriptionData.revealAddr, network), revealOutValue); while (buf.length) { let buf1 = buf.slice(0, Math.min(PART_LEN, buf.length)); let first = buf1.toString("hex"); if (first.length < 62) { first = first + '0'.repeat(62 - first.length); } buf = buf.slice(buf1.length); let buf2 = buf.slice(0, Math.min(PART_LEN, buf.length)); let second = buf2.toString("hex"); if (second.length < 62) { second = second + '0'.repeat(62 - second.length); } buf = buf.slice(buf1.length); const pubkeys = [ '03' + first + '00', '02' + second + '00', '020202020202020202020202020202020202020202020202020202020202020202', ].map(hex => Buffer.from(hex, 'hex')); const payment = bitcoin.payments.p2ms({ m: 1, pubkeys }); tx.addOutput(payment.output, revealOutValue); totalRevealPrevOutputValue += revealOutValue; } commitTxPrevOutputList.forEach(commitTxPrevOutput => { const hash = coin_base_1.base.reverseBuffer(coin_base_1.base.fromHex(commitTxPrevOutput.txId)); tx.addInput(hash, commitTxPrevOutput.vOut, defaultSequenceNum); this.commitTxPrevOutputFetcher.push(commitTxPrevOutput.amount); totalSenderAmount += commitTxPrevOutput.amount; }); const changePkScript = bitcoin.address.toOutputScript(changeAddress, network); tx.addOutput(changePkScript, 0); const txForEstimate = tx.clone(); signTx(txForEstimate, commitTxPrevOutputList, this.network); const vsize = (0, sigcost_1.countAdjustedVsize)(txForEstimate, commitTxPrevOutputList.map(a => a.address), network); const fee = Math.floor(vsize * commitFeeRate); const changeAmount = totalSenderAmount - totalRevealPrevOutputValue - fee; if (changeAmount >= minChangeValue) { tx.outs[tx.outs.length - 1].value = changeAmount; } else { tx.outs = tx.outs.slice(0, tx.outs.length - 1); txForEstimate.outs = txForEstimate.outs.slice(0, txForEstimate.outs.length - 1); const vsizeWithoutChange = (0, sigcost_1.countAdjustedVsize)(txForEstimate, commitTxPrevOutputList.map(a => a.address), network); const feeWithoutChange = Math.floor(vsizeWithoutChange * commitFeeRate); if (totalSenderAmount - totalRevealPrevOutputValue - feeWithoutChange < 0) { this.mustCommitTxFee = fee; return true; } } this.commitTx = tx; return false; } signCommitTx(commitTxPrevOutputList) { signTx(this.commitTx, commitTxPrevOutputList, this.network); } calculateFee() { let commitTxFee = 0; this.commitTx.ins.forEach((_, i) => { commitTxFee += this.commitTxPrevOutputFetcher[i]; }); this.commitTx.outs.forEach(out => { commitTxFee -= out.value; }); let revealTxFees = []; this.revealTxs.forEach((revealTx, i) => { let revealTxFee = 0; revealTxFee += this.revealTxPrevOutputFetcher[i]; revealTxFee -= revealTx.outs[0].value; revealTxFees.push(revealTxFee); }); return { commitTxFee, revealTxFees, }; } } exports.SrcInscriptionTool = SrcInscriptionTool; function signTx(tx, commitTxPrevOutputList, network) { tx.ins.forEach((input, i) => { const addressType = (0, txBuild_1.getAddressType)(commitTxPrevOutputList[i].address, network); const privateKey = coin_base_1.base.fromHex((0, txBuild_1.privateKeyFromWIF)(commitTxPrevOutputList[i].privateKey, network)); const privateKeyHex = coin_base_1.base.toHex(privateKey); const publicKey = (0, txBuild_1.private2public)(privateKeyHex); if (addressType === 'segwit_taproot') { const prevOutScripts = commitTxPrevOutputList.map(o => bitcoin.address.toOutputScript(o.address, network)); const values = commitTxPrevOutputList.map(o => o.amount); const hash = tx.hashForWitnessV1(i, prevOutScripts, values, bitcoin.Transaction.SIGHASH_DEFAULT); const tweakedPrivKey = taproot.taprootTweakPrivKey(privateKey); const signature = Buffer.from(schnorr.sign(hash, tweakedPrivKey, coin_base_1.base.randomBytes(32))); input.witness = [Buffer.from(signature)]; } else if (addressType === 'legacy') { const prevScript = bitcoin.address.toOutputScript(commitTxPrevOutputList[i].address, network); const hash = tx.hashForSignature(i, prevScript, bitcoin.Transaction.SIGHASH_ALL); const signature = (0, txBuild_1.sign)(hash, privateKeyHex); const payment = bitcoin.payments.p2pkh({ signature: bitcoin.script.signature.encode(signature, bitcoin.Transaction.SIGHASH_ALL), pubkey: publicKey, }); input.script = payment.input; } else { const pubKeyHash = bcrypto.hash160(publicKey); const prevOutScript = Buffer.of(0x19, 0x76, 0xa9, 0x14, ...pubKeyHash, 0x88, 0xac); const value = commitTxPrevOutputList[i].amount; const hash = tx.hashForWitness(i, prevOutScript, value, bitcoin.Transaction.SIGHASH_ALL); const signature = (0, txBuild_1.sign)(hash, privateKeyHex); input.witness = [ bitcoin.script.signature.encode(signature, bitcoin.Transaction.SIGHASH_ALL), publicKey, ]; const redeemScript = Buffer.of(0x16, 0, 20, ...pubKeyHash); if (addressType === "segwit_nested") { input.script = redeemScript; } } }); } function srcInscribe(network, request) { const tool = SrcInscriptionTool.newSrcInscriptionTool(network, request); if (tool.mustCommitTxFee > 0) { return { commitTx: "", revealTxs: [], commitTxFee: tool.mustCommitTxFee, revealTxFees: tool.mustRevealTxFees, commitAddrs: tool.commitAddrs, }; } return { commitTx: tool.commitTx.toHex(), revealTxs: tool.revealTxs.map(revealTx => revealTx.toHex()), ...tool.calculateFee(), commitAddrs: tool.commitAddrs, }; } exports.srcInscribe = srcInscribe; function rc4(key, str) { var s = [], j = 0, x, res = ''; for (var i = 0; i < 256; i++) { s[i] = i; } for (i = 0; i < 256; i++) { j = (j + s[i] + key.charCodeAt(i % key.length)) % 256; x = s[i]; s[i] = s[j]; s[j] = x; } i = 0; j = 0; for (var y = 0; y < str.length; y++) { i = (i + 1) % 256; j = (j + s[i]) % 256; x = s[i]; s[i] = s[j]; s[j] = x; res += String.fromCharCode(str.charCodeAt(y) ^ s[(s[i] + s[j]) % 256]); } return res; } function hex2bin(hex) { var bytes = []; var str; for (var i = 0; i < hex.length - 1; i += 2) { var ch = parseInt(hex.substr(i, 2), 16); bytes.push(ch); } str = String.fromCharCode.apply(String, bytes); return str; } function bin2hex(s) { var i, l, o = '', n; s += ''; for (i = 0, l = s.length; i < l; i++) { n = s.charCodeAt(i).toString(16); o += n.length < 2 ? '0' + n : n; } return o; } function xcp_rc4(key, datachunk) { return bin2hex(rc4(hex2bin(key), hex2bin(datachunk))); } //# sourceMappingURL=src20.js.map