Exemplo n.º 1
0
        private async Task <PSBT> UpdatePSBT(DerivationSchemeSettings derivationSchemeSettings, PSBT psbt, BTCPayNetwork network)
        {
            var result = await ExplorerClientProvider.GetExplorerClient(network).UpdatePSBTAsync(new UpdatePSBTRequest()
            {
                PSBT             = psbt,
                DerivationScheme = derivationSchemeSettings.AccountDerivation,
            });

            if (result == null)
            {
                return(null);
            }
            derivationSchemeSettings.RebaseKeyPaths(result.PSBT);
            return(result.PSBT);
        }
Exemplo n.º 2
0
        public async Task <IActionResult> WalletSend(
            [ModelBinder(typeof(WalletIdModelBinder))]
            WalletId walletId, WalletSendModel vm, string command = "", CancellationToken cancellation = default)
        {
            if (walletId?.StoreId == null)
            {
                return(NotFound());
            }
            var store = await Repository.FindStore(walletId.StoreId, GetUserId());

            if (store == null)
            {
                return(NotFound());
            }
            var network = this.NetworkProvider.GetNetwork <BTCPayNetwork>(walletId?.CryptoCode);

            if (network == null || network.ReadonlyWallet)
            {
                return(NotFound());
            }
            vm.SupportRBF = network.SupportRBF;
            decimal transactionAmountSum = 0;

            if (command == "add-output")
            {
                ModelState.Clear();
                vm.Outputs.Add(new WalletSendModel.TransactionOutput());
                return(View(vm));
            }
            if (command.StartsWith("remove-output", StringComparison.InvariantCultureIgnoreCase))
            {
                ModelState.Clear();
                var index = int.Parse(command.Substring(command.IndexOf(":", StringComparison.InvariantCultureIgnoreCase) + 1), CultureInfo.InvariantCulture);
                vm.Outputs.RemoveAt(index);
                return(View(vm));
            }


            if (!vm.Outputs.Any())
            {
                ModelState.AddModelError(string.Empty,
                                         "Please add at least one transaction output");
                return(View(vm));
            }

            var subtractFeesOutputsCount = new List <int>();
            var substractFees            = vm.Outputs.Any(o => o.SubtractFeesFromOutput);

            for (var i = 0; i < vm.Outputs.Count; i++)
            {
                var transactionOutput = vm.Outputs[i];
                if (transactionOutput.SubtractFeesFromOutput)
                {
                    subtractFeesOutputsCount.Add(i);
                }
                transactionOutput.DestinationAddress = transactionOutput.DestinationAddress?.Trim() ?? string.Empty;

                try
                {
                    BitcoinAddress.Create(transactionOutput.DestinationAddress, network.NBitcoinNetwork);
                }
                catch
                {
                    var inputName =
                        string.Format(CultureInfo.InvariantCulture, "Outputs[{0}].", i.ToString(CultureInfo.InvariantCulture)) +
                        nameof(transactionOutput.DestinationAddress);

                    ModelState.AddModelError(inputName, "Invalid address");
                }

                if (transactionOutput.Amount.HasValue)
                {
                    transactionAmountSum += transactionOutput.Amount.Value;

                    if (vm.CurrentBalance == transactionOutput.Amount.Value &&
                        !transactionOutput.SubtractFeesFromOutput)
                    {
                        vm.AddModelError(model => model.Outputs[i].SubtractFeesFromOutput,
                                         "You are sending your entire balance to the same destination, you should subtract the fees",
                                         this);
                    }
                }
            }

            if (subtractFeesOutputsCount.Count > 1)
            {
                foreach (var subtractFeesOutput in subtractFeesOutputsCount)
                {
                    vm.AddModelError(model => model.Outputs[subtractFeesOutput].SubtractFeesFromOutput,
                                     "You can only subtract fees from one output", this);
                }
            }
            else if (vm.CurrentBalance == transactionAmountSum && !substractFees)
            {
                ModelState.AddModelError(string.Empty,
                                         "You are sending your entire balance, you should subtract the fees from an output");
            }

            if (vm.CurrentBalance < transactionAmountSum)
            {
                for (var i = 0; i < vm.Outputs.Count; i++)
                {
                    vm.AddModelError(model => model.Outputs[i].Amount,
                                     "You are sending more than what you own", this);
                }
            }

            if (!ModelState.IsValid)
            {
                return(View(vm));
            }

            DerivationSchemeSettings derivationScheme = GetDerivationSchemeSettings(walletId);

            CreatePSBTResponse psbt = null;

            try
            {
                psbt = await CreatePSBT(network, derivationScheme, vm, cancellation);
            }
            catch (NBXplorerException ex)
            {
                ModelState.AddModelError(string.Empty, ex.Error.Message);
                return(View(vm));
            }
            catch (NotSupportedException)
            {
                ModelState.AddModelError(string.Empty, "You need to update your version of NBXplorer");
                return(View(vm));
            }
            derivationScheme.RebaseKeyPaths(psbt.PSBT);

            switch (command)
            {
            case "vault":
                return(ViewVault(walletId, psbt.PSBT));

            case "ledger":
                return(ViewWalletSendLedger(psbt.PSBT, psbt.ChangeAddress));

            case "seed":
                return(SignWithSeed(walletId, psbt.PSBT.ToBase64()));

            case "analyze-psbt":
                var name =
                    $"Send-{string.Join('_', vm.Outputs.Select(output => $"{output.Amount}->{output.DestinationAddress}{(output.SubtractFeesFromOutput ? "-Fees" : string.Empty)}"))}.psbt";
                return(RedirectToWalletPSBT(walletId, psbt.PSBT, name));

            default:
                return(View(vm));
            }
        }
