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));
        }
Exemplo n.º 6
0
    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;
    }
Exemplo n.º 9
0
        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));
        }
Exemplo n.º 10
0
    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;
    }
Exemplo n.º 11
0
        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);
        }
Exemplo n.º 13
0
        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();
        }
Exemplo n.º 14
0
        public OptimisePrivacyViewModel(Wallet wallet,
                                        TransactionInfo transactionInfo, BuildTransactionResult requestedTransaction)
        {
            _wallet = wallet;
            _requestedTransaction = requestedTransaction;
            _transactionInfo      = transactionInfo;

            this.WhenAnyValue(x => x.SelectedPrivacySuggestion)
            .Where(x => x is { })
Exemplo n.º 15
0
        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.");
        }
Exemplo n.º 16
0
        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());
            }
        }
Exemplo n.º 17
0
        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.", "");
                }
            });
        }
Exemplo n.º 20
0
    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));
                }
            }
        }
    }
Exemplo n.º 21
0
        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();
        }
Exemplo n.º 22
0
        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);
        }
Exemplo n.º 23
0
        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);
        }
Exemplo n.º 24
0
        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}";
        }
Exemplo n.º 26
0
        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}";
        }
Exemplo n.º 27
0
        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.", "");
            }
        }
Exemplo n.º 28
0
        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);
        }
Exemplo n.º 29
0
 public TransactionAuthorizationInfo(BuildTransactionResult buildTransactionResult)
 {
     Psbt        = buildTransactionResult.Psbt;
     Transaction = buildTransactionResult.Transaction;
 }
Exemplo n.º 30
0
    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);
    }