public async Task <IActionResult> AddDerivationScheme(string storeId, DerivationSchemeViewModel vm,
                                                              string cryptoCode)
        {
            vm.CryptoCode = cryptoCode;
            var store = HttpContext.GetStoreData();

            if (store == null)
            {
                return(NotFound());
            }

            var network = cryptoCode == null ? null : _ExplorerProvider.GetNetwork(cryptoCode);

            if (network == null)
            {
                return(NotFound());
            }

            vm.Network     = network;
            vm.RootKeyPath = network.GetRootKeyPath();
            DerivationSchemeSettings strategy = null;

            var wallet = _WalletProvider.GetWallet(network);

            if (wallet == null)
            {
                return(NotFound());
            }

            if (!string.IsNullOrEmpty(vm.Config))
            {
                if (!DerivationSchemeSettings.TryParseFromJson(vm.Config, network, out strategy))
                {
                    TempData.SetStatusMessageModel(new StatusMessageModel()
                    {
                        Severity = StatusMessageModel.StatusSeverity.Error,
                        Message  = "Config file was not in the correct format"
                    });
                    vm.Confirmation = false;
                    return(View(vm));
                }
            }

            if (vm.ColdcardPublicFile != null)
            {
                if (!DerivationSchemeSettings.TryParseFromColdcard(await ReadAllText(vm.ColdcardPublicFile), network, out strategy))
                {
                    TempData.SetStatusMessageModel(new StatusMessageModel()
                    {
                        Severity = StatusMessageModel.StatusSeverity.Error,
                        Message  = "Coldcard public file was not in the correct format"
                    });
                    vm.Confirmation = false;
                    return(View(vm));
                }
            }
            else
            {
                try
                {
                    if (!string.IsNullOrEmpty(vm.DerivationScheme))
                    {
                        var newStrategy = ParseDerivationStrategy(vm.DerivationScheme, null, network);
                        if (newStrategy.AccountDerivation != strategy?.AccountDerivation)
                        {
                            var accountKey = string.IsNullOrEmpty(vm.AccountKey) ? null : new BitcoinExtPubKey(vm.AccountKey, network.NBitcoinNetwork);
                            if (accountKey != null)
                            {
                                var accountSettings = newStrategy.AccountKeySettings.FirstOrDefault(a => a.AccountKey == accountKey);
                                if (accountSettings != null)
                                {
                                    accountSettings.AccountKeyPath  = vm.KeyPath == null ? null : KeyPath.Parse(vm.KeyPath);
                                    accountSettings.RootFingerprint = string.IsNullOrEmpty(vm.RootFingerprint) ? (HDFingerprint?)null : new HDFingerprint(NBitcoin.DataEncoders.Encoders.Hex.DecodeData(vm.RootFingerprint));
                                }
                            }
                            strategy            = newStrategy;
                            strategy.Source     = vm.Source;
                            vm.DerivationScheme = strategy.AccountDerivation.ToString();
                        }
                    }
                    else
                    {
                        strategy = null;
                    }
                }
                catch
                {
                    ModelState.AddModelError(nameof(vm.DerivationScheme), "Invalid Derivation Scheme");
                    vm.Confirmation = false;
                    return(View(vm));
                }
            }

            var oldConfig = vm.Config;

            vm.Config = strategy == null ? null : strategy.ToJson();

            PaymentMethodId paymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike);
            var             exisingStrategy = store.GetSupportedPaymentMethods(_NetworkProvider)
                                              .Where(c => c.PaymentId == paymentMethodId)
                                              .OfType <DerivationSchemeSettings>()
                                              .FirstOrDefault();
            var storeBlob      = store.GetStoreBlob();
            var wasExcluded    = storeBlob.GetExcludedPaymentMethods().Match(paymentMethodId);
            var willBeExcluded = !vm.Enabled;

            var showAddress = // Show addresses if:
                              // - If the user is testing the hint address in confirmation screen
                              (vm.Confirmation && !string.IsNullOrWhiteSpace(vm.HintAddress)) ||
                              // - The user is clicking on continue after changing the config
                              (!vm.Confirmation && oldConfig != vm.Config) ||
                              // - The user is clickingon continue without changing config nor enabling/disabling
                              (!vm.Confirmation && oldConfig == vm.Config && willBeExcluded == wasExcluded);

            showAddress = showAddress && strategy != null;
            if (!showAddress)
            {
                try
                {
                    if (strategy != null)
                    {
                        await wallet.TrackAsync(strategy.AccountDerivation);
                    }
                    store.SetSupportedPaymentMethod(paymentMethodId, strategy);
                    storeBlob.SetExcluded(paymentMethodId, willBeExcluded);
                    store.SetStoreBlob(storeBlob);
                }
                catch
                {
                    ModelState.AddModelError(nameof(vm.DerivationScheme), "Invalid Derivation Scheme");
                    return(View(vm));
                }

                await _Repo.UpdateStore(store);

                if (willBeExcluded != wasExcluded)
                {
                    var label = willBeExcluded ? "disabled" : "enabled";
                    TempData[WellKnownTempData.SuccessMessage] = $"On-Chain payments for {network.CryptoCode} has been {label}.";
                }
                else
                {
                    TempData[WellKnownTempData.SuccessMessage] = $"Derivation settings for {network.CryptoCode} has been modified.";
                }
                return(RedirectToAction(nameof(UpdateStore), new { storeId = storeId }));
            }
            else if (!string.IsNullOrEmpty(vm.HintAddress))
            {
                BitcoinAddress address = null;
                try
                {
                    address = BitcoinAddress.Create(vm.HintAddress, network.NBitcoinNetwork);
                }
                catch
                {
                    ModelState.AddModelError(nameof(vm.HintAddress), "Invalid hint address");
                    return(ShowAddresses(vm, strategy));
                }

                try
                {
                    var newStrategy = ParseDerivationStrategy(vm.DerivationScheme, address.ScriptPubKey, network);
                    if (newStrategy.AccountDerivation != strategy.AccountDerivation)
                    {
                        strategy.AccountDerivation = newStrategy.AccountDerivation;
                        strategy.AccountOriginal   = null;
                    }
                }
                catch
                {
                    ModelState.AddModelError(nameof(vm.HintAddress), "Impossible to find a match with this address");
                    return(ShowAddresses(vm, strategy));
                }

                vm.HintAddress = "";
                TempData[WellKnownTempData.SuccessMessage] =
                    "Address successfully found, please verify that the rest is correct and click on \"Confirm\"";
                ModelState.Remove(nameof(vm.HintAddress));
                ModelState.Remove(nameof(vm.DerivationScheme));
            }

            return(ShowAddresses(vm, strategy));
        }
