@atomiqlabs/sdk-lib
Version:
Basic SDK functionality library for atomiq
120 lines (119 loc) • 4.16 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.utils = exports.DUST_THRESHOLDS = void 0;
const Utils_1 = require("../../utils/Utils");
const logger = (0, Utils_1.getLogger)("CoinSelect: ");
// baseline estimates, used to improve performance
const TX_EMPTY_SIZE = 4 + 1 + 1 + 4;
const TX_INPUT_BASE = 32 + 4 + 1 + 4;
const WITNESS_OVERHEAD = 2 / 4;
const P2WPKH_WITNESS = (1 + 1 + 72 + 1 + 33) / 4;
const P2TR_WITNESS = (1 + 1 + 65) / 4;
const TX_INPUT_PUBKEYHASH = 107;
const TX_INPUT_P2SH_P2WPKH = 23 + P2WPKH_WITNESS + 1;
const TX_INPUT_P2WPKH = 0 + P2WPKH_WITNESS;
const TX_INPUT_P2WSH = 0 + (1 + 1 + 64) / 4;
const TX_INPUT_P2TR = 0 + P2TR_WITNESS;
const TX_OUTPUT_BASE = 8 + 1;
const TX_OUTPUT_PUBKEYHASH = 25;
const TX_OUTPUT_P2SH_P2WPKH = 23;
const TX_OUTPUT_P2WPKH = 22;
const TX_OUTPUT_P2WSH = 34;
const TX_OUTPUT_P2TR = 34;
const INPUT_BYTES = {
"p2sh-p2wpkh": TX_INPUT_P2SH_P2WPKH,
"p2wpkh": TX_INPUT_P2WPKH,
"p2tr": TX_INPUT_P2TR,
"p2pkh": TX_INPUT_PUBKEYHASH,
"p2wsh": TX_INPUT_P2WSH
};
function inputBytes(input) {
return TX_INPUT_BASE + (input.script ? input.script.length : INPUT_BYTES[input.type]);
}
const OUTPUT_BYTES = {
"p2sh-p2wpkh": TX_OUTPUT_P2SH_P2WPKH,
"p2wpkh": TX_OUTPUT_P2WPKH,
"p2tr": TX_OUTPUT_P2TR,
"p2pkh": TX_OUTPUT_PUBKEYHASH,
"p2wsh": TX_OUTPUT_P2WSH
};
function outputBytes(output) {
return TX_OUTPUT_BASE + (output.script ? output.script.length : OUTPUT_BYTES[output.type]);
}
exports.DUST_THRESHOLDS = {
"p2sh-p2wpkh": 540,
"p2wpkh": 294,
"p2tr": 330,
"p2pkh": 546,
"p2wsh": 330
};
function dustThreshold(output) {
return exports.DUST_THRESHOLDS[output.type];
}
function transactionBytes(inputs, outputs, changeType) {
let size = TX_EMPTY_SIZE;
let isSegwit = false;
if (changeType !== "p2pkh") {
size += WITNESS_OVERHEAD;
let isSegwit = true;
}
for (let input of inputs) {
if (!isSegwit && (input.type !== "p2pkh")) {
isSegwit = true;
size += WITNESS_OVERHEAD;
}
size += inputBytes(input);
}
for (let output of outputs) {
size += outputBytes(output);
}
return Math.ceil(size);
}
function uintOrNaN(v) {
if (typeof v !== 'number')
return NaN;
if (!isFinite(v))
return NaN;
if (Math.floor(v) !== v)
return NaN;
if (v < 0)
return NaN;
return v;
}
function sumForgiving(range) {
return range.reduce((a, x) => a + (isFinite(x.value) ? x.value : 0), 0);
}
function sumOrNaN(range) {
return range.reduce((a, x) => a + uintOrNaN(x.value), 0);
}
function finalize(inputs, outputs, feeRate, changeType, cpfpAddFee = 0) {
const bytesAccum = transactionBytes(inputs, outputs, changeType);
logger.debug("finalize(): Transaction bytes: ", bytesAccum);
const feeAfterExtraOutput = (feeRate * (bytesAccum + outputBytes({ type: changeType }))) + cpfpAddFee;
logger.debug("finalize(): TX fee after adding change output: ", feeAfterExtraOutput);
const remainderAfterExtraOutput = Math.floor(sumOrNaN(inputs) - (sumOrNaN(outputs) + feeAfterExtraOutput));
logger.debug("finalize(): Leaves change (changeType=" + changeType + ") value: ", remainderAfterExtraOutput);
// is it worth a change output?
if (remainderAfterExtraOutput >= dustThreshold({ type: changeType })) {
outputs = outputs.concat({ value: remainderAfterExtraOutput, type: changeType });
}
const fee = sumOrNaN(inputs) - sumOrNaN(outputs);
logger.debug("finalize(): Re-calculated total fee: ", fee);
if (!isFinite(fee))
return { fee: (feeRate * bytesAccum) + cpfpAddFee };
return {
inputs: inputs,
outputs: outputs,
fee: fee
};
}
exports.utils = {
dustThreshold: dustThreshold,
finalize: finalize,
inputBytes: inputBytes,
outputBytes: outputBytes,
sumOrNaN: sumOrNaN,
sumForgiving: sumForgiving,
transactionBytes: transactionBytes,
uintOrNaN: uintOrNaN
};