[BREAKGLASS] An unspent transaction output (UTXO) selection module for bitcoin.
Go to file
Ivan Vershigora 35f803875f
Some checks failed
Run Tests / unit (14) (push) Has been cancelled
Run Tests / unit (lts/*) (push) Has been cancelled
Run Tests / coverage (14) (push) Has been cancelled
Run Tests / coverage (lts/*) (push) Has been cancelled
Run Tests / lint (14) (push) Has been cancelled
Run Tests / lint (lts/*) (push) Has been cancelled
fix: decimal fee
2025-04-05 14:55:45 +01:00
.github/workflows Use Github Actions 2022-06-14 08:01:55 +09:00
stats Fix standard issues and npm audit 2020-01-07 16:44:58 +09:00
test fix: decimal fee 2025-04-05 14:55:45 +01:00
.gitignore add coverage to gitignore 2016-10-13 12:16:44 +11:00
accumulative.js feat: decimal fee rate 2025-03-30 11:49:27 +01:00
blackjack.js feat: decimal fee rate 2025-03-30 11:49:27 +01:00
break.js feat: decimal fee rate 2025-03-30 11:49:27 +01:00
index.d.ts feat: target value and selected inputs ouputs are optional 2023-08-10 19:09:09 +09:00
index.js add example strategy that orders by addresses 2016-11-07 15:34:55 +11:00
LICENSE change back to MIT 2018-09-11 15:41:22 +10:00
package-lock.json feat: decimal fee rate 2025-03-30 11:49:27 +01:00
package.json chore: add types property 2023-08-12 10:12:05 +09:00
README.md Update README to use PSBT 2020-01-07 15:55:40 +09:00
split.js feat: decimal fee rate 2025-03-30 11:49:27 +01:00
utils.js fix: decimal fee 2025-04-05 14:55:45 +01:00

coinselect

TRAVIS NPM

js-standard-style

An unspent transaction output (UTXO) selection module for bitcoin.

WARNING: Value units are in satoshis, not Bitcoin.

Algorithms

Module Algorithm Re-orders UTXOs?
require('coinselect') Blackjack, with Accumulative fallback By Descending Value
require('coinselect/accumulative') Accumulative - accumulates inputs until the target value (+fees) is reached, skipping detrimental inputs -
require('coinselect/blackjack') Blackjack - accumulates inputs until the target value (+fees) is matched, does not accumulate inputs that go over the target value (within a threshold) -
require('coinselect/break') Break - breaks the input values into equal denominations of output (as provided) -
require('coinselect/split') Split - splits the input values evenly between all outputs, any provided output with .value remains unchanged -

Note: Each algorithm will add a change output if the input - output - fee value difference is over a dust threshold. This is calculated independently by utils.finalize, irrespective of the algorithm chosen, for the purposes of safety.

Pro-tip: if you want to send-all inputs to an output address, coinselect/split with a partial output (.address defined, no .value) can be used to send-all, while leaving an appropriate amount for the fee.

Example

let coinSelect = require('coinselect')
let feeRate = 55 // satoshis per byte
let utxos = [
  ...,
  {
    txId: '...',
    vout: 0,
    ...,
    value: 10000,
    // For use with PSBT:
    // not needed for coinSelect, but will be passed on to inputs later
    nonWitnessUtxo: Buffer.from('...full raw hex of txId tx...', 'hex'),
    // OR
    // if your utxo is a segwit output, you can use witnessUtxo instead
    witnessUtxo: {
      script: Buffer.from('... scriptPubkey hex...', 'hex'),
      value: 10000 // 0.0001 BTC and is the exact same as the value above
    }
  }
]
let targets = [
  ...,
  {
    address: '1EHNa6Q4Jz2uvNExL497mE43ikXhwF6kZm',
    value: 5000
  }
]

// ...
let { inputs, outputs, fee } = coinSelect(utxos, targets, feeRate)

// the accumulated fee is always returned for analysis
console.log(fee)

// .inputs and .outputs will be undefined if no solution was found
if (!inputs || !outputs) return

let psbt = new bitcoin.Psbt()

inputs.forEach(input =>
  psbt.addInput({
    hash: input.txId,
    index: input.vout,
    nonWitnessUtxo: input.nonWitnessUtxo,
    // OR (not both)
    witnessUtxo: input.witnessUtxo,
  })
)
outputs.forEach(output => {
  // watch out, outputs may have been added that you need to provide
  // an output address/script for
  if (!output.address) {
    output.address = wallet.getChangeAddress()
    wallet.nextChangeAddress()
  }

  psbt.addOutput({
    address: output.address,
    value: output.value,
  })
})

License MIT