Esempio n. 2
0
        public async Task <IActionResult> UpdateWallet(WalletSetupViewModel vm)
        {
            var checkResult = IsAvailable(vm.CryptoCode, out var store, out var network);

            if (checkResult != null)
            {
                return(checkResult);
            }

            vm.Network = network;
            DerivationSchemeSettings strategy = null;

            var wallet = _WalletProvider.GetWallet(network);

            if (wallet == null)
            {
                return(NotFound());
            }

            if (vm.WalletFile != null)
            {
                if (!DerivationSchemeSettings.TryParseFromWalletFile(await ReadAllText(vm.WalletFile), network, out strategy))
                {
                    ModelState.AddModelError(nameof(vm.WalletFile), "Wallet file was not in the correct format");
                    return(View(vm.ViewName, vm));
                }
            }
            else if (!string.IsNullOrEmpty(vm.WalletFileContent))
            {
                if (!DerivationSchemeSettings.TryParseFromWalletFile(vm.WalletFileContent, network, out strategy))
                {
                    ModelState.AddModelError(nameof(vm.WalletFileContent), "QR import was not in the correct format");
                    return(View(vm.ViewName, vm));
                }
            }
            else if (!string.IsNullOrEmpty(vm.DerivationScheme))
            {
                try
                {
                    strategy        = ParseDerivationStrategy(vm.DerivationScheme, network);
                    strategy.Source = "ManualDerivationScheme";
                    if (!string.IsNullOrEmpty(vm.AccountKey))
                    {
                        var accountKey      = new BitcoinExtPubKey(vm.AccountKey, network.NBitcoinNetwork);
                        var accountSettings =
                            strategy.AccountKeySettings.FirstOrDefault(a => a.AccountKey == accountKey);
                        if (accountSettings != null)
                        {
                            accountSettings.AccountKeyPath =
                                vm.KeyPath == null ? null : KeyPath.Parse(vm.KeyPath);
                            accountSettings.RootFingerprint = string.IsNullOrEmpty(vm.RootFingerprint)
                                ? (HDFingerprint?)null
                                : new HDFingerprint(
                                NBitcoin.DataEncoders.Encoders.Hex.DecodeData(vm.RootFingerprint));
                        }
                    }
                    vm.DerivationScheme = strategy.AccountDerivation.ToString();
                    ModelState.Remove(nameof(vm.DerivationScheme));
                }
                catch
                {
                    ModelState.AddModelError(nameof(vm.DerivationScheme), "Invalid wallet format");
                    return(View(vm.ViewName, vm));
                }
            }
            else if (!string.IsNullOrEmpty(vm.Config))
            {
                if (!DerivationSchemeSettings.TryParseFromJson(UnprotectString(vm.Config), network, out strategy))
                {
                    ModelState.AddModelError(nameof(vm.Config), "Config file was not in the correct format");
                    return(View(vm.ViewName, vm));
                }
            }

            if (strategy is null)
            {
                ModelState.AddModelError(nameof(vm.DerivationScheme), "Please provide your extended public key");
                return(View(vm.ViewName, vm));
            }

            vm.Config = ProtectString(strategy.ToJson());
            ModelState.Remove(nameof(vm.Config));

            PaymentMethodId paymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike);
            var             storeBlob       = store.GetStoreBlob();

            if (vm.Confirmation)
            {
                try
                {
                    await wallet.TrackAsync(strategy.AccountDerivation);

                    store.SetSupportedPaymentMethod(paymentMethodId, strategy);
                    storeBlob.SetExcluded(paymentMethodId, false);
                    storeBlob.Hints.Wallet   = false;
                    storeBlob.PayJoinEnabled = strategy.IsHotWallet && !(vm.SetupRequest?.PayJoinEnabled is false);
                    store.SetStoreBlob(storeBlob);
                }
                catch
                {
                    ModelState.AddModelError(nameof(vm.DerivationScheme), "Invalid derivation scheme");
                    return(View(vm.ViewName, vm));
                }
                await _Repo.UpdateStore(store);

                _EventAggregator.Publish(new WalletChangedEvent {
                    WalletId = new WalletId(vm.StoreId, vm.CryptoCode)
                });

                TempData[WellKnownTempData.SuccessMessage] = $"Wallet settings for {network.CryptoCode} have been updated.";

                // This is success case when derivation scheme is added to the store
                return(RedirectToAction(nameof(UpdateStore), new { storeId = vm.StoreId }));
            }
            return(ConfirmAddresses(vm, strategy));
        }
        public async Task <IActionResult> UpdateWallet(WalletSetupViewModel vm)
        {
            var checkResult = IsAvailable(vm.CryptoCode, out var store, out var network);

            if (checkResult != null)
            {
                return(checkResult);
            }

            vm.Network     = network;
            vm.RootKeyPath = network.GetRootKeyPath();
            DerivationSchemeSettings strategy = null;

            var wallet = _WalletProvider.GetWallet(network);

            if (wallet == null)
            {
                return(NotFound());
            }

            if (!string.IsNullOrEmpty(vm.Config))
            {
                if (!DerivationSchemeSettings.TryParseFromJson(vm.Config, network, out strategy))
                {
                    ModelState.AddModelError(nameof(vm.Config), "Config file was not in the correct format");
                    return(View(vm.ViewName, vm));
                }
            }

            if (vm.WalletFile != null)
            {
                if (!DerivationSchemeSettings.TryParseFromWalletFile(await ReadAllText(vm.WalletFile), network, out strategy))
                {
                    ModelState.AddModelError(nameof(vm.WalletFile), "Wallet file was not in the correct format");
                    return(View(vm.ViewName, vm));
                }
            }
            else if (!string.IsNullOrEmpty(vm.WalletFileContent))
            {
                if (!DerivationSchemeSettings.TryParseFromWalletFile(vm.WalletFileContent, network, out strategy))
                {
                    ModelState.AddModelError(nameof(vm.WalletFileContent), "QR import was not in the correct format");
                    return(View(vm.ViewName, vm));
                }
            }
            else if (!string.IsNullOrEmpty(vm.DerivationScheme))
            {
                try
                {
                    var newStrategy = ParseDerivationStrategy(vm.DerivationScheme, null, network);
                    if (newStrategy.AccountDerivation != strategy?.AccountDerivation)
                    {
                        var accountKey = string.IsNullOrEmpty(vm.AccountKey)
                            ? null
                            : new BitcoinExtPubKey(vm.AccountKey, network.NBitcoinNetwork);
                        if (accountKey != null)
                        {
                            var accountSettings =
                                newStrategy.AccountKeySettings.FirstOrDefault(a => a.AccountKey == accountKey);
                            if (accountSettings != null)
                            {
                                accountSettings.AccountKeyPath =
                                    vm.KeyPath == null ? null : KeyPath.Parse(vm.KeyPath);
                                accountSettings.RootFingerprint = string.IsNullOrEmpty(vm.RootFingerprint)
                                    ? (HDFingerprint?)null
                                    : new HDFingerprint(
                                    NBitcoin.DataEncoders.Encoders.Hex.DecodeData(vm.RootFingerprint));
                            }
                        }

                        strategy            = newStrategy;
                        strategy.Source     = vm.Source;
                        vm.DerivationScheme = strategy.AccountDerivation.ToString();
                    }
                }
                catch
                {
                    ModelState.AddModelError(nameof(vm.DerivationScheme), "Invalid wallet format");
                    return(View(vm.ViewName, vm));
                }
            }
            else
            {
                ModelState.AddModelError(nameof(vm.DerivationScheme), "Please provide your extended public key");
                return(View(vm.ViewName, vm));
            }

            var oldConfig = vm.Config;

            vm.Config = strategy?.ToJson();
            var             configChanged   = oldConfig != vm.Config;
            PaymentMethodId paymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike);
            var             storeBlob       = store.GetStoreBlob();
            var             wasExcluded     = storeBlob.GetExcludedPaymentMethods().Match(paymentMethodId);
            var             willBeExcluded  = !vm.Enabled;
            var             excludedChanged = willBeExcluded != wasExcluded;

            var showAddress = // Show addresses if:
                              // - If the user is testing the hint address in confirmation screen
                              (vm.Confirmation && !string.IsNullOrWhiteSpace(vm.HintAddress)) ||
                              // - The user is clicking on continue after changing the config
                              (!vm.Confirmation && configChanged);

            showAddress = showAddress && strategy != null;
            if (!showAddress)
            {
                try
                {
                    if (strategy != null)
                    {
                        await wallet.TrackAsync(strategy.AccountDerivation);
                    }
                    store.SetSupportedPaymentMethod(paymentMethodId, strategy);
                    storeBlob.SetExcluded(paymentMethodId, willBeExcluded);
                    storeBlob.Hints.Wallet = false;
                    store.SetStoreBlob(storeBlob);
                }
                catch
                {
                    ModelState.AddModelError(nameof(vm.DerivationScheme), "Invalid derivation scheme");
                    return(View(vm.ViewName, vm));
                }

                await _Repo.UpdateStore(store);

                _EventAggregator.Publish(new WalletChangedEvent {
                    WalletId = new WalletId(vm.StoreId, vm.CryptoCode)
                });

                if (excludedChanged)
                {
                    var label = willBeExcluded ? "disabled" : "enabled";
                    TempData[WellKnownTempData.SuccessMessage] =
                        $"On-Chain payments for {network.CryptoCode} have been {label}.";
                }
                else
                {
                    TempData[WellKnownTempData.SuccessMessage] =
                        $"Derivation settings for {network.CryptoCode} have been modified.";
                }

                // This is success case when derivation scheme is added to the store
                return(RedirectToAction(nameof(UpdateStore), new { storeId = vm.StoreId }));
            }

            if (!string.IsNullOrEmpty(vm.HintAddress))
            {
                BitcoinAddress address;
                try
                {
                    address = BitcoinAddress.Create(vm.HintAddress, network.NBitcoinNetwork);
                }
                catch
                {
                    ModelState.AddModelError(nameof(vm.HintAddress), "Invalid hint address");
                    return(ConfirmAddresses(vm, strategy));
                }

                try
                {
                    var newStrategy = ParseDerivationStrategy(vm.DerivationScheme, address.ScriptPubKey, network);
                    if (newStrategy.AccountDerivation != strategy.AccountDerivation)
                    {
                        strategy.AccountDerivation = newStrategy.AccountDerivation;
                        strategy.AccountOriginal   = null;
                    }
                }
                catch
                {
                    ModelState.AddModelError(nameof(vm.HintAddress), "Impossible to find a match with this address. Are you sure the wallet and address provided are correct and from the same source?");
                    return(ConfirmAddresses(vm, strategy));
                }

                vm.HintAddress = "";
                TempData[WellKnownTempData.SuccessMessage] =
                    "Address successfully found, please verify that the rest is correct and click on \"Confirm\"";
                ModelState.Remove(nameof(vm.HintAddress));
                ModelState.Remove(nameof(vm.DerivationScheme));
            }

            return(ConfirmAddresses(vm, strategy));
        }
