starting prototype for common txn info view
ADD
WalletWasabi.Gui/Controls/WalletExplorer/AddressMoneyTuple.cs
+using NBitcoin;
+using System;
+
+namespace WalletWasabi.Gui.Controls.WalletExplorer
+{
+ public struct AddressMoneyTuple : IEquatable<AddressMoneyTuple>
+ {
+ public AddressMoneyTuple(string address, Money amount)
+ {
+ Address = address;
+ Amount = amount;
+ IsEmpty = false;
+ }
+
+ private AddressMoneyTuple(bool isEmpty)
+ {
+ Address = null;
+ Amount = null;
+ IsEmpty = isEmpty;
+ }
+
+ public static AddressMoneyTuple Empty { get; } = new AddressMoneyTuple(true);
+
+ public string Address { get; }
+ public Money Amount { get; }
+ private bool IsEmpty { get; set; }
+
+ public static bool operator ==(AddressMoneyTuple x, AddressMoneyTuple y) => x.Equals(y);
+ public static bool operator !=(AddressMoneyTuple x, AddressMoneyTuple y) => !(x == y);
+
+
+ public bool Equals(AddressMoneyTuple other)
+ {
+ return this.IsEmpty == other.IsEmpty
+ && this.Amount == other.Amount
+ && this.Address == other.Address;
+ }
+ }
+}

ADD
WalletWasabi.Gui/Controls/WalletExplorer/BuildTransactionDetails.xaml.cs
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Data;
+using Avalonia.Markup.Xaml;
+using ReactiveUI;
+using System.Reactive.Linq;
+
+namespace WalletWasabi.Gui.Controls.WalletExplorer
+{
+ public class BuildTransactionDetails : UserControl
+ {
+ public BuildTransactionDetails()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+ }
+}

MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/TransactionInfo.cs
WalletWasabi.Gui/Controls/WalletExplorer/TransactionInfo.cs
+using System.Transactions;
using ReactiveUI;
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
using System.Text;
+using WalletWasabi.Blockchain.TransactionBuilding;
+using NBitcoin;
namespace WalletWasabi.Gui.Controls.WalletExplorer
{
@@ -14,6 +19,10 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
private string _label;
private int _blockHeight;
private string _transactionId;
+ private IList<AddressMoneyTuple> _inputsInfo;
+ private IList<AddressMoneyTuple> _outputsInfo;
+ private string _totalInputValue;
+ private string _totalOutputValue;
public DateTimeOffset DateTime
{
@@ -56,5 +65,69 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
get => _transactionId;
set => this.RaiseAndSetIfChanged(ref _transactionId, value);
}
+
+ public IList<AddressMoneyTuple> InputsInfo
+ {
+ get => _inputsInfo;
+ set => this.RaiseAndSetIfChanged(ref _inputsInfo, value);
+ }
+
+ public IList<AddressMoneyTuple> OutputsInfo
+ {
+ get => _outputsInfo;
+ set => this.RaiseAndSetIfChanged(ref _outputsInfo, value);
+ }
+
+ public string TotalInputValue
+ {
+ get => _totalInputValue;
+ set => this.RaiseAndSetIfChanged(ref _totalInputValue, value);
+ }
+
+ public string TotalOutputValue
+ {
+ get => _totalOutputValue;
+ set => this.RaiseAndSetIfChanged(ref _totalOutputValue, value);
+ }
+
+ public static TransactionInfo FromBuildTxnResult(BuildTransactionResult result)
+ {
+ var inputAddrMoney = result.Transaction.Transaction.Inputs.Select(x =>
+ {
+ if (result.Store.TransactionStore.TryGetTransaction(x.PrevOut.Hash, out var prevTxn))
+ {
+ var targetTxO = prevTxn.Transaction.Outputs[x.PrevOut.N];
+ var address = targetTxO.ScriptPubKey.GetDestinationAddress(result.Network).ToString();
+ var money = targetTxO.Value;
+
+ return new AddressMoneyTuple(address, money);
+ }
+ else
+ {
+ return AddressMoneyTuple.Empty;
+ }
+ }).Where(x => x != AddressMoneyTuple.Empty).ToList();
+
+ var outputAddrMoney = result.Transaction.Transaction.Outputs.Select(targetTxO =>
+ {
+ var address = targetTxO.ScriptPubKey.GetDestinationAddress(result.Network).ToString();
+ var money = targetTxO.Value;
+ return new AddressMoneyTuple(address, money);
+ }).ToList();
+
+ var totalInValue = inputAddrMoney.Select(x => x.Amount).Sum().ToString();
+ var totalOutValue = outputAddrMoney.Select(x => x.Amount).Sum().ToString();
+
+ return new TransactionInfo()
+ {
+ TransactionId = result.Transaction.GetHash().ToString(),
+ Confirmed = false,
+ DateTime = result.Transaction.FirstSeen,
+ InputsInfo = inputAddrMoney,
+ OutputsInfo = outputAddrMoney,
+ TotalInputValue = totalInValue,
+ TotalOutputValue = totalOutValue
+ };
+ }
}
}

MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/TransactionViewerView.xaml
WalletWasabi.Gui/Controls/WalletExplorer/TransactionViewerView.xaml
<Button Command="{Binding OpenTransactionBroadcaster}" Content="Open Transaction Broadcaster" />
</StackPanel>
</Panel>
- <Grid ScrollViewer.VerticalScrollBarVisibility="Auto" RowDefinitions="26,1*">
- <StackPanel Grid.Row="0" Orientation="Horizontal" Spacing="5">
- <TextBlock Text="Transaction ID:" FontWeight="Bold" />
- <controls:ExtendedTextBox Text="{Binding TxId, ConverterParameter=50, Converter={StaticResource LurkingWifeModeStringConverter}, Mode=OneWay}" Classes="selectableTextBlock" />
- </StackPanel>
- <DockPanel LastChildFill="True" Grid.Row="1" Margin="0 8 0 0">
- <StackPanel DockPanel.Dock="Top" Orientation="Vertical" Margin="0 8 0 0" Spacing="5">
- <TextBlock IsVisible="{Binding IsPsbtJsonTextTruncated}" Text="The JSON text in this preview has been truncated due to its large size." FontWeight="Bold" Classes="warningMessage" />
- <TextBlock Text="PSBT JSON Preview:" FontWeight="Bold" />
- </StackPanel>
- <controls:MultiTextBox Margin="0 8 0 0" Text="{Binding TruncatedPsbtJsonText, ConverterParameter=1536, Converter={StaticResource LurkingWifeModeStringConverter}, Mode=OneWay}" IsSelectable="False" IsReadOnly="True" TextWrapping="Wrap" AcceptsReturn="True" />
- </DockPanel>
+ <Grid ScrollViewer.VerticalScrollBarVisibility="Auto">
+ <local:BuildTransactionDetails Grid.Row="1" DataContext="{Binding TransactionInfo}" DockPanel.Dock="Top" Margin="0 8 0 0"/>
</Grid>
</DockPanel>
</Grid>

MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/TransactionViewerViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/TransactionViewerViewModel.cs
@@ -31,7 +31,8 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
private string _psbtHexText;
private string _psbtBase64Text;
private byte[] _psbtBytes;
- private bool _IsPsbtJsonTextTruncated;
+ private bool _isPsbtJsonTextTruncated;
+ private TransactionInfo _transactionInfo;
public TransactionViewerViewModel() : base("Transaction")
{
@@ -149,8 +150,14 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
public bool IsPsbtJsonTextTruncated
{
- get => _IsPsbtJsonTextTruncated;
- set => this.RaiseAndSetIfChanged(ref _IsPsbtJsonTextTruncated, value);
+ get => _isPsbtJsonTextTruncated;
+ set => this.RaiseAndSetIfChanged(ref _isPsbtJsonTextTruncated, value);
+ }
+
+ public TransactionInfo TransactionInfo
+ {
+ get => _transactionInfo;
+ set => this.RaiseAndSetIfChanged(ref _transactionInfo, value);
}
public override void OnOpen(CompositeDisposable disposables)
@@ -174,6 +181,8 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
{
try
{
+ TransactionInfo = TransactionInfo.FromBuildTxnResult(result);
+
TxId = result.Transaction.GetHash().ToString();
PsbtJsonText = result.Psbt.ToString();

MODIFY
WalletWasabi.Gui/Converters/MoneyBrushConverter.cs
WalletWasabi.Gui/Converters/MoneyBrushConverter.cs
@@ -20,6 +20,11 @@ namespace WalletWasabi.Gui.Converters
var money = decimal.Parse(moneyString);
return money < 0 ? Brushes.IndianRed : Brushes.MediumSeaGreen;
}
+ else if (value is Money inMoney)
+ {
+ var money = decimal.Parse(inMoney.ToString());
+ return money < 0 ? Brushes.IndianRed : Brushes.MediumSeaGreen;
+ }
else
{
throw new TypeArgumentException(value, typeof(string), nameof(value));

MODIFY
WalletWasabi/Blockchain/TransactionBuilding/BuildTransactionResult.cs
WalletWasabi/Blockchain/TransactionBuilding/BuildTransactionResult.cs
@@ -3,13 +3,16 @@ using System.Collections.Generic;
using WalletWasabi.Blockchain.TransactionOutputs;
using WalletWasabi.Blockchain.Transactions;
using WalletWasabi.Helpers;
+using WalletWasabi.Stores;
namespace WalletWasabi.Blockchain.TransactionBuilding
{
public class BuildTransactionResult
{
- public BuildTransactionResult(SmartTransaction transaction, PSBT psbt, bool spendsUnconfirmed, bool signed, Money fee, decimal feePercentOfSent, IEnumerable<SmartCoin> outerWalletOutputs, IEnumerable<SmartCoin> innerWalletOutputs, IEnumerable<SmartCoin> spentCoins)
+ public BuildTransactionResult(BitcoinStore store, Network network, SmartTransaction transaction, PSBT psbt, bool spendsUnconfirmed, bool signed, Money fee, decimal feePercentOfSent, IEnumerable<SmartCoin> outerWalletOutputs, IEnumerable<SmartCoin> innerWalletOutputs, IEnumerable<SmartCoin> spentCoins)
{
+ Store = Guard.NotNull(nameof(store), store);
+ Network = Guard.NotNull(nameof(network), network);
Transaction = Guard.NotNull(nameof(transaction), transaction);
Psbt = Guard.NotNull(nameof(psbt), psbt);
SpendsUnconfirmed = spendsUnconfirmed;
@@ -20,7 +23,8 @@ namespace WalletWasabi.Blockchain.TransactionBuilding
InnerWalletOutputs = innerWalletOutputs ?? new List<SmartCoin>();
SpentCoins = Guard.NotNullOrEmpty(nameof(spentCoins), spentCoins);
}
-
+ public BitcoinStore Store { get; }
+ public Network Network { get; }
public SmartTransaction Transaction { get; }
public PSBT Psbt { get; }
public bool SpendsUnconfirmed { get; }

MODIFY
WalletWasabi/Blockchain/Transactions/TransactionFactory.cs
WalletWasabi/Blockchain/Transactions/TransactionFactory.cs
@@ -307,7 +307,7 @@ namespace WalletWasabi.Blockchain.Transactions
Logger.LogInfo($"Transaction is successfully built: {tx.GetHash()}.");
var sign = !KeyManager.IsWatchOnly;
var spendsUnconfirmed = spentCoins.Any(c => !c.Confirmed);
- return new BuildTransactionResult(new SmartTransaction(tx, Height.Unknown), psbt, spendsUnconfirmed, sign, fee, feePc, outerWalletOutputs, innerWalletOutputs, spentCoins);
+ return new BuildTransactionResult(Store, Network, new SmartTransaction(tx, Height.Unknown), psbt, spendsUnconfirmed, sign, fee, feePc, outerWalletOutputs, innerWalletOutputs, spentCoins);
}
private PSBT TryNegotiatePayjoin(IPayjoinClient payjoinClient, TransactionBuilder builder, PSBT psbt)

starting prototype for common txn info view