private async Task <IActionResult> SetItUp(CreatePaymentForwarderViewModel vm) { var client = _nbXplorerClientProvider.GetClient(vm.CryptoCode); var presetName = $"Generated_PaymentForwarder_{vm.CryptoCode}"; var sourceExternalServiceId = vm.SelectedSourceWalletExternalServiceId; if (vm.GenerateSourceWallet) { var genResponse = await client.GenerateWalletAsync(new GenerateWalletRequest() { ScriptPubKeyType = client.Network.NBitcoinNetwork.Consensus.SupportSegwit ? ScriptPubKeyType.SegwitP2SH : ScriptPubKeyType.Legacy, }); var data = new NBXplorerWalletExternalServiceData() { CryptoCode = vm.CryptoCode, DerivationStrategy = genResponse.DerivationScheme.ToString(), PrivateKeys = new List <PrivateKeyDetails>() { new PrivateKeyDetails() { MnemonicSeed = genResponse.Mnemonic } } }; var sourceWallet = new ExternalServiceData() { Name = presetName + "_Source_Wallet", UserId = _userManager.GetUserId(User), Type = NBXplorerWalletService.NBXplorerWalletServiceType, DataJson = client.Serializer.ToString(data) }; await _externalServiceManager.AddOrUpdateExternalServiceData(sourceWallet); sourceExternalServiceId = sourceWallet.Id; } var recipe = new Recipe() { Name = presetName, Description = "Generated from a preset", UserId = _userManager.GetUserId(User), Enabled = false }; await _recipeManager.AddOrUpdateRecipe(recipe); var recipeTrigger = new RecipeTrigger() { ExternalServiceId = sourceExternalServiceId, TriggerId = NBXplorerBalanceTrigger.Id, RecipeId = recipe.Id, DataJson = client.Serializer.ToString(new NBXplorerBalanceTriggerParameters() { BalanceValue = vm.SendEntireBalance ? 0 : vm.SendBalanceValue, BalanceComparer = vm.SendEntireBalance ? BalanceComparer.GreaterThan : BalanceComparer.GreaterThanOrEqual, BalanceMoneyUnit = vm.SendBalanceMoneyUnit }) }; await _recipeManager.AddOrUpdateRecipeTrigger(recipeTrigger); var recipeActionGroup = new RecipeActionGroup() { RecipeId = recipe.Id }; await _recipeManager.AddRecipeActionGroup(recipeActionGroup); var recipeActionGroupIndex = 0; var ouputs = new List <SendTransactionData.TransactionOutput>(); foreach (var paymentDestination in vm.PaymentDestinations) { var destinationExternalServiceId = paymentDestination.SelectedDestinationWalletExternalServiceId; NBXplorerWalletExternalServiceData data = null; if (string.IsNullOrEmpty(destinationExternalServiceId)) { data = new NBXplorerWalletExternalServiceData() { CryptoCode = vm.CryptoCode, Address = paymentDestination.DestinationAddress, DerivationStrategy = paymentDestination.DerivationStrategy, }; var wallet = new ExternalServiceData() { Name = presetName + $"_Dest_Wallet_{data.DerivationStrategy}", UserId = _userManager.GetUserId(User), Type = NBXplorerWalletService.NBXplorerWalletServiceType, DataJson = client.Serializer.ToString(data) }; await _externalServiceManager.AddOrUpdateExternalServiceData(wallet); destinationExternalServiceId = wallet.Id; } else { var service = vm.Services.Items.Cast <ExternalServiceData>() .Single(serviceData => serviceData.Id == destinationExternalServiceId); data = new NBXplorerWalletService(service, _nbXplorerPublicWalletProvider, _derivationSchemeParser, _nbXplorerClientProvider).GetData(); } var recipeAction = new RecipeAction() { RecipeId = recipe.Id, RecipeActionGroupId = recipeActionGroup.Id, ActionId = new GenerateNextAddressDataActionHandler().ActionId, ExternalServiceId = destinationExternalServiceId, Order = recipeActionGroupIndex, DataJson = JsonConvert.SerializeObject(new GenerateNextAddressData()) }; await _recipeManager.AddOrUpdateRecipeAction(recipeAction); var noDecimalParse = paymentDestination.AmountPercentage / 100; ouputs.Add(new SendTransactionData.TransactionOutput() { DestinationAddress = "{{ActionData" + recipeActionGroupIndex + "}}", Amount = "{{TriggerData.Balance.ToDecimal(MoneyUnit.BTC) * " + noDecimalParse + "}}", SubtractFeesFromOutput = paymentDestination.SubtractFeesFromOutput }); recipeActionGroupIndex++; } var sweepRecipeAction = new RecipeAction() { RecipeId = recipe.Id, RecipeActionGroupId = recipeActionGroup.Id, ActionId = new SendTransactionDataActionHandler().ActionId, ExternalServiceId = sourceExternalServiceId, Order = recipeActionGroupIndex, DataJson = JsonConvert.SerializeObject(new SendTransactionData() { Outputs = ouputs }) }; await _recipeManager.AddOrUpdateRecipeAction(sweepRecipeAction); return(RedirectToAction("EditRecipe", "Recipes", new { id = recipe.Id, statusMessage = "Preset generated. Recipe is currently disabled for now. Please verify details are correct before enabling!" })); }
public async Task <IActionResult> Create(CreatePaymentForwarderViewModel viewModel) { var services = await _externalServiceManager.GetExternalServicesData(new ExternalServicesDataQuery() { UserId = _userManager.GetUserId(User), Type = new[] { NBXplorerWalletService.NBXplorerWalletServiceType } }); if (services.Any()) { var newServices = services.ToList(); newServices.Insert(0, new ExternalServiceData() { Id = null, Name = "None" }); services = newServices; } viewModel.Services = new SelectList(services, nameof(ExternalServiceData.Id), nameof(ExternalServiceData.Name)); viewModel.CryptoCodes = new SelectList(_nbXplorerOptions.Cryptos?.ToList() ?? new List <string>(), viewModel.CryptoCode); viewModel.PaymentDestinations = viewModel.PaymentDestinations ?? new List <CreatePaymentForwarderViewModel.PaymentDestination>(); if (!string.IsNullOrEmpty(viewModel.Action)) { if (viewModel.Action == "add-destination") { viewModel.PaymentDestinations.Add(new CreatePaymentForwarderViewModel.PaymentDestination()); return(View(viewModel)); } if (viewModel.Action.StartsWith("remove-destination", StringComparison.InvariantCultureIgnoreCase)) { var index = int.Parse(viewModel.Action.Substring(viewModel.Action.IndexOf(":") + 1)); viewModel.PaymentDestinations.RemoveAt(index); return(View(viewModel)); } } if ((string.IsNullOrEmpty(viewModel.SelectedSourceWalletExternalServiceId) && !viewModel.GenerateSourceWallet) || !string.IsNullOrEmpty(viewModel.SelectedSourceWalletExternalServiceId) && viewModel.GenerateSourceWallet) { if (viewModel.Services.Items.Any()) { ModelState.AddModelError(nameof(viewModel.SelectedSourceWalletExternalServiceId), "Please select a source nbxplorer wallet OR check the generate wallet checkbox"); } ModelState.AddModelError(nameof(viewModel.GenerateSourceWallet), "Please select a source nbxplorer wallet OR check the generate wallet checkbox"); } else if (!string.IsNullOrEmpty(viewModel.SelectedSourceWalletExternalServiceId)) { var service = services.SingleOrDefault(data => data.Id == viewModel.SelectedSourceWalletExternalServiceId); if (service == null) { viewModel.AddModelError( nameof(viewModel.SelectedSourceWalletExternalServiceId), "Wallet chosen is wrong... ヽ༼ ಠ益ಠ ༽ノ ", ModelState); } else { var data = new NBXplorerWalletService(service, _nbXplorerPublicWalletProvider, _derivationSchemeParser, _nbXplorerClientProvider).GetData(); if (data.CryptoCode != viewModel.CryptoCode) { viewModel.AddModelError( nameof(viewModel.SelectedSourceWalletExternalServiceId), "Wallet chosen not the same crypto", ModelState); } else if (!data.PrivateKeys.Any()) { viewModel.AddModelError( nameof(viewModel.SelectedSourceWalletExternalServiceId), "Wallet chosen has so signing keys which would make forwarding txs impossible", ModelState); } } } if (!viewModel.PaymentDestinations.Any()) { ModelState.AddModelError(string.Empty, "Please add at least one transaction destination"); } else { var totalSumIssue = viewModel.PaymentDestinations.Sum(output => output.AmountPercentage) > 100; var subtractFeesOutputs = viewModel.PaymentDestinations.Select((output, i) => (output, i)) .Where(tuple => tuple.Item1.SubtractFeesFromOutput); if (subtractFeesOutputs.Count() > 1) { foreach (var subtractFeesOutput in subtractFeesOutputs) { viewModel.AddModelError( nameof(CreatePaymentForwarderViewModel.PaymentDestination.SubtractFeesFromOutput), "You can only subtract fees from one destination", ModelState); } } for (var index = 0; index < viewModel.PaymentDestinations.Count; index++) { if (totalSumIssue) { viewModel.AddModelError( nameof(CreatePaymentForwarderViewModel.PaymentDestination.AmountPercentage), "Your total amounts across all outputs exceeds 100%. We're not a central bank and can't print more money than you own, sorry.", ModelState); } var viewModelPaymentDestination = viewModel.PaymentDestinations[index]; var check = (string.IsNullOrEmpty(viewModelPaymentDestination.DerivationStrategy) ? 0 : 1) + (string.IsNullOrEmpty(viewModelPaymentDestination.DestinationAddress) ? 0 : 1) + (string.IsNullOrEmpty(viewModelPaymentDestination.SelectedDestinationWalletExternalServiceId) ? 0 : 1); if (check != 1) { viewModel.AddModelError( nameof(CreatePaymentForwarderViewModel.PaymentDestination.DestinationAddress), "Please choose to track either an address OR a derivation scheme OR an existing NBXplorer Wallet External Service", ModelState); viewModel.AddModelError( nameof(CreatePaymentForwarderViewModel.PaymentDestination.DerivationStrategy), "Please choose to track either an address OR a derivation scheme OR an existing NBXplorer Wallet External Service", ModelState); viewModel.AddModelError( nameof(CreatePaymentForwarderViewModel.PaymentDestination.SelectedDestinationWalletExternalServiceId), "Please choose to track either an address OR a derivation scheme OR an existing NBXplorer Wallet External Service", ModelState); } if (!string.IsNullOrEmpty(viewModelPaymentDestination.SelectedDestinationWalletExternalServiceId) && !string.IsNullOrEmpty(viewModel.CryptoCode)) { var service = services.SingleOrDefault(data => data.Id == viewModelPaymentDestination.SelectedDestinationWalletExternalServiceId); if (service == null) { viewModel.AddModelError( nameof(CreatePaymentForwarderViewModel.PaymentDestination.SelectedDestinationWalletExternalServiceId), "Wallet chosen is wrong... ヽ༼ ಠ益ಠ ༽ノ ", ModelState); } else if ( new NBXplorerWalletService(service, _nbXplorerPublicWalletProvider, _derivationSchemeParser, _nbXplorerClientProvider).GetData().CryptoCode != viewModel.CryptoCode) { viewModel.AddModelError( nameof(CreatePaymentForwarderViewModel.PaymentDestination.SelectedDestinationWalletExternalServiceId), "Wallet chosen not the same crypto", ModelState); } } BitcoinAddress address = null; DerivationStrategyBase derivationStrategy = null; if (!string.IsNullOrEmpty(viewModelPaymentDestination.DestinationAddress) && !string.IsNullOrEmpty(viewModel.CryptoCode)) { try { address = BitcoinAddress.Create(viewModelPaymentDestination.DestinationAddress, _nbXplorerClientProvider.GetClient(viewModel.CryptoCode).Network.NBitcoinNetwork); } catch (Exception) { viewModel.AddModelError( nameof(CreatePaymentForwarderViewModel.PaymentDestination.DestinationAddress), "Invalid Address", ModelState); } } if (!string.IsNullOrEmpty(viewModelPaymentDestination.DerivationStrategy) && !string.IsNullOrEmpty(viewModel.CryptoCode)) { try { var factory = _nbXplorerClientProvider.GetClient(viewModel.CryptoCode).Network .DerivationStrategyFactory; derivationStrategy = _derivationSchemeParser.Parse(factory, viewModelPaymentDestination.DerivationStrategy); } catch { viewModel.AddModelError( nameof(CreatePaymentForwarderViewModel.PaymentDestination.DerivationStrategy), "Invalid Derivation Scheme", ModelState); } } } } if (!ModelState.IsValid) { return(View(viewModel)); } return(await SetItUp(viewModel)); }