Esempio n. 4
0
        public async Task <IActionResult> GenerateWallet(string storeId, string cryptoCode, WalletSetupMethod method, WalletSetupRequest request)
        {
            var checkResult = IsAvailable(cryptoCode, out var store, out var network);

            if (checkResult != null)
            {
                return(checkResult);
            }

            var(hotWallet, rpcImport) = await CanUseHotWallet();

            if (!hotWallet && request.SavePrivateKeys || !rpcImport && request.ImportKeysToRPC)
            {
                return(NotFound());
            }

            var client   = _ExplorerProvider.GetExplorerClient(cryptoCode);
            var isImport = method == WalletSetupMethod.Seed;
            var vm       = new WalletSetupViewModel
            {
                StoreId                = storeId,
                CryptoCode             = cryptoCode,
                Method                 = method,
                SetupRequest           = request,
                Confirmation           = string.IsNullOrEmpty(request.ExistingMnemonic),
                Network                = network,
                Source                 = isImport ? "SeedImported" : "NBXplorerGenerated",
                IsHotWallet            = isImport ? request.SavePrivateKeys : method == WalletSetupMethod.HotWallet,
                DerivationSchemeFormat = "BTCPay",
                CanUseHotWallet        = hotWallet,
                CanUseRPCImport        = rpcImport,
                IsTaprootActivated     = TaprootActivated(cryptoCode),
                SupportTaproot         = network.NBitcoinNetwork.Consensus.SupportTaproot,
                SupportSegwit          = network.NBitcoinNetwork.Consensus.SupportSegwit
            };

            if (isImport && string.IsNullOrEmpty(request.ExistingMnemonic))
            {
                ModelState.AddModelError(nameof(request.ExistingMnemonic), "Please provide your existing seed");
                return(View(vm.ViewName, vm));
            }

            GenerateWalletResponse response;

            try
            {
                response = await client.GenerateWalletAsync(request);

                if (response == null)
                {
                    throw new Exception("Node unavailable");
                }
            }
            catch (Exception e)
            {
                TempData.SetStatusMessageModel(new StatusMessageModel
                {
                    Severity = StatusMessageModel.StatusSeverity.Error,
                    Html     = $"There was an error generating your wallet: {e.Message}"
                });
                return(View(vm.ViewName, vm));
            }

            var derivationSchemeSettings = new DerivationSchemeSettings(response.DerivationScheme, network);

            if (method == WalletSetupMethod.Seed)
            {
                derivationSchemeSettings.Source      = "ImportedSeed";
                derivationSchemeSettings.IsHotWallet = request.SavePrivateKeys;
            }
            else
            {
                derivationSchemeSettings.Source      = "NBXplorerGenerated";
                derivationSchemeSettings.IsHotWallet = method == WalletSetupMethod.HotWallet;
            }

            var accountSettings = derivationSchemeSettings.GetSigningAccountKeySettings();

            accountSettings.AccountKeyPath           = response.AccountKeyPath.KeyPath;
            accountSettings.RootFingerprint          = response.AccountKeyPath.MasterFingerprint;
            derivationSchemeSettings.AccountOriginal = response.DerivationScheme.ToString();

            // Set wallet properties from generate response
            vm.RootFingerprint = response.AccountKeyPath.MasterFingerprint.ToString();
            vm.AccountKey      = response.AccountHDKey.Neuter().ToWif();
            vm.KeyPath         = response.AccountKeyPath.KeyPath.ToString();
            vm.Config          = ProtectString(derivationSchemeSettings.ToJson());

            var result = await UpdateWallet(vm);

            if (!ModelState.IsValid || !(result is RedirectToActionResult))
            {
                return(result);
            }

            if (!isImport)
            {
                TempData.SetStatusMessageModel(new StatusMessageModel
                {
                    Severity = StatusMessageModel.StatusSeverity.Success,
                    Html     = "<span class='text-centered'>Your wallet has been generated.</span>"
                });
                var seedVm = new RecoverySeedBackupViewModel
                {
                    CryptoCode = cryptoCode,
                    Mnemonic   = response.Mnemonic,
                    Passphrase = response.Passphrase,
                    IsStored   = request.SavePrivateKeys,
                    ReturnUrl  = Url.Action(nameof(GenerateWalletConfirm), new { storeId, cryptoCode })
                };
                if (this._BTCPayEnv.IsDeveloping)
                {
                    GenerateWalletResponse = response;
                }
                return(this.RedirectToRecoverySeedBackup(seedVm));
            }

            TempData.SetStatusMessageModel(new StatusMessageModel
            {
                Severity = StatusMessageModel.StatusSeverity.Warning,
                Html     = "Please check your addresses and confirm."
            });
            return(result);
        }
