Line data Source code
1 : library coin_selection;
2 :
3 : import 'dart:math';
4 :
5 : import 'package:walletkit_dart/walletkit_dart.dart';
6 :
7 : const kMinInputs = 2;
8 :
9 1 : List<ElectrumOutput> singleRandomDrawUTXOSelection(
10 : List<ElectrumOutput> utxos,
11 : BigInt targetAmount,
12 : ) {
13 1 : List<ElectrumOutput> selectedUTXOs = [];
14 1 : BigInt selectedAmount = BigInt.zero;
15 1 : Random random = Random();
16 :
17 : // try to use at least 2 UTXOs if possible
18 3 : while (selectedAmount < targetAmount || (selectedUTXOs.length < kMinInputs && utxos.isNotEmpty)) {
19 1 : if (utxos.isEmpty) {
20 0 : throw SendFailure('Insufficient UTXOs to reach the target amount');
21 : }
22 :
23 : // Randomly select a UTXO and remove it from the available list.
24 2 : int randomIndex = random.nextInt(utxos.length);
25 1 : ElectrumOutput selectedUTXO = utxos.removeAt(randomIndex);
26 :
27 : // Add the chosen UTXO to the selected list and update the selected amount.
28 1 : selectedUTXOs.add(selectedUTXO);
29 2 : selectedAmount += selectedUTXO.value;
30 : }
31 : return selectedUTXOs;
32 : }
33 :
34 1 : List<ElectrumOutput> smallestFirstUTXOSelection(
35 : List<ElectrumOutput> utxos,
36 : BigInt targetAmount,
37 : ) {
38 : // Sort UTXOs by value, smallest first.
39 5 : utxos.sort((a, b) => a.value.compareTo(b.value));
40 :
41 1 : List<ElectrumOutput> selectedUTXOs = [];
42 1 : BigInt selectedAmount = BigInt.zero;
43 :
44 2 : for (ElectrumOutput utxo in utxos) {
45 1 : selectedUTXOs.add(utxo);
46 2 : selectedAmount += utxo.value;
47 :
48 1 : if (selectedAmount >= targetAmount) {
49 : break;
50 : }
51 : }
52 :
53 1 : if (selectedAmount < targetAmount) {
54 0 : throw Exception('Insufficient UTXOs to reach the target amount');
55 : }
56 :
57 : return selectedUTXOs;
58 : }
59 :
60 : ///
61 : /// FIFO: TODO: Get blockheigt information for each Input
62 : ///
63 : // List<Input> fifoUTXOSelection(List<Input> utxos, int targetAmount) {
64 : // // Sort UTXOs by block height, older first.
65 : // utxos.sort((a, b) => a.blockHeight.compareTo(b.blockHeight));
66 :
67 : // List<Input> selectedUTXOs = [];
68 : // int selectedAmount = 0;
69 :
70 : // for (Input utxo in utxos) {
71 : // selectedUTXOs.add(utxo);
72 : // selectedAmount += utxo.vout;
73 :
74 : // if (selectedAmount >= targetAmount) {
75 : // break;
76 : // }
77 : // }
78 :
79 : // if (selectedAmount < targetAmount) {
80 : // throw Exception('Insufficient UTXOs to reach the target amount');
81 : // }
82 :
83 : // return selectedUTXOs;
84 : // }
85 :
86 : ///
87 : ///
88 : ///
89 0 : List<ElectrumOutput> fillUpToTargetAmount(
90 : List<ElectrumOutput> alreadyUsedUTXOs,
91 : List<ElectrumOutput> utxos,
92 : BigInt targetAmount,
93 : ) {
94 0 : List<ElectrumOutput> selectedUTXOs = [...alreadyUsedUTXOs];
95 0 : BigInt selectedAmount = selectedUTXOs.fold(
96 0 : BigInt.zero,
97 0 : (preV, utxo) => preV + utxo.value,
98 : );
99 :
100 : /// Remove already used UTXOs from the available list.
101 0 : utxos.removeWhere((element) => alreadyUsedUTXOs.contains(element));
102 :
103 : // try to use at least 2 UTXOs if possible
104 0 : while (selectedAmount < targetAmount || (selectedUTXOs.length < kMinInputs && utxos.isNotEmpty)) {
105 0 : if (utxos.isEmpty) {
106 0 : throw SendFailure(
107 0 : 'Insufficient UTXOs for Fee + Target Amount ($targetAmount)',
108 : );
109 : }
110 :
111 0 : final max = utxos.reduce(
112 0 : (value, element) => value.value > element.value ? value : element,
113 : );
114 :
115 0 : utxos.remove(max);
116 :
117 : // Add the chosen UTXO to the selected list and update the selected amount.
118 0 : selectedUTXOs.add(max);
119 0 : selectedAmount += max.value;
120 : }
121 : return selectedUTXOs;
122 : }
|