private async Task OnConfirmAsync(BuildTransactionResult transaction) { var transactionAuthorizationInfo = new TransactionAuthorizationInfo(transaction); var authResult = await AuthorizeAsync(transactionAuthorizationInfo); if (authResult) { IsBusy = true; try { var finalTransaction = await GetFinalTransactionAsync(transactionAuthorizationInfo.Transaction, _info); await SendTransactionAsync(finalTransaction); Navigate().To(new SendSuccessViewModel(_wallet, finalTransaction)); } catch (Exception ex) { await ShowErrorAsync("Transaction", ex.ToUserFriendlyString(), "Wasabi was unable to send your transaction."); } IsBusy = false; } }
public static async IAsyncEnumerable <ChangeAvoidanceSuggestionViewModel> GenerateSuggestionsAsync( TransactionInfo transactionInfo, BitcoinAddress destination, Wallet wallet, [EnumeratorCancellation] CancellationToken cancellationToken) { var selections = ChangelessTransactionCoinSelector.GetAllStrategyResultsAsync( transactionInfo.Coins, transactionInfo.FeeRate, new TxOut(transactionInfo.Amount, destination), cancellationToken).ConfigureAwait(false); await foreach (var selection in selections) { if (selection.Any()) { BuildTransactionResult transaction = TransactionHelpers.BuildChangelessTransaction( wallet, destination, transactionInfo.UserLabels, transactionInfo.FeeRate, selection, tryToSign: false); yield return(new ChangeAvoidanceSuggestionViewModel( transactionInfo.Amount.ToDecimal(MoneyUnit.BTC), transaction, wallet.Synchronizer.UsdExchangeRate)); } } }
public ChangeAvoidanceSuggestionViewModel(decimal originalAmount, BuildTransactionResult transactionResult, decimal fiatExchangeRate, bool isOriginal) { TransactionResult = transactionResult; decimal total = transactionResult.CalculateDestinationAmount().ToDecimal(MoneyUnit.BTC); _amountFiat = total.GenerateFiatText(fiatExchangeRate, "USD"); if (!isOriginal) { var fiatTotal = total * fiatExchangeRate; var fiatOriginal = originalAmount * fiatExchangeRate; var fiatDifference = fiatTotal - fiatOriginal; _differenceFiat = (fiatDifference > 0 ? $"{fiatDifference.GenerateFiatText("USD")} More" : $"{Math.Abs(fiatDifference).GenerateFiatText("USD")} Less") .Replace("(", "").Replace(")", ""); } _amount = $"{total} BTC"; }
public InsufficientBalanceDialogViewModel(BalanceType type, BuildTransactionResult transaction, decimal usdExchangeRate) { var destinationAmount = transaction.CalculateDestinationAmount().ToDecimal(MoneyUnit.BTC); var btcAmountText = $"{destinationAmount} bitcoins "; var fiatAmountText = destinationAmount.GenerateFiatText(usdExchangeRate, "USD"); AmountText = $"{btcAmountText}{fiatAmountText}"; var fee = transaction.Fee; var btcFeeText = $"{fee.ToDecimal(MoneyUnit.Satoshi)} satoshis "; var fiatFeeText = fee.ToDecimal(MoneyUnit.BTC).GenerateFiatText(usdExchangeRate, "USD"); FeeText = $"{btcFeeText}{fiatFeeText}"; switch (type) { case BalanceType.Private: Caption = $"There are not enough private funds to cover the transaction fee. Alternatively you could:"; break; case BalanceType.Pocket: Caption = $"There are not enough funds selected to cover the transaction fee. Alternatively you could:"; break; default: Caption = $"There are not enough funds available to cover the transaction fee. Alternatively you could:"; break; } NextCommand = ReactiveCommand.Create(() => Close(result: true)); CancelCommand = ReactiveCommand.Create(() => Close(DialogResultKind.Cancel)); SetupCancel(enableCancel: false, enableCancelOnEscape: true, enableCancelOnPressed: true); }
public TransactionPreviewViewModel(Wallet wallet, TransactionInfo info, BuildTransactionResult transaction) { _wallet = wallet; _labels = SmartLabel.Empty; _info = info; SetupCancel(enableCancel: true, enableCancelOnEscape: true, enableCancelOnPressed: false); EnableBack = true; _confirmationTimeText = ""; var destinationAmount = transaction.CalculateDestinationAmount().ToDecimal(MoneyUnit.BTC); var btcAmountText = $"{destinationAmount} bitcoins "; var fiatAmountText = destinationAmount.GenerateFiatText(_wallet.Synchronizer.UsdExchangeRate, "USD"); AmountText = $"{btcAmountText}{fiatAmountText}"; AddressText = info.Address.ToString(); var fee = transaction.Fee; var btcFeeText = $"{fee.ToDecimal(MoneyUnit.Satoshi)} satoshis "; var fiatFeeText = fee.ToDecimal(MoneyUnit.BTC).GenerateFiatText(_wallet.Synchronizer.UsdExchangeRate, "USD"); FeeText = $"{btcFeeText}{fiatFeeText}"; PayJoinUrl = info.PayJoinClient?.PaymentUrl.AbsoluteUri; IsPayJoin = PayJoinUrl is not null; NextCommand = ReactiveCommand.CreateFromTask(async() => await OnNextAsync(transaction)); }
public static async IAsyncEnumerable <ChangeAvoidanceSuggestionViewModel> GenerateSuggestionsAsync( TransactionInfo transactionInfo, BitcoinAddress destination, Wallet wallet, [EnumeratorCancellation] CancellationToken cancellationToken) { Task <ChangeAvoidanceSuggestionViewModel?> bnbSuggestionTask = Task.Run(() => { if (ChangelessTransactionCoinSelector.TryGetCoins(transactionInfo.Coins, transactionInfo.FeeRate, new TxOut(transactionInfo.Amount, destination), out IEnumerable <SmartCoin>?selection, cancellationToken)) { BuildTransactionResult transaction = TransactionHelpers.BuildChangelessTransaction( wallet, destination, transactionInfo.UserLabels, transactionInfo.FeeRate, selection, tryToSign: false); return(new ChangeAvoidanceSuggestionViewModel( transactionInfo.Amount.ToDecimal(MoneyUnit.BTC), transaction, wallet.Synchronizer.UsdExchangeRate, isOriginal: false)); } return(null); }); ChangeAvoidanceSuggestionViewModel?bnbSuggestion = await bnbSuggestionTask; if (bnbSuggestion is not null) { yield return(bnbSuggestion); } }
public PrivacySuggestionControlViewModel(decimal originalAmount, BuildTransactionResult transactionResult, PrivacyOptimisationLevel optimisationLevel, decimal fiatExchangeRate, params string[] benefits) { TransactionResult = transactionResult; _optimisationLevel = optimisationLevel; _benefits = benefits; decimal total = transactionResult.CalculateDestinationAmount().ToDecimal(MoneyUnit.BTC); var fiatTotal = total * fiatExchangeRate; _amountFiat = total.GenerateFiatText(fiatExchangeRate, "USD"); if (optimisationLevel == PrivacyOptimisationLevel.Better) { var fiatOriginal = originalAmount * fiatExchangeRate; var fiatDifference = fiatTotal - fiatOriginal; _caption = (fiatDifference > 0 ? $"{fiatDifference.GenerateFiatText("USD")} More" : $"{Math.Abs(fiatDifference).GenerateFiatText("USD")} Less") .Replace("(", "").Replace(")", ""); } else { _caption = "As Requested"; } _amount = $"{total}"; }
public void UpdateTransaction(BuildTransactionResult transactionResult, TransactionInfo info) { _transaction = transactionResult; ConfirmationTimeText = $"Approximately {TextHelpers.TimeSpanToFriendlyString(info.ConfirmationTimeSpan)} "; var destinationAmount = _transaction.CalculateDestinationAmount(); var btcAmountText = $"{destinationAmount.ToFormattedString()} BTC"; var fiatAmountText = destinationAmount.ToDecimal(MoneyUnit.BTC).GenerateFiatText(_wallet.Synchronizer.UsdExchangeRate, "USD"); AmountText = $"{btcAmountText} {fiatAmountText}"; var fee = _transaction.Fee; var feeText = fee.ToFeeDisplayUnitString(); var fiatFeeText = fee.ToDecimal(MoneyUnit.BTC).GenerateFiatText(_wallet.Synchronizer.UsdExchangeRate, "USD"); FeeText = $"{feeText} {fiatFeeText}"; TransactionHasChange = _transaction.InnerWalletOutputs.Any(x => x.ScriptPubKey != _address.ScriptPubKey); TransactionHasPockets = !info.IsPrivate; Labels = SmartLabel.Merge(transactionResult.SpentCoins.Select(x => x.GetLabels(info.PrivateCoinThreshold))); var exactPocketUsed = Labels.Count() == info.UserLabels.Count() && Labels.All(label => info.UserLabels.Contains(label, StringComparer.OrdinalIgnoreCase)); TransactionHasPockets = Labels.Any() && !exactPocketUsed; IsCustomFeeUsed = info.IsCustomFeeUsed; IsOtherPocketSelectionPossible = info.IsOtherPocketSelectionPossible; }
public InsufficientBalanceDialogViewModel(BalanceType type, BuildTransactionResult transaction, decimal usdExchangeRate) { var destinationAmount = transaction.CalculateDestinationAmount().ToDecimal(MoneyUnit.BTC); var fee = transaction.Fee; BtcAmountText = $"{destinationAmount} bitcoins "; FiatAmountText = $"(≈{(destinationAmount * usdExchangeRate).FormattedFiat()} USD) "; BtcFeeText = $"{fee.ToDecimal(MoneyUnit.Satoshi)} satoshis "; FiatFeeText = $"(≈{(fee.ToDecimal(MoneyUnit.BTC) * usdExchangeRate).FormattedFiat()} USD)"; switch (type) { case BalanceType.Private: Caption = $"There are not enough private funds to cover the transaction fee. Alternatively you could:"; break; case BalanceType.Pocket: Caption = $"There are not enough funds selected to cover the transaction fee. Alternatively you could:"; break; default: Caption = $"There are not enough funds available to cover the transaction fee. Alternatively you could:"; break; } NextCommand = ReactiveCommand.Create(() => Close(result: true)); CancelCommand = ReactiveCommand.Create(() => Close(DialogResultKind.Cancel)); }
public void UpdateTransaction(BuildTransactionResult transactionResult) { _transaction = transactionResult; ConfirmationTimeText = $"Approximately {TextHelpers.TimeSpanToFriendlyString(_info.ConfirmationTimeSpan)} "; var destinationAmount = _transaction.CalculateDestinationAmount().ToDecimal(MoneyUnit.BTC); var btcAmountText = $"{destinationAmount} BTC "; var fiatAmountText = destinationAmount.GenerateFiatText(_wallet.Synchronizer.UsdExchangeRate, "USD"); AmountText = $"{btcAmountText}{fiatAmountText}"; var fee = _transaction.Fee; var feeText = fee.ToFeeDisplayFormatString(); var fiatFeeText = fee.ToDecimal(MoneyUnit.BTC) .GenerateFiatText(_wallet.Synchronizer.UsdExchangeRate, "USD"); FeeText = $"{feeText}{fiatFeeText}"; TransactionHasChange = _transaction.InnerWalletOutputs.Any(x => x.ScriptPubKey != _address.ScriptPubKey); TransactionHasPockets = !_info.IsPrivate; IsCustomFeeUsed = _info.IsCustomFeeUsed; }
public TransactionPreviewViewModel(Wallet wallet, TransactionInfo info, TransactionBroadcaster broadcaster, BuildTransactionResult transaction) { EnableCancel = true; EnableBack = true; var destinationAmount = transaction.CalculateDestinationAmount().ToDecimal(MoneyUnit.BTC); var btcAmountText = $"{destinationAmount} bitcoins "; var fiatAmountText = destinationAmount.GenerateFiatText(wallet.Synchronizer.UsdExchangeRate, "USD"); AmountText = $"{btcAmountText}{fiatAmountText}"; Labels = info.Labels.Labels.ToArray(); AddressText = info.Address.ToString(); ConfirmationTimeText = $"Approximately {TextHelpers.TimeSpanToFriendlyString(info.ConfirmationTimeSpan)} "; var fee = transaction.Fee; var btcFeeText = $"{fee.ToDecimal(MoneyUnit.Satoshi)} satoshis "; var fiatFeeText = fee.ToDecimal(MoneyUnit.BTC).GenerateFiatText(wallet.Synchronizer.UsdExchangeRate, "USD"); FeeText = $"{btcFeeText}{fiatFeeText}"; NextCommand = ReactiveCommand.CreateFromTask(async() => await OnNext(wallet, broadcaster, transaction)); }
/// <inheritdoc /> /// <exception cref="InvalidAddressException">Thrown if <paramref name="address"/> is invalid.</exception> public BuildTransactionResult Withdraw(decimal amount, string address) { this.logger.Trace("({0}:{1},{2}:'{3}')", nameof(amount), amount, nameof(address), address); ValidateAddressResult validationResult = blockCoreNodeAPI.ValidateAddress(address).Result; var result = new BuildTransactionResult(); if (!validationResult.isvalid) { this.logger.Trace("(-)[INVALID_ADDRESS]"); throw new InvalidAddressException(); } try { result = blockCoreNodeAPI.SendTo(address, amount).Result; } catch (Exception ex) { if (ex.Message == "No spendable transactions found") { // This should never happen. this.logger.Fatal(ex.Message); this.fatalNotifier.NotifySupport(ex.Message); } this.logger.Error(ex.ToString); throw; } this.logger.Trace("(-)"); return(result); }
protected override async Task BuildTransaction(string password, PaymentIntent payments, FeeStrategy feeStrategy, bool allowUnconfirmed = false, IEnumerable <OutPoint> allowedInputs = null) { BuildTransactionResult result = await Task.Run(() => Wallet.BuildTransaction(Password, payments, feeStrategy, allowUnconfirmed: true, allowedInputs: allowedInputs, GetPayjoinClient())); MainWindowViewModel.Instance.StatusBar.TryAddStatus(StatusType.SigningTransaction); SmartTransaction signedTransaction = result.Transaction; if (Wallet.KeyManager.IsHardwareWallet && !result.Signed) // If hardware but still has a privkey then it's password, then meh. { try { IsHardwareBusy = true; MainWindowViewModel.Instance.StatusBar.TryAddStatus(StatusType.AcquiringSignatureFromHardwareWallet); var client = new HwiClient(Global.Network); using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(3)); PSBT signedPsbt = null; try { try { signedPsbt = await client.SignTxAsync(Wallet.KeyManager.MasterFingerprint.Value, result.Psbt, cts.Token); } catch (PSBTException ex) when(ex.Message.Contains("NullFail")) { NotificationHelpers.Warning("Fall back to Unverified Inputs Mode, trying to sign again."); // Ledger Nano S hackfix https://github.com/MetacoSA/NBitcoin/pull/888 var noinputtx = result.Psbt.Clone(); foreach (var input in noinputtx.Inputs) { input.NonWitnessUtxo = null; } signedPsbt = await client.SignTxAsync(Wallet.KeyManager.MasterFingerprint.Value, noinputtx, cts.Token); } } catch (HwiException) { await PinPadViewModel.UnlockAsync(); signedPsbt = await client.SignTxAsync(Wallet.KeyManager.MasterFingerprint.Value, result.Psbt, cts.Token); } signedTransaction = signedPsbt.ExtractSmartTransaction(result.Transaction); } finally { MainWindowViewModel.Instance.StatusBar.TryRemoveStatus(StatusType.AcquiringSignatureFromHardwareWallet); IsHardwareBusy = false; } } MainWindowViewModel.Instance.StatusBar.TryAddStatus(StatusType.BroadcastingTransaction); await Task.Run(async() => await Global.TransactionBroadcaster.SendTransactionAsync(signedTransaction)); ResetUi(); }
public OptimisePrivacyViewModel(Wallet wallet, TransactionInfo transactionInfo, BuildTransactionResult requestedTransaction) { _wallet = wallet; _requestedTransaction = requestedTransaction; _transactionInfo = transactionInfo; this.WhenAnyValue(x => x.SelectedPrivacySuggestion) .Where(x => x is { })
protected override async Task BuildTransaction(string password, PaymentIntent payments, FeeStrategy feeStrategy, bool allowUnconfirmed = false, IEnumerable <OutPoint> allowedInputs = null) { BuildTransactionResult result = await Task.Run(() => Wallet.BuildTransaction(Password, payments, feeStrategy, allowUnconfirmed: true, allowedInputs: allowedInputs)); var txviewer = new TransactionViewerViewModel(); IoC.Get <IShell>().AddDocument(txviewer); IoC.Get <IShell>().Select(txviewer); txviewer.Update(result); ResetUi(); NotificationHelpers.Success("Transaction was built."); }
private async Task OnNext(Wallet wallet, TransactionBroadcaster broadcaster, BuildTransactionResult transaction) { var transactionAuthorizationInfo = new TransactionAuthorizationInfo(transaction); var authResult = await AuthorizeAsync(wallet, transactionAuthorizationInfo); if (authResult) { await SendTransaction(wallet, broadcaster, transactionAuthorizationInfo.Transaction); Navigate().To(new SendSuccessViewModel()); } }
public static Money CalculateDestinationAmount(this BuildTransactionResult result) { var isNormalPayment = result.OuterWalletOutputs.Any(); if (isNormalPayment) { return(result.OuterWalletOutputs.Sum(x => x.Amount)); } else { return(result.InnerWalletOutputs .Where(x => !x.HdPubKey.IsInternal) .Select(x => x.Amount) .Sum()); } }
public TransactionPreviewViewModel(Wallet wallet, TransactionInfo info, BuildTransactionResult transaction) { _wallet = wallet; _labels = SmartLabel.Empty; _info = info; SetupCancel(enableCancel: false, enableCancelOnEscape: true, enableCancelOnPressed: false); EnableBack = true; _confirmationTimeText = ""; var destinationAmount = transaction.CalculateDestinationAmount().ToDecimal(MoneyUnit.BTC); var btcAmountText = $"{destinationAmount} bitcoins "; var fiatAmountText = destinationAmount.GenerateFiatText(_wallet.Synchronizer.UsdExchangeRate, "USD"); AmountText = $"{btcAmountText}{fiatAmountText}"; AddressText = info.Address.ToString(); var fee = transaction.Fee; var btcFeeText = $"{fee.ToDecimal(MoneyUnit.Satoshi)} sats "; var fiatFeeText = fee.ToDecimal(MoneyUnit.BTC).GenerateFiatText(_wallet.Synchronizer.UsdExchangeRate, "USD"); FeeText = $"{btcFeeText}{fiatFeeText}"; PayJoinUrl = info.PayJoinClient?.PaymentUrl.AbsoluteUri; IsPayJoin = PayJoinUrl is not null; if (PreferPsbtWorkflow) { SkipCommand = ReactiveCommand.CreateFromTask(async() => await OnConfirmAsync(transaction)); NextCommand = ReactiveCommand.CreateFromTask(async() => { var saved = await TransactionHelpers.ExportTransactionToBinaryAsync(transaction); if (saved) { Navigate().To(new SuccessViewModel("The PSBT has been successfully created.")); } }); _nextButtonText = "Save PSBT file"; } else { NextCommand = ReactiveCommand.CreateFromTask(async() => await OnConfirmAsync(transaction)); _nextButtonText = "Confirm"; } }
public TransactionPreviewViewModel(Wallet wallet, TransactionInfo info, TransactionBroadcaster broadcaster, BuildTransactionResult transaction) { var destinationAmount = transaction.CalculateDestinationAmount().ToDecimal(MoneyUnit.BTC); var fee = transaction.Fee; BtcAmountText = $"{destinationAmount} bitcoins "; FiatAmountText = $"(≈{(destinationAmount * wallet.Synchronizer.UsdExchangeRate).FormattedFiat()} USD) "; Labels = info.Labels.Labels.ToArray(); AddressText = info.Address.ToString(); ConfirmationTimeText = $"Approximately {TextHelpers.TimeSpanToFriendlyString(info.ConfirmationTimeSpan)} "; BtcFeeText = $"{fee.ToDecimal(MoneyUnit.Satoshi)} satoshis "; FiatFeeText = $"(≈{(fee.ToDecimal(MoneyUnit.BTC) * wallet.Synchronizer.UsdExchangeRate).FormattedFiat()} USD)"; NextCommand = ReactiveCommand.CreateFromTask(async() => { var transactionAuthorizationInfo = new TransactionAuthorizationInfo(transaction); var authDialog = AuthorizationHelpers.GetAuthorizationDialog(wallet, transactionAuthorizationInfo); var authDialogResult = await NavigateDialog(authDialog, authDialog.DefaultTarget); if (authDialogResult.Result) { IsBusy = true; // Dequeue any coin-joining coins. await wallet.ChaumianClient.DequeueAllCoinsFromMixAsync(DequeueReason.TransactionBuilding); await broadcaster.SendTransactionAsync(transactionAuthorizationInfo.Transaction); Navigate().Clear(); IsBusy = false; } else if (authDialogResult.Kind == DialogResultKind.Normal) { await ShowErrorAsync("Authorization", "The Authorization has failed, please try again.", ""); } }); }
public static async IAsyncEnumerable <ChangeAvoidanceSuggestionViewModel> GenerateSuggestionsAsync( TransactionInfo transactionInfo, BitcoinAddress destination, Wallet wallet, ImmutableArray <SmartCoin> coinsToUse, int maxInputCount, decimal usdExchangeRate, [EnumeratorCancellation] CancellationToken cancellationToken) { var selections = ChangelessTransactionCoinSelector.GetAllStrategyResultsAsync( coinsToUse, transactionInfo.FeeRate, new TxOut(transactionInfo.Amount, destination), maxInputCount, cancellationToken).ConfigureAwait(false); HashSet <Money> foundSolutionsByAmount = new(); await foreach (var selection in selections) { if (selection.Any()) { BuildTransactionResult transaction = TransactionHelpers.BuildChangelessTransaction( wallet, destination, transactionInfo.UserLabels, transactionInfo.FeeRate, selection, tryToSign: false); var destinationAmount = transaction.CalculateDestinationAmount(); // If BnB solutions become the same transaction somehow, do not show the same suggestion twice. if (!foundSolutionsByAmount.Contains(destinationAmount)) { foundSolutionsByAmount.Add(destinationAmount); yield return(new ChangeAvoidanceSuggestionViewModel( transactionInfo.Amount.ToDecimal(MoneyUnit.BTC), transaction, usdExchangeRate)); } } } }
protected override async Task DoAfterBuildTransaction(BuildTransactionResult result) { MainWindowViewModel.Instance.StatusBar.TryAddStatus(StatusType.SigningTransaction); SmartTransaction signedTransaction = result.Transaction; if (Wallet.KeyManager.IsHardwareWallet && !result.Signed) // If hardware but still has a privkey then it's password, then meh. { try { IsHardwareBusy = true; MainWindowViewModel.Instance.StatusBar.TryAddStatus(StatusType.AcquiringSignatureFromHardwareWallet); var client = new HwiClient(Global.Network); using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(3)); PSBT signedPsbt = null; try { signedPsbt = await client.SignTxAsync(Wallet.KeyManager.MasterFingerprint.Value, result.Psbt, cts.Token); } catch (HwiException) { await PinPadViewModel.UnlockAsync(); signedPsbt = await client.SignTxAsync(Wallet.KeyManager.MasterFingerprint.Value, result.Psbt, cts.Token); } signedTransaction = signedPsbt.ExtractSmartTransaction(result.Transaction); } catch (Exception ex) { NotificationHelpers.Error(ex.ToUserFriendlyString()); return; } finally { MainWindowViewModel.Instance.StatusBar.TryRemoveStatus(StatusType.AcquiringSignatureFromHardwareWallet); IsHardwareBusy = false; } } MainWindowViewModel.Instance.StatusBar.TryAddStatus(StatusType.BroadcastingTransaction); await Task.Run(async() => await Global.TransactionBroadcaster.SendTransactionAsync(signedTransaction)); ResetUi(); }
public async Task <BuildTransactionResult> BuildTransaction(string address, Money amount) { var result = new BuildTransactionResult(); var recipient = new Recipient() { amount = amount.ToString(), destinationAddress = address }; var recipients = new List <Recipient> { recipient }; var newTransaction = new BuildTransactionRequest() { accountName = AccountName, allowUnconfirmed = true, feeAmount = MinFee.ToString(), password = WalletPassword, walletName = WalletName, recipients = recipients, segwitChangeAddress = UseSegwit }; var client = new RestClient(ApiUrl); var request = new RestRequest("/api/Wallet/build-transaction", Method.Post); var transactionToSend = JsonConvert.SerializeObject(newTransaction); request.AddParameter("application/json; charset=utf-8", transactionToSend, ParameterType.RequestBody); request.RequestFormat = DataFormat.Json; var response = await client.ExecuteAsync <BuildTransactionResult>(request); if (response.StatusCode == HttpStatusCode.OK) { result = response.Data; } else { result = null; } return(result); }
protected override Task DoAfterBuildTransaction(BuildTransactionResult result) { try { var txviewer = new TransactionViewerViewModel(); IoC.Get <IShell>().AddDocument(txviewer); IoC.Get <IShell>().Select(txviewer); txviewer.Update(result); ResetUi(); NotificationHelpers.Success("Transaction was built."); } catch (Exception ex) { return(Task.FromException(ex)); } return(Task.CompletedTask); }
protected override async Task BuildTransaction(string password, PaymentIntent payments, FeeStrategy feeStrategy, bool allowUnconfirmed = false, IEnumerable <OutPoint> allowedInputs = null) { BuildTransactionResult result = await Task.Run(() => Wallet.BuildTransaction(Password, payments, feeStrategy, allowUnconfirmed: true, allowedInputs: allowedInputs, GetPayjoinClient())); MainWindowViewModel.Instance.StatusBar.TryAddStatus(StatusType.SigningTransaction); SmartTransaction signedTransaction = result.Transaction; if (Wallet.KeyManager.IsHardwareWallet && !result.Signed) // If hardware but still has a privkey then it's password, then meh. { try { IsHardwareBusy = true; MainWindowViewModel.Instance.StatusBar.TryAddStatus(StatusType.AcquiringSignatureFromHardwareWallet); var client = new HwiClient(Global.Network); using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(3)); PSBT signedPsbt = null; try { signedPsbt = await client.SignTxAsync(Wallet.KeyManager.MasterFingerprint.Value, result.Psbt, cts.Token); } catch (HwiException) { await PinPadViewModel.UnlockAsync(); signedPsbt = await client.SignTxAsync(Wallet.KeyManager.MasterFingerprint.Value, result.Psbt, cts.Token); } signedTransaction = signedPsbt.ExtractSmartTransaction(result.Transaction); } finally { MainWindowViewModel.Instance.StatusBar.TryRemoveStatus(StatusType.AcquiringSignatureFromHardwareWallet); IsHardwareBusy = false; } } MainWindowViewModel.Instance.StatusBar.TryAddStatus(StatusType.BroadcastingTransaction); await Task.Run(async() => await Global.TransactionBroadcaster.SendTransactionAsync(signedTransaction)); ResetUi(); }
public PrivacySuggestionControlViewModel(decimal originalAmount, BuildTransactionResult transactionResult, PrivacyOptimisationLevel optimisationLevel, params string[] benefits) { _transactionResult = transactionResult; _optimisationLevel = optimisationLevel; _benefits = benefits; decimal total = transactionResult.CalculateDestinationAmount().ToDecimal(MoneyUnit.BTC); if (optimisationLevel == PrivacyOptimisationLevel.Better) { var pcDifference = ((total - originalAmount) / originalAmount) * 100; _caption = pcDifference > 0 ? $"{pcDifference:F}% More" : $"{Math.Abs(pcDifference):F}% Less"; } else { _caption = "As Requested"; } _title = $"{total}"; }
public PrivacySuggestionControlViewModel( decimal originalAmount, BuildTransactionResult transactionResult, PrivacyOptimisationLevel optimisationLevel, decimal fiatExchangeRate, params PrivacySuggestionBenefit[] benefits) { TransactionResult = transactionResult; _optimisationLevel = optimisationLevel; _benefits = benefits.ToList(); decimal total = transactionResult.CalculateDestinationAmount().ToDecimal(MoneyUnit.BTC); var fiatTotal = total * fiatExchangeRate; _amountFiat = total.GenerateFiatText(fiatExchangeRate, "USD"); _optimisationLevelGood = optimisationLevel == PrivacyOptimisationLevel.Better; if (_optimisationLevelGood) { var fiatOriginal = originalAmount * fiatExchangeRate; var fiatDifference = fiatTotal - fiatOriginal; var difference = (fiatDifference > 0 ? $"{fiatDifference.GenerateFiatText("USD")} More" : $"{Math.Abs(fiatDifference).GenerateFiatText("USD")} Less") .Replace("(", "").Replace(")", ""); _benefits.Add(new(false, difference)); } else { // This is just to pad the control. _benefits.Add(new(false, " ")); } _amount = $"{total}"; }
private async Task OnNext(Wallet wallet, TransactionBroadcaster broadcaster, BuildTransactionResult transaction) { var transactionAuthorizationInfo = new TransactionAuthorizationInfo(transaction); var authDialog = AuthorizationHelpers.GetAuthorizationDialog(wallet, transactionAuthorizationInfo); var authDialogResult = await NavigateDialog(authDialog, authDialog.DefaultTarget); if (authDialogResult.Result) { IsBusy = true; // Dequeue any coin-joining coins. await wallet.ChaumianClient.DequeueAllCoinsFromMixAsync(DequeueReason.TransactionBuilding); await broadcaster.SendTransactionAsync(transactionAuthorizationInfo.Transaction); Navigate().Clear(); IsBusy = false; } else if (authDialogResult.Kind == DialogResultKind.Normal) { await ShowErrorAsync("Authorization", "The Authorization has failed, please try again.", ""); } }
protected override Task DoAfterBuildTransaction(BuildTransactionResult result) { try { var txviewer = IoC.Get <IShell>().Documents?.OfType <TransactionViewerViewModel>()?.FirstOrDefault(x => x.Wallet.Id == Wallet.Id); if (txviewer is null) { txviewer = new TransactionViewerViewModel(Wallet); IoC.Get <IShell>().AddDocument(txviewer); } IoC.Get <IShell>().Select(txviewer); txviewer.Update(result); ResetUi(); NotificationHelpers.Success("Transaction is successfully built!", ""); } catch (Exception ex) { return(Task.FromException(ex)); } return(Task.CompletedTask); }
public TransactionAuthorizationInfo(BuildTransactionResult buildTransactionResult) { Psbt = buildTransactionResult.Psbt; Transaction = buildTransactionResult.Transaction; }
public async Task BuildPrivacySuggestionsAsync(Wallet wallet, TransactionInfo info, BitcoinAddress destination, BuildTransactionResult transaction, bool isFixedAmount, CancellationToken cancellationToken) { _suggestionCancellationTokenSource?.Cancel(); _suggestionCancellationTokenSource?.Dispose(); _suggestionCancellationTokenSource = new(TimeSpan.FromSeconds(15)); using CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(_suggestionCancellationTokenSource.Token, cancellationToken); Suggestions.Clear(); SelectedSuggestion = null; if (!info.IsPrivate) { Suggestions.Add(new PocketSuggestionViewModel(SmartLabel.Merge(transaction.SpentCoins.Select(x => x.GetLabels(wallet.KeyManager.MinAnonScoreTarget))))); } var loadingRing = new LoadingSuggestionViewModel(); Suggestions.Add(loadingRing); var hasChange = transaction.InnerWalletOutputs.Any(x => x.ScriptPubKey != destination.ScriptPubKey); if (hasChange && !isFixedAmount && !info.IsPayJoin) { // Exchange rate can change substantially during computation itself. // Reporting up-to-date exchange rates would just confuse users. decimal usdExchangeRate = wallet.Synchronizer.UsdExchangeRate; int originalInputCount = transaction.SpentCoins.Count(); int maxInputCount = (int)(Math.Max(3, originalInputCount * 1.3)); IAsyncEnumerable <ChangeAvoidanceSuggestionViewModel> suggestions = ChangeAvoidanceSuggestionViewModel.GenerateSuggestionsAsync(info, destination, wallet, maxInputCount, usdExchangeRate, linkedCts.Token); await foreach (var suggestion in suggestions) { Suggestions.Insert(Suggestions.Count - 1, suggestion); } } Suggestions.Remove(loadingRing); }