UNPKG

@bitcoinerlab/coinselect

Version:

A TypeScript library for Bitcoin transaction management, based on Bitcoin Descriptors for defining inputs and outputs. It facilitates optimal UTXO selection and transaction size calculation.

68 lines (67 loc) 3.73 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.addUntilReach = void 0; const index_1 = require("../index"); const validation_1 = require("../validation"); const vsize_1 = require("../vsize"); const dust_1 = require("../dust"); /** * The `addUntilReach` algorithm is similar to the default {@link coinselect coinselect}. * It continuously adds UTXOs until the combined value surpasses the sum of the targets and fees. * This function does not reorder UTXOs before selection. It evaluates whether creating change * is feasible, with consideration of whether the change exceeds the dust threshold. * * Notes: * * - This function does not reorder UTXOs prior to selection. * - UTXOs that do not provide enough value to cover their respective fee contributions are automatically excluded. * * Refer to {@link coinselect coinselect} for additional details on input parameters and expected returned values. */ function addUntilReach({ utxos, targets, remainder, feeRate, dustRelayFeeRate = index_1.DUST_RELAY_FEE_RATE }) { (0, validation_1.validateOutputWithValues)(utxos); (0, validation_1.validateOutputWithValues)(targets); (0, validation_1.validateDust)(targets); (0, validation_1.validateFeeRate)(feeRate); (0, validation_1.validateFeeRate)(dustRelayFeeRate); const targetsValue = targets.reduce((a, target) => a + target.value, 0); const utxosSoFar = []; for (const candidate of utxos) { const txSizeSoFar = (0, vsize_1.vsize)(utxosSoFar.map(utxo => utxo.output), targets.map(target => target.output)); const utxosSoFarValue = utxosSoFar.reduce((a, utxo) => a + utxo.value, 0); const txFeeSoFar = Math.ceil(txSizeSoFar * feeRate); const txSizeWithCandidate = (0, vsize_1.vsize)([candidate.output, ...utxosSoFar.map(utxo => utxo.output)], targets.map(target => target.output)); const txFeeWithCandidate = Math.ceil(txSizeWithCandidate * feeRate); const candidateFeeContribution = txFeeWithCandidate - txFeeSoFar; if (candidateFeeContribution < 0) throw new Error(`candidateFeeContribution < 0`); // Only consider inputs with more value than the fee they require if (candidate.value > candidateFeeContribution) { if (utxosSoFarValue + candidate.value >= targetsValue + txFeeWithCandidate) { // Evaluate if adding remainder is beneficial const txSizeWithCandidateAndChange = (0, vsize_1.vsize)([candidate.output, ...utxosSoFar.map(utxo => utxo.output)], [remainder, ...targets.map(target => target.output)]); const txFeeWithCandidateAndChange = Math.ceil(txSizeWithCandidateAndChange * feeRate); const remainderValue = utxosSoFarValue + candidate.value - (targetsValue + txFeeWithCandidateAndChange); //return the same reference if nothing changed to interact nicely with //reactive components const utxosResult = [candidate, ...utxosSoFar]; const targetsResult = (0, dust_1.isDust)(remainder, remainderValue, dustRelayFeeRate) ? targets : [...targets, { output: remainder, value: remainderValue }]; return { utxos: utxosResult.length === utxos.length ? utxos : utxosResult, targets: targetsResult, ...(0, validation_1.validatedFeeAndVsize)(utxosResult, targetsResult, feeRate) }; } else { utxosSoFar.push(candidate); } } } return; } exports.addUntilReach = addUntilReach;