Esempio n. 5
0
        public async Task <IActionResult> AddDerivationScheme(string storeId, DerivationSchemeViewModel vm,
                                                              string cryptoCode)
        {
            vm.CryptoCode = cryptoCode;
            var store = HttpContext.GetStoreData();

            if (store == null)
            {
                return(NotFound());
            }

            var network = cryptoCode == null ? null : _ExplorerProvider.GetNetwork(cryptoCode);

            if (network == null)
            {
                return(NotFound());
            }

            vm.RootKeyPath = network.GetRootKeyPath();
            DerivationSchemeSettings strategy = null;

            var wallet = _WalletProvider.GetWallet(network);

            if (wallet == null)
            {
                return(NotFound());
            }

            if (!string.IsNullOrEmpty(vm.Config))
            {
                if (!DerivationSchemeSettings.TryParseFromJson(vm.Config, network, out strategy))
                {
                    vm.StatusMessage = new StatusMessageModel()
                    {
                        Severity = StatusMessageModel.StatusSeverity.Error,
                        Message  = "El archivo de configuración no estaba en el formato correcto"
                    }.ToString();
                    vm.Confirmation = false;
                    return(View(vm));
                }
            }

            if (vm.ColdcardPublicFile != null)
            {
                if (!DerivationSchemeSettings.TryParseFromColdcard(await ReadAllText(vm.ColdcardPublicFile), network, out strategy))
                {
                    vm.StatusMessage = new StatusMessageModel()
                    {
                        Severity = StatusMessageModel.StatusSeverity.Error,
                        Message  = "El archivo público de Coldcard no estaba en el formato correcto"
                    }.ToString();
                    vm.Confirmation = false;
                    return(View(vm));
                }
            }
            else
            {
                try
                {
                    if (!string.IsNullOrEmpty(vm.DerivationScheme))
                    {
                        var newStrategy = ParseDerivationStrategy(vm.DerivationScheme, null, network);
                        if (newStrategy.AccountDerivation != strategy?.AccountDerivation)
                        {
                            var accountKey = string.IsNullOrEmpty(vm.AccountKey) ? null : new BitcoinExtPubKey(vm.AccountKey, network.NBitcoinNetwork);
                            if (accountKey != null)
                            {
                                var accountSettings = newStrategy.AccountKeySettings.FirstOrDefault(a => a.AccountKey == accountKey);
                                if (accountSettings != null)
                                {
                                    accountSettings.AccountKeyPath  = vm.KeyPath == null ? null : KeyPath.Parse(vm.KeyPath);
                                    accountSettings.RootFingerprint = string.IsNullOrEmpty(vm.RootFingerprint) ? (HDFingerprint?)null : new HDFingerprint(NBitcoin.DataEncoders.Encoders.Hex.DecodeData(vm.RootFingerprint));
                                }
                            }
                            strategy            = newStrategy;
                            strategy.Source     = vm.Source;
                            vm.DerivationScheme = strategy.AccountDerivation.ToString();
                        }
                    }
                    else
                    {
                        strategy = null;
                    }
                }
                catch
                {
                    ModelState.AddModelError(nameof(vm.DerivationScheme), "Esquema de derivación no válido");
                    vm.Confirmation = false;
                    return(View(vm));
                }
            }

            var oldConfig = vm.Config;

            vm.Config = strategy == null ? null : strategy.ToJson();

            PaymentMethodId paymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike);
            var             exisingStrategy = store.GetSupportedPaymentMethods(_NetworkProvider)
                                              .Where(c => c.PaymentId == paymentMethodId)
                                              .OfType <DerivationSchemeSettings>()
                                              .FirstOrDefault();
            var storeBlob      = store.GetStoreBlob();
            var wasExcluded    = storeBlob.GetExcludedPaymentMethods().Match(paymentMethodId);
            var willBeExcluded = !vm.Enabled;

            var showAddress = // Show addresses if:
                              // - If the user is testing the hint address in confirmation screen
                              (vm.Confirmation && !string.IsNullOrWhiteSpace(vm.HintAddress)) ||
                              // - The user is clicking on continue after changing the config
                              (!vm.Confirmation && oldConfig != vm.Config) ||
                              // - The user is clickingon continue without changing config nor enabling/disabling
                              (!vm.Confirmation && oldConfig == vm.Config && willBeExcluded == wasExcluded);

            showAddress = showAddress && strategy != null;
            if (!showAddress)
            {
                try
                {
                    if (strategy != null)
                    {
                        await wallet.TrackAsync(strategy.AccountDerivation);
                    }
                    store.SetSupportedPaymentMethod(paymentMethodId, strategy);
                    storeBlob.SetExcluded(paymentMethodId, willBeExcluded);
                    store.SetStoreBlob(storeBlob);
                }
                catch
                {
                    ModelState.AddModelError(nameof(vm.DerivationScheme), "Esquema de derivación no válido");
                    return(View(vm));
                }

                await _Repo.UpdateStore(store);

                if (oldConfig != vm.Config)
                {
                    StatusMessage = $"Ajustes de derivación para{network.CryptoCode} ha sido modificado.";
                }
                if (willBeExcluded != wasExcluded)
                {
                    var label = willBeExcluded ? "desactivado" : "activado";
                    StatusMessage = $"Pagos en cadena para {network.CryptoCode} ha sido {label}.";
                }
                return(RedirectToAction(nameof(UpdateStore), new { storeId = storeId }));
            }
            else if (!string.IsNullOrEmpty(vm.HintAddress))
            {
                BitcoinAddress address = null;
                try
                {
                    address = BitcoinAddress.Create(vm.HintAddress, network.NBitcoinNetwork);
                }
                catch
                {
                    ModelState.AddModelError(nameof(vm.HintAddress), "Dirección de sugerencia no válida");
                    return(ShowAddresses(vm, strategy));
                }

                try
                {
                    var newStrategy = ParseDerivationStrategy(vm.DerivationScheme, address.ScriptPubKey, network);
                    if (newStrategy.AccountDerivation != strategy.AccountDerivation)
                    {
                        strategy.AccountDerivation = newStrategy.AccountDerivation;
                        strategy.AccountOriginal   = null;
                    }
                }
                catch
                {
                    ModelState.AddModelError(nameof(vm.HintAddress), "Imposible encontrar una coincidencia con esta dirección");
                    return(ShowAddresses(vm, strategy));
                }

                vm.HintAddress   = "";
                vm.StatusMessage =
                    "Dirección encontrada con éxito, verifique que el resto sea correcto y haga clic en \"Confirm\"";
                ModelState.Remove(nameof(vm.HintAddress));
                ModelState.Remove(nameof(vm.DerivationScheme));
            }

            return(ShowAddresses(vm, strategy));
        }