1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//! Shared high-value-first coin selection with a coin-count cap (epic #410 / #413).
//!
//! Every browser/JS spend creator (hub, extension, dig-sdk) needs the SAME selection policy so a
//! wallet moves value consistently and a "too many coins" condition is signalled — not hidden behind
//! a generic insufficient-funds error. This is that one policy, implemented once here (released via
//! the chip35 wasm) and byte-mirrored by the native Rust layer (`dig-l1-wallet`, T0b/#414).
//!
//! ## Policy
//! Sort the available coins DESCENDING by amount (tie-break by coin id, ascending, for determinism),
//! then greedily accumulate largest-first until the running total reaches `target`. Only a selection
//! that fits within `cap` coins is accepted. The outcome is one of three DISTINCT results
//! ([`SelectCoinsResult`]):
//! - [`SelectCoinsResult::Ok`] — a selection within `cap` covers the target.
//! - [`SelectCoinsResult::NeedsConsolidation`] — enough total value EXISTS, but reaching the target
//! needs more than `cap` coins. The caller consolidates (merge coins → one, see
//! [`crate::build_coin_consolidation`] / [`crate::build_cat_consolidation`]) then re-selects.
//! - [`SelectCoinsResult::InsufficientFunds`] — the total value is below the target regardless of the
//! cap. This is genuinely not-enough-money, kept separate from the consolidation signal.
//!
//! Boundary discipline: pure. No keys, no networking, no on-chain reads. Works for XCH and for any
//! single CAT tail (the caller passes the CAT coins' underlying [`Coin`]s).
use Coin;
/// The default maximum number of coins a single selection (and thus a single spend bundle) will use.
/// Chosen to keep a spend bundle comfortably within block/mempool cost limits; a caller may override
/// it per selection.
pub const DEFAULT_COIN_CAP: usize = 50;
/// A successful selection: the chosen coins (descending by amount, tie-broken by coin id) plus the
/// selected total and the change over `target`. `coins[0]` is the largest — the natural lead coin for
/// the builders, which spend element 0 as the lead.
/// The discriminated outcome of [`select_coins`]. `NeedsConsolidation` is deliberately distinct from
/// `InsufficientFunds`: the former means "the money is here but too fragmented", the latter means
/// "not enough money". Every failure carries the counts the caller needs to drive a consolidation
/// loop or an honest error.
/// Select coins high-value-first to cover `target`, using at most `cap` coins.
///
/// See the [module docs](self) for the policy and the three-way result. Deterministic for a given
/// input set + `target` + `cap`. Pure — no keys, no networking.