Exemplo n.º 3
0
        public async Task <IActionResult> WalletSend(
            [ModelBinder(typeof(WalletIdModelBinder))]
            WalletId walletId, WalletSendModel vm, string command = null, CancellationToken cancellation = default)
        {
            if (walletId?.StoreId == null)
            {
                return(NotFound());
            }
            var store = await Repository.FindStore(walletId.StoreId, GetUserId());

            if (store == null)
            {
                return(NotFound());
            }
            var network = this.NetworkProvider.GetNetwork(walletId?.CryptoCode);

            if (network == null)
            {
                return(NotFound());
            }
            vm.SupportRBF = network.SupportRBF;
            var destination = ParseDestination(vm.Destination, network.NBitcoinNetwork);

            if (destination == null)
            {
                ModelState.AddModelError(nameof(vm.Destination), "Invalid address");
            }

            if (vm.Amount.HasValue)
            {
                if (vm.CurrentBalance == vm.Amount.Value && !vm.SubstractFees)
                {
                    ModelState.AddModelError(nameof(vm.Amount), "You are sending all your balance to the same destination, you should substract the fees");
                }
                if (vm.CurrentBalance < vm.Amount.Value)
                {
                    ModelState.AddModelError(nameof(vm.Amount), "You are sending more than what you own");
                }
            }
            if (!ModelState.IsValid)
            {
                return(View(vm));
            }

            DerivationSchemeSettings derivationScheme = await GetDerivationSchemeSettings(walletId);

            CreatePSBTResponse psbt = null;

            try
            {
                psbt = await CreatePSBT(network, derivationScheme, vm, cancellation);
            }
            catch (NBXplorerException ex)
            {
                ModelState.AddModelError(nameof(vm.Amount), ex.Error.Message);
                return(View(vm));
            }
            catch (NotSupportedException)
            {
                ModelState.AddModelError(nameof(vm.Destination), "You need to update your version of NBXplorer");
                return(View(vm));
            }
            derivationScheme.RebaseKeyPaths(psbt.PSBT);
            if (command == "ledger")
            {
                return(ViewWalletSendLedger(psbt.PSBT, psbt.ChangeAddress));
            }
            else if (command == "analyze-psbt")
            {
                return(ViewPSBT(psbt.PSBT, $"Send-{vm.Amount.Value}-{network.CryptoCode}-to-{destination[0].ToString()}.psbt"));
            }
            return(View(vm));
        }
        public async Task <IActionResult> WalletSend(
            [ModelBinder(typeof(WalletIdModelBinder))]
            WalletId walletId, WalletSendModel vm, string command = "", CancellationToken cancellation = default)
        {
            if (walletId?.StoreId == null)
            {
                return(NotFound());
            }
            var store = await Repository.FindStore(walletId.StoreId, GetUserId());

            if (store == null)
            {
                return(NotFound());
            }
            var network = this.NetworkProvider.GetNetwork(walletId?.CryptoCode);

            if (network == null)
            {
                return(NotFound());
            }
            vm.SupportRBF = network.SupportRBF;
            decimal transactionAmountSum = 0;

            if (command == "add-output")
            {
                ModelState.Clear();
                vm.Outputs.Add(new WalletSendModel.TransactionOutput());
                return(View(vm));
            }
            if (command.StartsWith("remove-output", StringComparison.InvariantCultureIgnoreCase))
            {
                ModelState.Clear();
                var index = int.Parse(command.Substring(command.IndexOf(":", StringComparison.InvariantCultureIgnoreCase) + 1), CultureInfo.InvariantCulture);
                vm.Outputs.RemoveAt(index);
                return(View(vm));
            }


            if (!vm.Outputs.Any())
            {
                ModelState.AddModelError(string.Empty,
                                         "Por favor agregue al menos una salida de transacción");
                return(View(vm));
            }

            var subtractFeesOutputsCount = new List <int>();
            var substractFees            = vm.Outputs.Any(o => o.SubtractFeesFromOutput);

            for (var i = 0; i < vm.Outputs.Count; i++)
            {
                var transactionOutput = vm.Outputs[i];
                if (transactionOutput.SubtractFeesFromOutput)
                {
                    subtractFeesOutputsCount.Add(i);
                }
                var destination = ParseDestination(transactionOutput.DestinationAddress, network.NBitcoinNetwork);
                if (destination == null)
                {
                    ModelState.AddModelError(nameof(transactionOutput.DestinationAddress), "Dirección inválida");
                }

                if (transactionOutput.Amount.HasValue)
                {
                    transactionAmountSum += transactionOutput.Amount.Value;

                    if (vm.CurrentBalance == transactionOutput.Amount.Value &&
                        !transactionOutput.SubtractFeesFromOutput)
                    {
                        vm.AddModelError(model => model.Outputs[i].SubtractFeesFromOutput,
                                         "Está enviando todo su saldo al mismo destino, debe restar las tarifas",
                                         ModelState);
                    }
                }
            }

            if (subtractFeesOutputsCount.Count > 1)
            {
                foreach (var subtractFeesOutput in subtractFeesOutputsCount)
                {
                    vm.AddModelError(model => model.Outputs[subtractFeesOutput].SubtractFeesFromOutput,
                                     "Solo puedes restar tarifas de una salida", ModelState);
                }
            }
            else if (vm.CurrentBalance == transactionAmountSum && !substractFees)
            {
                ModelState.AddModelError(string.Empty,
                                         "Está enviando todo su saldo, debe restar las tarifas de una salida");
            }

            if (vm.CurrentBalance < transactionAmountSum)
            {
                for (var i = 0; i < vm.Outputs.Count; i++)
                {
                    vm.AddModelError(model => model.Outputs[i].Amount,
                                     "Estás enviando más de lo que tienes", ModelState);
                }
            }

            if (!ModelState.IsValid)
            {
                return(View(vm));
            }

            DerivationSchemeSettings derivationScheme = await GetDerivationSchemeSettings(walletId);

            CreatePSBTResponse psbt = null;

            try
            {
                psbt = await CreatePSBT(network, derivationScheme, vm, cancellation);
            }
            catch (NBXplorerException ex)
            {
                ModelState.AddModelError(string.Empty, ex.Error.Message);
                return(View(vm));
            }
            catch (NotSupportedException)
            {
                ModelState.AddModelError(string.Empty, "Necesitas actualizar tu versión de NBXplorer");
                return(View(vm));
            }
            derivationScheme.RebaseKeyPaths(psbt.PSBT);

            switch (command)
            {
            case "ledger":
                return(ViewWalletSendLedger(psbt.PSBT, psbt.ChangeAddress));

            case "seed":
                return(SignWithSeed(walletId, psbt.PSBT.ToBase64()));

            case "analyze-psbt":
                var name =
                    $"Send-{string.Join('_', vm.Outputs.Select(output => $"{output.Amount}->{output.DestinationAddress}{(output.SubtractFeesFromOutput ? "-Fees" : string.Empty)}"))}.psbt";
                return(RedirectToAction(nameof(WalletPSBT), new { walletId = walletId, psbt = psbt.PSBT.ToBase64(), FileName = name }));

            default:
                return(View(vm));
            }
        }