UNPKG

nerve-sdk-js

Version:
991 lines (857 loc) 37.3 kB
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } var { ECPairFactory } = require('ecpair'); var bufferutils_1 = require('bitcoinjs-lib/src/bufferutils'); var bitcoin = require('bitcoinjs-lib'); var bitcore = require('bitcore-lib'); var http = require("../test/api/https"); var nerve = require("../index"); var ECPair; var toXOnly = pubKey => pubKey.length === 32 ? pubKey : pubKey.slice(1, 33); var MAX_SAFE_INTEGER = 9007199254740991; function estimateTxSizeHeader(mainnet) { if (!mainnet) mainnet = false; var currentNetwork = mainnet ? bitcoin.networks.bitcoin : bitcoin.networks.testnet; var keyPair = ECPair.fromWIF('L2uPYXe17xSTqbCjZvL2DsyXPCbXspvcu5mHLDYUgzdUbZGSKrSr'); var script0 = bitcoin.payments.p2pkh({ network: currentNetwork, pubkey: keyPair.publicKey }); var script1 = bitcoin.payments.p2sh({ redeem: bitcoin.payments.p2wpkh({ network: currentNetwork, pubkey: keyPair.publicKey }) }); var script2 = bitcoin.payments.p2wpkh({ network: currentNetwork, pubkey: keyPair.publicKey }); var childNodeXOnlyPubkey = toXOnly(keyPair.publicKey); var script3 = bitcoin.payments.p2tr({ network: currentNetwork, internalPubkey: childNodeXOnlyPubkey }); return { currentNetwork, keyPair, script0, script1, script2, script3, childNodeXOnlyPubkey }; } function estimateTxSizeBody(currentNetwork, inputCount, inputData, address0, address1, address2, address3, legacyOutputCount, nestedSegwitOutputCount, nativeSegwitOutputCount, taprootOutputCount, opReturnArray) { var psbt = new bitcoin.Psbt({ network: currentNetwork }); psbt.addInput(inputData); for (var i = 1; i < inputCount; i++) { var _inputData = Object.assign({}, inputData); _inputData.index = i; psbt.addInput(_inputData); } for (var _i = 0; _i < opReturnArray.length; _i++) { var opReturnBuffer = opReturnArray[_i]; psbt.addOutput({ script: bitcoin.payments.embed({ data: [opReturnBuffer] }).output, value: 0 }); } for (var _i2 = 0; _i2 < legacyOutputCount; _i2++) { psbt.addOutput({ address: address0, value: 1002000 }); } for (var _i3 = 0; _i3 < nestedSegwitOutputCount; _i3++) { psbt.addOutput({ address: address1, value: 1002000 }); } for (var _i4 = 0; _i4 < nativeSegwitOutputCount; _i4++) { psbt.addOutput({ address: address2, value: 1002000 }); } for (var _i5 = 0; _i5 < taprootOutputCount; _i5++) { psbt.addOutput({ address: address3, value: 1002000 }); } return psbt; } function estimateTxSizeFooter(psbt) { psbt.finalizeAllInputs(); var tx = psbt.extractTransaction(true); var vSize = tx.virtualSize(); return vSize + 1; } function estimateLegacyTxSize(mainnet, inputCount, legacyOutputCount, nestedSegwitOutputCount, nativeSegwitOutputCount, taprootOutputCount, opReturnArray) { try { var { currentNetwork, keyPair, script0, script1, script2, script3, childNodeXOnlyPubkey } = estimateTxSizeHeader(mainnet); var inputData = { hash: "96cd2cef275b9de5dba26ac969224689eabe71b0ac60787510a10e16af24b7a7", index: 0, nonWitnessUtxo: Buffer.from('010000000190a1ae9724b99311109e72d76684a7db5c181b9aad4d37c0698b7e1846ce5a14010000006a47304402201d54971b87675a4f874b1565153cd2b4afea1045292a5024185d68b989931fba02203fe1aa43c40cc88a74e65780c2f37403f113d4ca7df38d3c9e2f99ffbab067a501210365db9da3f8a260078a7e8f8b708a1161468fb2323ffda5ec16b261ec1056f455ffffffff65f0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88acf0e62607000000001976a9148bbc95d2709c71607c60ee3f097c1217482f518d88ac00000000', 'hex') }; var psbt = estimateTxSizeBody(currentNetwork, inputCount, inputData, script0.address, script1.address, script2.address, script3.address, legacyOutputCount, nestedSegwitOutputCount, nativeSegwitOutputCount, taprootOutputCount, opReturnArray); for (var i = 0; i < inputCount; i++) { psbt.signInput(i, keyPair); } return estimateTxSizeFooter(psbt); } catch (e) { console.log(e); throw e; } } function estimateNestedSegwitTxSize(mainnet, inputCount, legacyOutputCount, nestedSegwitOutputCount, nativeSegwitOutputCount, taprootOutputCount, opReturnArray) { try { var { currentNetwork, keyPair, script0, script1, script2, script3, childNodeXOnlyPubkey } = estimateTxSizeHeader(mainnet); var inputData = { hash: "c904f6daae3617a2611e30bfbb782a8ad4845fb25e1a6759ed9eee25e2990ad3", index: 0, witnessUtxo: { script: script1.output, value: 11181008 }, redeemScript: script1.redeem.output }; var psbt = estimateTxSizeBody(currentNetwork, inputCount, inputData, script0.address, script1.address, script2.address, script3.address, legacyOutputCount, nestedSegwitOutputCount, nativeSegwitOutputCount, taprootOutputCount, opReturnArray); for (var i = 0; i < inputCount; i++) { psbt.signInput(i, keyPair); } return estimateTxSizeFooter(psbt); } catch (e) { console.log(e); throw e; } } function estimateNativeSegwitTxSize(mainnet, inputCount, legacyOutputCount, nestedSegwitOutputCount, nativeSegwitOutputCount, taprootOutputCount, opReturnArray) { try { var { currentNetwork, keyPair, script0, script1, script2, script3, childNodeXOnlyPubkey } = estimateTxSizeHeader(mainnet); var inputData = { hash: "c904f6daae3617a2611e30bfbb782a8ad4845fb25e1a6759ed9eee25e2990ad3", index: 0, witnessUtxo: { script: script2.output, value: 11181008 } }; var psbt = estimateTxSizeBody(currentNetwork, inputCount, inputData, script0.address, script1.address, script2.address, script3.address, legacyOutputCount, nestedSegwitOutputCount, nativeSegwitOutputCount, taprootOutputCount, opReturnArray); for (var i = 0; i < inputCount; i++) { psbt.signInput(i, keyPair); } return estimateTxSizeFooter(psbt); } catch (e) { console.log(e); throw e; } } function estimateTaprootTxSize(mainnet, inputCount, legacyOutputCount, nestedSegwitOutputCount, nativeSegwitOutputCount, taprootOutputCount, opReturnArray) { try { var { currentNetwork, keyPair, script0, script1, script2, script3, childNodeXOnlyPubkey } = estimateTxSizeHeader(mainnet); var tweakedChildNode = keyPair.tweak(bitcoin.crypto.taggedHash('TapTweak', childNodeXOnlyPubkey)); var inputData = { hash: "a5c83bbcdd14bc1b502e4a9e349409dbfbe1afa6eb94decea942b92dc1e2231c", index: 0, witnessUtxo: { script: script3.output, value: 11181008 }, tapInternalKey: childNodeXOnlyPubkey }; var psbt = estimateTxSizeBody(currentNetwork, inputCount, inputData, script0.address, script1.address, script2.address, script3.address, legacyOutputCount, nestedSegwitOutputCount, nativeSegwitOutputCount, taprootOutputCount, opReturnArray); for (var i = 0; i < inputCount; i++) { psbt.signInput(i, tweakedChildNode); } return estimateTxSizeFooter(psbt); } catch (e) { console.log(e); throw e; } } function getSpendingUtxos(utxos, spending) { var spendingUtxos = []; var total = 0; var inputCount = 0; for (var i = 0; i < utxos.length; i++) { var utxo = utxos[i]; if (total >= spending) { break; } inputCount++; total += utxo.amount; spendingUtxos.push(utxo); } if (total < spending) { throw "not enough utxo"; } return { spendingUtxos, total, inputCount }; } function createSpendingUtxosAndOutput(mainnet, txType, pubkeyHex, utxos, receiveAddress, sendAmount, feeRate, opReturnArray) { var currentNetwork = mainnet ? bitcoin.networks.bitcoin : bitcoin.networks.testnet; var pubkeyBuffer = Buffer.from(pubkeyHex, 'hex'); var script; if (txType == 0) { script = bitcoin.payments.p2pkh({ network: currentNetwork, pubkey: pubkeyBuffer }); } else if (txType == 1) { script = bitcoin.payments.p2sh({ redeem: bitcoin.payments.p2wpkh({ network: currentNetwork, pubkey: pubkeyBuffer }) }); } else if (txType == 2) { script = bitcoin.payments.p2wpkh({ network: currentNetwork, pubkey: pubkeyBuffer }); } else if (txType == 3) { var childNodeXOnlyPubkey = toXOnly(pubkeyBuffer); script = bitcoin.payments.p2tr({ network: currentNetwork, internalPubkey: childNodeXOnlyPubkey }); } var psbt = new bitcoin.Psbt({ network: currentNetwork }); var { total, spending, spendingUtxos, size } = btc.calcSpendingUtxosAndFee(mainnet, txType, utxos, receiveAddress, sendAmount, feeRate, opReturnArray); for (var i = 0; i < opReturnArray.length; i++) { var opReturnBuffer = opReturnArray[i]; psbt.addOutput({ script: bitcoin.payments.embed({ data: [opReturnBuffer] }).output, value: 0 }); } psbt.addOutput({ address: receiveAddress, value: sendAmount }); if (total > spending && total - spending > 546) { psbt.addOutput({ address: script.address, value: total - spending }); } return { psbt, currentNetwork, spendingUtxos, script }; } function getBtcRpc() { var mainnet = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; var url = 'https://btctest.nerve.network'; var authValue = 'Basic TmVydmU6OW83ZlNtWFBCZlBRb1FTYm5CQg=='; if (mainnet) { url = 'https://btc.nerve.network'; authValue = 'Basic TmVydmU6OW83ZlNtWFBCQ000RjZjQUpzZlBRb1FTYm5CQg=='; } return { url, authValue }; } function getFeeRateOnChain() { return _getFeeRateOnChain.apply(this, arguments); } function _getFeeRateOnChain() { _getFeeRateOnChain = _asyncToGenerator(function* () { var mainnet = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; var highFeeRate = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; // if (!mainnet) { // return 1; // } var mode = highFeeRate ? "CONSERVATIVE" : "ECONOMICAL"; var { url, authValue } = getBtcRpc(mainnet); return yield http.postCompleteWithHeader(url, 'estimatesmartfee', [15, mode], { 'Authorization': authValue }).then(response => { if (response.hasOwnProperty("result")) { return Math.ceil(response.result.feerate * 1.1 * 100000); } else { throw "Get rawtransaction error"; } }).catch(error => { throw "Network error"; }); }); return _getFeeRateOnChain.apply(this, arguments); } function getMempoolSpaceRpc() { var mainnet = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; var url = 'https://mempool.space/testnet/api/v1/'; if (mainnet) { url = 'https://mempool.space/api/v1/'; } return url; } function checkUInt53(n) { if (n < 0 || n > MAX_SAFE_INTEGER || n % 1 !== 0) throw new RangeError('value out of range'); } function encodingLength(number) { checkUInt53(number); return number < 0xfd ? 1 : number <= 0xffff ? 3 : number <= 0xffffffff ? 5 : 9; } function calcOpReturnLen(opReturnBytesLen) { var dataLen; if (opReturnBytesLen < 76) { dataLen = opReturnBytesLen + 1; } else if (opReturnBytesLen < 256) { dataLen = opReturnBytesLen + 2; } else dataLen = opReturnBytesLen + 3; var scriptLen; scriptLen = dataLen + 1 + encodingLength(dataLen + 1); var amountLen = 8; return scriptLen + amountLen; } /** * calc split number of Transaction Change Spliting by splitGranularity * change = fromTotal - transfer - fee * change = splitNum * splitGranularity * fee = txSize * feeRate * txSize = f(splitNum) * f(splitNum) = f(1) + 43 * (splitNum - 1) ==> Derived from btc.calcTxSizeWithdrawal(inputNum, splitNum) * In summary: * splitNum = (fromTotal - transfer - btc.calcTxSizeWithdrawal(inputNum, 1) * feeRate + 43 * feeRate) / (43 * feeRate + splitGranularity) */ function calcSplitNumP2WSH(fromTotal, transfer, feeRate, splitGranularity, inputNum) { // numerator and denominator var numerator = fromTotal - transfer - btc.calcTxSizeWithdrawal(inputNum, 1) * feeRate + 43 * feeRate; var denominator = 43 * feeRate + splitGranularity; var splitNum = int(numerator / denominator); if (splitNum == 0 && numerator % denominator > 0) { splitNum = 1; } return splitNum; } var btc = { initEccLibForNode() { var ecc = require('tiny-secp256k1'); ECPair = ECPairFactory(ecc); bitcoin.initEccLib(ecc); return ECPair; }, initEccLibForWeb() { require('tiny-secp256k1').then(ecc => { ECPair = ECPairFactory(ecc); bitcoin.initEccLib(ecc); }); }, getAddressByPub(pubKey, isMainnet) { var network = isMainnet ? bitcoin.networks.bitcoin : bitcoin.networks.testnet; var publicKeyBuffer = Buffer.from(pubKey, 'hex'); // p2pkh Legacy var { address: Legacy } = bitcoin.payments.p2pkh({ network, pubkey: publicKeyBuffer }); // p2wpkh Native Segwit var { address: NativeSegwit } = bitcoin.payments.p2wpkh({ network, pubkey: publicKeyBuffer }); // p2sh Nested Segwit var { address: NestedSegwit } = bitcoin.payments.p2sh({ redeem: bitcoin.payments.p2wpkh({ network, pubkey: publicKeyBuffer }) }); // p2tr Taproot var { address: Taproot } = bitcoin.payments.p2tr({ network, internalPubkey: toXOnly(publicKeyBuffer) }); return { Legacy, NativeSegwit, NestedSegwit, Taproot }; }, calcSpendingUtxosAndFee(mainnet, txType, utxos, receiveAddress, sendAmount, feeRate, opReturnArray) { var len0 = 0, len1 = 0, len2 = 0, len3 = 0; var outForType = 'len' + txType + '++'; eval(outForType); var add = bitcore.Address.fromString(receiveAddress, mainnet ? 'livenet' : 'testnet'); if (add.isPayToPublicKeyHash()) { len0++; } else if (add.isPayToScriptHash()) { len1++; } else if (add.isPayToWitnessPublicKeyHash() || add.isPayToWitnessScriptHash()) { len2++; } else if (add.isPayToTaproot()) { len3++; } var nativeWitnessScriptOutCount = add.isPayToWitnessScriptHash() ? 1 : 0; var fee = 0; var size = 0; var total = 0; var enoughUTXO = false; var spendingUtxos = []; for (var k = 0; k < utxos.length; k++) { var utxo = utxos[k]; spendingUtxos.push(utxo); total += Number(utxo.amount); size = this.estimateTxSize(txType, spendingUtxos.length, len0, len1, len2, len3, opReturnArray, nativeWitnessScriptOutCount); fee = size * feeRate; if (total >= sendAmount + fee) { enoughUTXO = true; break; } } if (!enoughUTXO) { throw "not enough utxo, may need more: " + (sendAmount + fee - total); } var spending = sendAmount + fee; /*let size = this.estimateTxSize(txType, 1, len0, len1, len2, len3, opReturnArray, nativeWitnessScriptOutCount); let fee = size * feeRate; let spending = sendAmount + fee; let {spendingUtxos, total, inputCount} = getSpendingUtxos(utxos, spending); if (inputCount > 1) { let _size = this.estimateTxSize(txType, inputCount, len0, len1, len2, len3, opReturnArray, nativeWitnessScriptOutCount); fee = _size * feeRate; spending = sendAmount + fee; size = _size; while (total < spending) { let { spendingUtxos: spendingUtxos1, total: total1, inputCount: inputCount1 } = getSpendingUtxos(utxos, spending); if (inputCount1 > inputCount) { let size1 = this.estimateTxSize(txType, inputCount1, len0, len1, len2, len3, opReturnArray, nativeWitnessScriptOutCount); fee = size1 * feeRate; spending = sendAmount + fee; size = size1; } total = total1; inputCount = inputCount1; spendingUtxos = spendingUtxos1; } } return {total, spending, spendingUtxos, size, fee}; */ return { total, spending, spendingUtxos, size, fee }; }, estimateTxSize(txType, inputCount, legacyOutputCount, nestedSegwitOutputCount, nativeSegwitOutputCount, taprootOutputCount, opReturnArray, nativeSegwitScriptOutputCount) { var size = 0; if (nativeSegwitScriptOutputCount) { size += nativeSegwitScriptOutputCount * 12; } switch (txType) { case 0: size += estimateLegacyTxSize(false, inputCount, legacyOutputCount, nestedSegwitOutputCount, nativeSegwitOutputCount, taprootOutputCount, opReturnArray); break; case 1: size += estimateNestedSegwitTxSize(false, inputCount, legacyOutputCount, nestedSegwitOutputCount, nativeSegwitOutputCount, taprootOutputCount, opReturnArray); break; case 2: size += estimateNativeSegwitTxSize(false, inputCount, legacyOutputCount, nestedSegwitOutputCount, nativeSegwitOutputCount, taprootOutputCount, opReturnArray); break; case 3: size += estimateTaprootTxSize(false, inputCount, legacyOutputCount, nestedSegwitOutputCount, nativeSegwitOutputCount, taprootOutputCount, opReturnArray); break; default: return 0; } return size; }, getrawtransaction() { var _arguments = arguments; return _asyncToGenerator(function* () { var mainnet = _arguments.length > 0 && _arguments[0] !== undefined ? _arguments[0] : false; var txid = _arguments.length > 1 ? _arguments[1] : undefined; var verbose = _arguments.length > 2 && _arguments[2] !== undefined ? _arguments[2] : false; var { url, authValue } = getBtcRpc(mainnet); return yield http.postCompleteWithHeader(url, 'getrawtransaction', [txid, verbose], { 'Authorization': authValue }).then(response => { if (response.hasOwnProperty("result")) { return response.result; } else { throw "Get rawtransaction error"; } }).catch(error => { throw "Network error"; }); })(); }, getFeeRate() { var _arguments2 = arguments; return _asyncToGenerator(function* () { var mainnet = _arguments2.length > 0 && _arguments2[0] !== undefined ? _arguments2[0] : false; var highFeeRate = _arguments2.length > 1 && _arguments2[1] !== undefined ? _arguments2[1] : true; var mode = highFeeRate ? "CONSERVATIVE" : "ECONOMICAL"; var url = getMempoolSpaceRpc(mainnet); url += 'fees/recommended'; return yield http.get(url).then(response => { if (response.hasOwnProperty("fastestFee")) { return highFeeRate ? response.fastestFee : response.halfHourFee; } else { throw "Get recommended fee error: " + response; } }).catch(error => { throw "Network error: " + error; }); })(); }, getUtxos() { var _arguments3 = arguments; return _asyncToGenerator(function* () { var mainnet = _arguments3.length > 0 && _arguments3[0] !== undefined ? _arguments3[0] : false; var address = _arguments3.length > 1 ? _arguments3[1] : undefined; var amount = _arguments3.length > 2 ? _arguments3[2] : undefined; if (!amount) { amount = 0; } if (mainnet) { return yield http.postDirect('https://api.v2.nabox.io/nabox-api/btc/utxo', { "language": "CHS", "chainId": 0, "address": address, "coinAmount": amount, "serviceCharge": amount, "utxoType": 1 }).then(response => { if (response.hasOwnProperty("code")) { if (response.code !== 1000) { throw response.msg; } var utxos = response.data.utxo; for (var i = 0; i < utxos.length; i++) { var utxo = utxos[i]; utxo.amount = utxo.satoshi; } return utxos; } else { throw "Get utxo error"; } }).catch(error => { throw "Network error"; }); } return yield http.get('https://mempool.space/testnet/api/address/' + address + '/utxo').then(response => { if (typeof response === 'object') { var utxos = response; for (var i = 0; i < utxos.length; i++) { var utxo = utxos[i]; utxo.amount = utxo.value; } return utxos; } else { console.log(typeof response, response); throw "Get utxo error"; } }).catch(error => { throw "Network error"; }); })(); }, checkAddressType() { var mainnet = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; var address = arguments.length > 1 ? arguments[1] : undefined; var add = bitcore.Address.fromString(address, mainnet ? 'livenet' : 'testnet'); if (add.isPayToPublicKeyHash()) { return 0; } else if (add.isPayToScriptHash()) { return 1; } else if (add.isPayToWitnessPublicKeyHash() || add.isPayToWitnessScriptHash()) { return 2; } else if (add.isPayToTaproot()) { return 3; } return -1; }, createLegacyTx() { var _arguments4 = arguments, _this = this; return _asyncToGenerator(function* () { var mainnet = _arguments4.length > 0 && _arguments4[0] !== undefined ? _arguments4[0] : false; var pubkeyHex = _arguments4.length > 1 ? _arguments4[1] : undefined; var utxos = _arguments4.length > 2 ? _arguments4[2] : undefined; var receiveAddress = _arguments4.length > 3 ? _arguments4[3] : undefined; var sendAmount = _arguments4.length > 4 ? _arguments4[4] : undefined; var feeRate = _arguments4.length > 5 ? _arguments4[5] : undefined; var opReturnArray = _arguments4.length > 6 ? _arguments4[6] : undefined; var isRBF = _arguments4.length > 7 ? _arguments4[7] : undefined; var { psbt, currentNetwork, spendingUtxos, script } = createSpendingUtxosAndOutput(mainnet, 0, pubkeyHex, utxos, receiveAddress, sendAmount, feeRate, opReturnArray); var _SEQUENCE = bitcoin.Transaction.DEFAULT_SEQUENCE; if (isRBF) _SEQUENCE -= 2; for (var i = 0; i < spendingUtxos.length; i++) { var utxo = spendingUtxos[i]; var txHex = yield _this.getrawtransaction(mainnet, utxo.txid); utxo.txHex = txHex; psbt.addInput({ hash: utxo.txid, index: Number(utxo.vout), nonWitnessUtxo: Buffer.from(utxo.txHex, 'hex'), sequence: _SEQUENCE }); } var psbtHex = psbt.toHex(); return psbtHex; })(); }, createNestedSegwitTx() { var mainnet = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; var pubkeyHex = arguments.length > 1 ? arguments[1] : undefined; var utxos = arguments.length > 2 ? arguments[2] : undefined; var receiveAddress = arguments.length > 3 ? arguments[3] : undefined; var sendAmount = arguments.length > 4 ? arguments[4] : undefined; var feeRate = arguments.length > 5 ? arguments[5] : undefined; var opReturnArray = arguments.length > 6 ? arguments[6] : undefined; var isRBF = arguments.length > 7 ? arguments[7] : undefined; var { psbt, currentNetwork, spendingUtxos, script } = createSpendingUtxosAndOutput(mainnet, 1, pubkeyHex, utxos, receiveAddress, sendAmount, feeRate, opReturnArray); var _SEQUENCE = bitcoin.Transaction.DEFAULT_SEQUENCE; if (isRBF) _SEQUENCE -= 2; for (var i = 0; i < spendingUtxos.length; i++) { var utxo = spendingUtxos[i]; psbt.addInput({ hash: utxo.txid, index: Number(utxo.vout), witnessUtxo: { script: script.output, value: utxo.amount }, redeemScript: script.redeem.output, sequence: _SEQUENCE }); } var psbtHex = psbt.toHex(); return psbtHex; }, createNativeSegwitTx() { var mainnet = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; var pubkeyHex = arguments.length > 1 ? arguments[1] : undefined; var utxos = arguments.length > 2 ? arguments[2] : undefined; var receiveAddress = arguments.length > 3 ? arguments[3] : undefined; var sendAmount = arguments.length > 4 ? arguments[4] : undefined; var feeRate = arguments.length > 5 ? arguments[5] : undefined; var opReturnArray = arguments.length > 6 ? arguments[6] : undefined; var isRBF = arguments.length > 7 ? arguments[7] : undefined; var { psbt, currentNetwork, spendingUtxos, script } = createSpendingUtxosAndOutput(mainnet, 2, pubkeyHex, utxos, receiveAddress, sendAmount, feeRate, opReturnArray); var _SEQUENCE = bitcoin.Transaction.DEFAULT_SEQUENCE; if (isRBF) _SEQUENCE -= 2; for (var i = 0; i < spendingUtxos.length; i++) { var utxo = spendingUtxos[i]; psbt.addInput({ hash: utxo.txid, index: Number(utxo.vout), witnessUtxo: { script: script.output, value: utxo.amount }, sequence: _SEQUENCE }); } var psbtHex = psbt.toHex(); return psbtHex; }, createTaprootTx() { var mainnet = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; var pubkeyHex = arguments.length > 1 ? arguments[1] : undefined; var utxos = arguments.length > 2 ? arguments[2] : undefined; var receiveAddress = arguments.length > 3 ? arguments[3] : undefined; var sendAmount = arguments.length > 4 ? arguments[4] : undefined; var feeRate = arguments.length > 5 ? arguments[5] : undefined; var opReturnArray = arguments.length > 6 ? arguments[6] : undefined; var isRBF = arguments.length > 7 ? arguments[7] : undefined; var { psbt, currentNetwork, spendingUtxos, script } = createSpendingUtxosAndOutput(mainnet, 3, pubkeyHex, utxos, receiveAddress, sendAmount, feeRate, opReturnArray); var _SEQUENCE = bitcoin.Transaction.DEFAULT_SEQUENCE; if (isRBF) _SEQUENCE -= 2; var childNodeXOnlyPubkey = toXOnly(Buffer.from(pubkeyHex, 'hex')); for (var i = 0; i < spendingUtxos.length; i++) { var utxo = spendingUtxos[i]; psbt.addInput({ hash: utxo.txid, index: Number(utxo.vout), witnessUtxo: { script: script.output, value: utxo.amount }, tapInternalKey: childNodeXOnlyPubkey, sequence: _SEQUENCE }); } var psbtHex = psbt.toHex(); return psbtHex; }, calcMultiSignSizeP2WSH(inputNum, outputNum, opReturnBytesLenArray, m, n) { var redeemScriptLength = 1 + n * 33 + 1 + 1; var scriptLength = 1 + m * (1 + 1 + 69 + 1) + encodingLength(redeemScriptLength) + redeemScriptLength; var inputLength = 40 + (scriptLength / 4 + 1); var length; if (!opReturnBytesLenArray || opReturnBytesLenArray.length == 0) { length = 12 + inputLength * inputNum + 43 * (outputNum + 1); } else { var totalOpReturnLen = 0; var arrayLength = opReturnBytesLenArray.length; for (var i = 0; i < arrayLength; i++) { var len = opReturnBytesLenArray[i]; totalOpReturnLen += calcOpReturnLen(len); } length = 12 + inputLength * inputNum + 43 * (outputNum + 1) + totalOpReturnLen; } return length; }, calcTxSizeWithdrawal(utxoSize) { var outputNum = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; var m, n; if (nerve.chainId() == 9) { m = 10, n = 15; } else { m = 2, n = 3; } return nerve.bitcoin.calcMultiSignSizeP2WSH(utxoSize, outputNum, [32], m, n); }, calcFeeWithdrawal(utxos, amount, feeRate) { var splitGranularity = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; if (Array.isArray(utxos)) { utxos.sort((a, b) => { if (a.value !== b.value) { return a.value < b.value ? -1 : 1; } else if (a.txid !== b.txid) { return a.txid < b.txid ? -1 : 1; } else if (a.vout !== b.vout) { return Number(a.vout) < Number(b.vout) ? -1 : 1; } else { return 0; } }); } var _fee = 0, total = 0, totalSpend = 0; var resultList = []; amount = Number(amount); for (var i = 0; i < utxos.length; i++) { var utxo = utxos[i]; total = total + utxo.amount; resultList.push(utxo); if (splitGranularity > 0) { var splitNum = calcSplitNumP2WSH(total, amount, feeRate, splitGranularity, resultList.length); _fee = this.calcTxSizeWithdrawal(resultList.length, splitNum) * feeRate; } else { _fee = this.calcTxSizeWithdrawal(resultList.length) * feeRate; } totalSpend = amount + _fee; if (total >= totalSpend) { break; } } if (total < totalSpend) { throw "not enough fee"; } return _fee; }, isValidBTCAddress() { var mainnet = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; var address = arguments.length > 1 ? arguments[1] : undefined; try { var network = !mainnet ? bitcoin === null || bitcoin === void 0 ? void 0 : bitcoin.networks.testnet : bitcoin === null || bitcoin === void 0 ? void 0 : bitcoin.networks.bitcoin; bitcoin.address.toOutputScript(address, network); return true; } catch (e) { return false; } } }; module.exports = btc;