feat(wallet): surface immature balance separately from confirmed

Extract confirmation-threshold logic into UtxoSpendability (shared by
TransactionFactory and WalletSynchronizer) and use it to compute
ImmatureSats, so coinbase/under-confirmed amounts are shown separately
from spendable balance in the GUI and CLI instead of being lumped into
"confirmed".
This commit is contained in:
2026-07-02 18:24:26 +02:00
parent 9aecdb1aaa
commit 6c24a8bb46
9 changed files with 66 additions and 24 deletions
@@ -25,6 +25,9 @@ public partial class MainWindowViewModel
[ObservableProperty]
private string unconfirmedText = "";
[ObservableProperty]
private string immatureText = "";
[ObservableProperty]
private string networkInfo = "";
@@ -249,6 +252,7 @@ public partial class MainWindowViewModel
{
BalanceText = $"0.00000000 {Profile.CoinUnit}";
UnconfirmedText = "";
ImmatureText = "";
ReceiveAddress = _account.GetReceiveAddress(0).ToString();
History.Clear();
Addresses.Clear();
@@ -261,11 +265,14 @@ public partial class MainWindowViewModel
$"m/{_doc!.AccountPath}/0/{i}"));
return;
}
BalanceText = Fmt(cache.ConfirmedSats);
BalanceText = Fmt(cache.ConfirmedSats - cache.ImmatureSats);
var pending = cache.History.Where(t => t.Height <= 0).Sum(t => t.DeltaSats);
UnconfirmedText = pending != 0
? $"{Loc.Tr("msg.pending")}: {(pending > 0 ? "+" : "")}{Fmt(pending)} — {Loc.Tr("msg.notspendable")}"
: "";
ImmatureText = cache.ImmatureSats != 0
? $"{Loc.Tr("msg.immature")}: {Fmt(cache.ImmatureSats)} — {Loc.Tr("msg.notspendable")}"
: "";
ReceiveAddress = _account.GetReceiveAddress(cache.NextReceiveIndex).ToString();
History.Clear();
foreach (var tx in cache.History)