nerve-sdk-js
Version:
nerve nerve-js nerve-sdk nerve-js-sdk
991 lines (857 loc) • 37.3 kB
JavaScript
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;