public async Task <IActionResult> WalletPSBT([ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId, WalletPSBTViewModel vm) { var network = NetworkProvider.GetNetwork(walletId.CryptoCode); if (await vm.GetPSBT(network.NBitcoinNetwork) is PSBT psbt) { vm.Decoded = psbt.ToString(); vm.PSBT = psbt.ToBase64(); } return(View(vm ?? new WalletPSBTViewModel())); }
public async Task FundStoreWallet(WalletId walletId, int coins = 1, decimal denomination = 1m) { GoToWalletReceive(walletId); Driver.FindElement(By.Id("generateButton")).Click(); var addressStr = Driver.FindElement(By.Id("vue-address")).GetProperty("value"); var address = BitcoinAddress.Create(addressStr, ((BTCPayNetwork)Server.NetworkProvider.GetNetwork(walletId.CryptoCode)).NBitcoinNetwork); for (int i = 0; i < coins; i++) { await Server.ExplorerNode.SendToAddressAsync(address, Money.Coins(denomination)); } }
public async Task <IActionResult> WalletPSBTReady( [ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId, WalletPSBTReadyViewModel vm, string command = null) { PSBT psbt = null; var network = NetworkProvider.GetNetwork(walletId.CryptoCode); try { psbt = PSBT.Parse(vm.PSBT, network.NBitcoinNetwork); await FetchTransactionDetails(walletId, vm, network); } catch { vm.GlobalError = "Invalid PSBT"; return(View(vm)); } if (command == "broadcast") { if (!psbt.IsAllFinalized() && !psbt.TryFinalize(out var errors)) { vm.SetErrors(errors); return(View(vm)); } var transaction = psbt.ExtractTransaction(); try { var broadcastResult = await ExplorerClientProvider.GetExplorerClient(network).BroadcastAsync(transaction); if (!broadcastResult.Success) { vm.GlobalError = $"RPC Error while broadcasting: {broadcastResult.RPCCode} {broadcastResult.RPCCodeMessage} {broadcastResult.RPCMessage}"; return(View(vm)); } } catch (Exception ex) { vm.GlobalError = "Error while broadcasting: " + ex.Message; return(View(vm)); } return(await RedirectToWalletTransaction(walletId, transaction)); } else if (command == "analyze-psbt") { return(RedirectToAction(nameof(WalletPSBT), new { walletId = walletId, psbt = psbt.ToBase64() })); } else { vm.GlobalError = "Unknown command"; return(View(vm)); } }
public async Task <IActionResult> PullPayments( [ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId) { using var ctx = this._dbContextFactory.CreateContext(); var now = DateTimeOffset.UtcNow; var storeId = walletId.StoreId; var pps = await ctx.PullPayments.Where(p => p.StoreId == storeId && !p.Archived) .OrderByDescending(p => p.StartDate) .Select(o => new { PullPayment = o, Awaiting = o.Payouts .Where(p => p.State == PayoutState.AwaitingPayment || p.State == PayoutState.AwaitingApproval), Completed = o.Payouts .Where(p => p.State == PayoutState.Completed || p.State == PayoutState.InProgress) }) .ToListAsync(); var vm = new PullPaymentsModel { HasDerivationSchemeSettings = GetDerivationSchemeSettings(walletId) != null }; foreach (var o in pps) { var pp = o.PullPayment; var totalCompleted = o.Completed.Where(o => o.IsInPeriod(pp, now)) .Select(o => o.GetBlob(_jsonSerializerSettings).Amount).Sum(); var totalAwaiting = o.Awaiting.Where(o => o.IsInPeriod(pp, now)) .Select(o => o.GetBlob(_jsonSerializerSettings).Amount).Sum(); var ppBlob = pp.GetBlob(); var ni = _currencyTable.GetCurrencyData(ppBlob.Currency, true); var nfi = _currencyTable.GetNumberFormatInfo(ppBlob.Currency, true); var period = pp.GetPeriod(now); vm.PullPayments.Add(new PullPaymentsModel.PullPaymentModel() { StartDate = pp.StartDate, EndDate = pp.EndDate, Id = pp.Id, Name = ppBlob.Name, Progress = new PullPaymentsModel.PullPaymentModel.ProgressModel() { CompletedPercent = (int)(totalCompleted / ppBlob.Limit * 100m), AwaitingPercent = (int)(totalAwaiting / ppBlob.Limit * 100m), Awaiting = totalAwaiting.RoundToSignificant(ni.Divisibility).ToString("C", nfi), Completed = totalCompleted.RoundToSignificant(ni.Divisibility).ToString("C", nfi), Limit = _currencyTable.DisplayFormatCurrency(ppBlob.Limit, ppBlob.Currency), ResetIn = period?.End is DateTimeOffset nr ? ZeroIfNegative(nr - now).TimeString() : null, EndIn = pp.EndDate is DateTimeOffset end ? ZeroIfNegative(end - now).TimeString() : null } });
public async Task <IActionResult> WalletSend( [ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId, string defaultDestination = null, string defaultAmount = null) { if (walletId?.StoreId == null) { return(NotFound()); } var store = await Repository.FindStore(walletId.StoreId, GetUserId()); DerivationStrategy paymentMethod = GetPaymentMethod(walletId, store); if (paymentMethod == null) { return(NotFound()); } var storeData = store.GetStoreBlob(); var rateRules = store.GetStoreBlob().GetRateRules(NetworkProvider); rateRules.Spread = 0.0m; var currencyPair = new Rating.CurrencyPair(paymentMethod.PaymentId.CryptoCode, GetCurrencyCode(storeData.DefaultLang) ?? "USD"); WalletModel model = new WalletModel() { DefaultAddress = defaultDestination, DefaultAmount = defaultAmount, ServerUrl = GetLedgerWebsocketUrl(this.HttpContext, walletId.CryptoCode, paymentMethod.DerivationStrategyBase), CryptoCurrency = walletId.CryptoCode }; using (CancellationTokenSource cts = new CancellationTokenSource()) { try { cts.CancelAfter(TimeSpan.FromSeconds(5)); var result = await RateFetcher.FetchRate(currencyPair, rateRules).WithCancellation(cts.Token); if (result.BidAsk != null) { model.Rate = result.BidAsk.Center; model.Divisibility = _currencyTable.GetNumberFormatInfo(currencyPair.Right, true).CurrencyDecimalDigits; model.Fiat = currencyPair.Right; } else { model.RateError = $"{result.EvaluatedRule} ({string.Join(", ", result.Errors.OfType<object>().ToArray())})"; } } catch (Exception ex) { model.RateError = ex.Message; } } return(View(model)); }
private IActionResult RedirectToWalletTransaction(WalletId walletId, Transaction transaction) { var network = NetworkProvider.GetNetwork <BTCPayNetwork>(walletId.CryptoCode); if (transaction != null) { var wallet = _walletProvider.GetWallet(network); var derivationSettings = GetDerivationSchemeSettings(walletId); wallet.InvalidateCache(derivationSettings.AccountDerivation); TempData[WellKnownTempData.SuccessMessage] = $"Transaction broadcasted successfully ({transaction.GetHash().ToString()})"; } return(RedirectToAction(nameof(WalletTransactions), new { walletId = walletId.ToString() })); }
public async Task <IActionResult> WalletSend( [ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId, WalletSendModel vm) { 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()); } 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)); } return(RedirectToAction(nameof(WalletSendLedger), new WalletSendLedgerModel() { Destination = vm.Destination, Amount = vm.Amount.Value, SubstractFees = vm.SubstractFees, FeeSatoshiPerByte = vm.FeeSatoshiPerByte })); }
public Mnemonic GenerateWallet(string cryptoCode = "BTC", string seed = "", bool importkeys = false, bool privkeys = false, ScriptPubKeyType format = ScriptPubKeyType.Segwit) { Driver.FindElement(By.Id($"Modify{cryptoCode}")).Click(); // Replace previous wallet case if (Driver.PageSource.Contains("id=\"ChangeWalletLink\"")) { Driver.FindElement(By.Id("ChangeWalletLink")).Click(); Driver.FindElement(By.Id("continue")).Click(); } if (string.IsNullOrEmpty(seed)) { var option = privkeys ? "Hotwallet" : "Watchonly"; Logs.Tester.LogInformation($"Generating new seed ({option})"); Driver.FindElement(By.Id("GenerateWalletLink")).Click(); Driver.FindElement(By.Id($"Generate{option}Link")).Click(); } else { Logs.Tester.LogInformation("Progressing with existing seed"); Driver.FindElement(By.Id("ImportWalletOptionsLink")).Click(); Driver.FindElement(By.Id("ImportSeedLink")).Click(); Driver.FindElement(By.Id("ExistingMnemonic")).SendKeys(seed); Driver.SetCheckbox(By.Id("SavePrivateKeys"), privkeys); } Driver.FindElement(By.Id("ScriptPubKeyType")).Click(); Driver.FindElement(By.CssSelector($"#ScriptPubKeyType option[value={format}]")).Click(); // Open advanced settings via JS, because if we click the link it triggers the toggle animation. // This leads to Selenium trying to click the button while it is moving resulting in an error. Driver.ExecuteJavaScript("document.getElementById('AdvancedSettings').classList.add('show')"); Driver.SetCheckbox(By.Id("ImportKeysToRPC"), importkeys); Driver.FindElement(By.Id("Continue")).Click(); // Seed backup page FindAlertMessage(); if (string.IsNullOrEmpty(seed)) { seed = Driver.FindElements(By.Id("RecoveryPhrase")).First().GetAttribute("data-mnemonic"); } // Confirm seed backup Driver.FindElement(By.Id("confirm")).Click(); Driver.FindElement(By.Id("submit")).Click(); WalletId = new WalletId(StoreId, cryptoCode); return(new Mnemonic(seed)); }
public async Task <IActionResult> NewPullPayment([ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId, NewPullPaymentModel model) { if (GetDerivationSchemeSettings(walletId) == null) { return(NotFound()); } model.Name ??= string.Empty; model.Currency = model.Currency.ToUpperInvariant().Trim(); if (_currencyTable.GetCurrencyData(model.Currency, false) is null) { ModelState.AddModelError(nameof(model.Currency), "Invalid currency"); } if (model.Amount <= 0.0m) { ModelState.AddModelError(nameof(model.Amount), "The amount should be more than zero"); } if (model.Name.Length > 50) { ModelState.AddModelError(nameof(model.Name), "The name should be maximum 50 characters."); } var paymentMethodId = walletId.GetPaymentMethodId(); var n = this.NetworkProvider.GetNetwork <BTCPayNetwork>(paymentMethodId.CryptoCode); if (n is null || paymentMethodId.PaymentType != PaymentTypes.BTCLike || n.ReadonlyWallet) { ModelState.AddModelError(nameof(model.Name), "Pull payments are not supported with this wallet"); } if (!ModelState.IsValid) { return(View(model)); } await _pullPaymentService.CreatePullPayment(new HostedServices.CreatePullPayment() { Name = model.Name, Amount = model.Amount, Currency = model.Currency, StoreId = walletId.StoreId, PaymentMethodIds = new[] { paymentMethodId }, EmbeddedCSS = model.EmbeddedCSS, CustomCSSLink = model.CustomCSSLink }); this.TempData.SetStatusMessageModel(new StatusMessageModel() { Message = "Pull payment request created", Severity = StatusMessageModel.StatusSeverity.Success }); return(RedirectToAction(nameof(PullPayments), new { walletId = walletId.ToString() })); }
public async Task <IActionResult> WalletRescan( [ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId) { if (walletId?.StoreId == null) { return(NotFound()); } var store = await Repository.FindStore(walletId.StoreId, GetUserId()); DerivationStrategy paymentMethod = GetPaymentMethod(walletId, store); if (paymentMethod == null) { return(NotFound()); } var vm = new RescanWalletModel(); vm.IsFullySync = _dashboard.IsFullySynched(walletId.CryptoCode, out var unused); // We need to ensure it is segwit, // because hardware wallet support need the parent transactions to sign, which NBXplorer don't have. (Nor does a pruned node) vm.IsSegwit = paymentMethod.DerivationStrategyBase.IsSegwit(); vm.IsServerAdmin = User.Claims.Any(c => c.Type == Policies.CanModifyServerSettings.Key && c.Value == "true"); vm.IsSupportedByCurrency = _dashboard.Get(walletId.CryptoCode)?.Status?.BitcoinStatus?.Capabilities?.CanScanTxoutSet == true; var explorer = ExplorerClientProvider.GetExplorerClient(walletId.CryptoCode); var scanProgress = await explorer.GetScanUTXOSetInformationAsync(paymentMethod.DerivationStrategyBase); if (scanProgress != null) { vm.PreviousError = scanProgress.Error; if (scanProgress.Status == ScanUTXOStatus.Queued || scanProgress.Status == ScanUTXOStatus.Pending) { if (scanProgress.Progress == null) { vm.Progress = 0; } else { vm.Progress = scanProgress.Progress.OverallProgress; vm.RemainingTime = TimeSpan.FromSeconds(scanProgress.Progress.RemainingSeconds).PrettyPrint(); } } if (scanProgress.Status == ScanUTXOStatus.Complete) { vm.LastSuccess = scanProgress.Progress; vm.TimeOfScan = (scanProgress.Progress.CompletedAt.Value - scanProgress.Progress.StartedAt).PrettyPrint(); } } return(View(vm)); }
public void GoToWallet(WalletId walletId = null, WalletsNavPages navPages = WalletsNavPages.Send) { walletId ??= WalletId; Driver.Navigate().GoToUrl(new Uri(ServerUri, $"wallets/{walletId}")); if (navPages == WalletsNavPages.PSBT) { Driver.FindElement(By.Id("WalletNav-Send")).Click(); Driver.FindElement(By.Id("PSBT")).Click(); } else if (navPages != WalletsNavPages.Transactions) { Driver.FindElement(By.Id($"WalletNav-{navPages}")).Click(); } }
public async Task <IActionResult> WalletSettings(string storeId, string cryptoCode) { var checkResult = IsAvailable(cryptoCode, out var store, out var network); if (checkResult != null) { return(checkResult); } var derivation = GetExistingDerivationStrategy(cryptoCode, store); if (derivation == null) { return(NotFound()); } var storeBlob = store.GetStoreBlob(); var excludeFilters = storeBlob.GetExcludedPaymentMethods(); (bool canUseHotWallet, bool rpcImport) = await CanUseHotWallet(); var client = _ExplorerProvider.GetExplorerClient(network); var vm = new WalletSettingsViewModel { StoreId = storeId, CryptoCode = cryptoCode, WalletId = new WalletId(storeId, cryptoCode), Enabled = !excludeFilters.Match(derivation.PaymentId), Network = network, IsHotWallet = derivation.IsHotWallet, Source = derivation.Source, RootFingerprint = derivation.GetSigningAccountKeySettings().RootFingerprint.ToString(), DerivationScheme = derivation.AccountDerivation.ToString(), DerivationSchemeInput = derivation.AccountOriginal, KeyPath = derivation.GetSigningAccountKeySettings().AccountKeyPath?.ToString(), UriScheme = derivation.Network.NBitcoinNetwork.UriScheme, Label = derivation.Label, SelectedSigningKey = derivation.SigningKey.ToString(), NBXSeedAvailable = derivation.IsHotWallet && canUseHotWallet && !string.IsNullOrEmpty(await client.GetMetadataAsync <string>(derivation.AccountDerivation, WellknownMetadataKeys.MasterHDKey)), AccountKeys = derivation.AccountKeySettings .Select(e => new WalletSettingsAccountKeyViewModel { AccountKey = e.AccountKey.ToString(), MasterFingerprint = e.RootFingerprint is HDFingerprint fp ? fp.ToString() : null, AccountKeyPath = e.AccountKeyPath == null ? "" : $"m/{e.AccountKeyPath}" }).ToList(),
private async Task <IActionResult> RedirectToWalletTransaction(WalletId walletId, Transaction transaction) { var network = NetworkProvider.GetNetwork <BTCPayNetwork>(walletId.CryptoCode); if (transaction != null) { var wallet = _walletProvider.GetWallet(network); var derivationSettings = await GetDerivationSchemeSettings(walletId); wallet.InvalidateCache(derivationSettings.AccountDerivation); StatusMessage = $"Transaction broadcasted successfully ({transaction.GetHash().ToString()})"; } return(RedirectToAction(nameof(WalletTransactions))); }
private DerivationStrategy GetPaymentMethod(WalletId walletId, StoreData store) { if (store == null || !store.HasClaim(Policies.CanModifyStoreSettings.Key)) { return(null); } var paymentMethod = store .GetSupportedPaymentMethods(_NetworkProvider) .OfType <DerivationStrategy>() .FirstOrDefault(p => p.PaymentId.PaymentType == Payments.PaymentTypes.BTCLike && p.PaymentId.CryptoCode == walletId.CryptoCode); return(paymentMethod); }
public async Task <IActionResult> WalletPSBT( [ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId, WalletPSBTViewModel vm, string command = null) { var network = NetworkProvider.GetNetwork(walletId.CryptoCode); var psbt = await vm.GetPSBT(network.NBitcoinNetwork); if (psbt == null) { ModelState.AddModelError(nameof(vm.PSBT), "Invalid PSBT"); return(View(vm)); } switch (command) { case null: vm.Decoded = psbt.ToString(); ModelState.Remove(nameof(vm.PSBT)); ModelState.Remove(nameof(vm.FileName)); ModelState.Remove(nameof(vm.UploadedPSBTFile)); vm.PSBT = psbt.ToBase64(); vm.FileName = vm.UploadedPSBTFile?.FileName; return(View(vm)); case "ledger": return(ViewWalletSendLedger(psbt)); case "seed": return(SignWithSeed(walletId, psbt.ToBase64())); case "broadcast": { return(await WalletPSBTReady(walletId, psbt.ToBase64())); } case "combine": ModelState.Remove(nameof(vm.PSBT)); return(View(nameof(WalletPSBTCombine), new WalletPSBTCombineViewModel() { OtherPSBT = psbt.ToBase64() })); case "save-psbt": return(FilePSBT(psbt, vm.FileName)); default: return(View(vm)); } }
public async Task <IActionResult> WalletSign([ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId, WalletPSBTViewModel vm, string returnUrl = null, string command = null) { var network = NetworkProvider.GetNetwork <BTCPayNetwork>(walletId.CryptoCode); if (returnUrl is null) { returnUrl = Url.Action(nameof(WalletTransactions), new { walletId }); } var psbt = await vm.GetPSBT(network.NBitcoinNetwork); if (psbt is null || vm.InvalidPSBT) { ModelState.AddModelError(nameof(vm.PSBT), "Invalid PSBT"); return(View("WalletSigningOptions", new WalletSigningOptionsModel(vm.SigningContext, returnUrl))); } switch (command) { case "vault": return(ViewVault(walletId, vm.SigningContext)); case "seed": return(SignWithSeed(walletId, vm.SigningContext)); case "decode": return(await WalletPSBT(walletId, vm, "decode")); default: break; } if (await CanUseHotWallet()) { var derivationScheme = GetDerivationSchemeSettings(walletId); if (derivationScheme.IsHotWallet) { var extKey = await ExplorerClientProvider.GetExplorerClient(walletId.CryptoCode) .GetMetadataAsync <string>(derivationScheme.AccountDerivation, WellknownMetadataKeys.MasterHDKey); if (extKey != null) { return(await SignWithSeed(walletId, new SignWithSeedViewModel { SeedOrKey = extKey, SigningContext = vm.SigningContext })); } } } return(View("WalletSigningOptions", new WalletSigningOptionsModel(vm.SigningContext, returnUrl))); }
public ActionResult GenerateWalletConfirm(string storeId, string cryptoCode) { var checkResult = IsAvailable(cryptoCode, out _, out var network); if (checkResult != null) { return(checkResult); } TempData[WellKnownTempData.SuccessMessage] = $"Wallet settings for {network.CryptoCode} have been updated."; var walletId = new WalletId(storeId, cryptoCode); return(RedirectToAction(nameof(UIWalletsController.WalletTransactions), "UIWallets", new { walletId })); }
public async Task CanUseCoinSelection() { using (var s = SeleniumTester.Create()) { await s.StartAsync(); var userId = s.RegisterNewUser(true); var storeId = s.CreateNewStore().storeId; s.GenerateWallet("BTC", "", false, true); var walletId = new WalletId(storeId, "BTC"); s.GoToWalletReceive(walletId); s.Driver.FindElement(By.Id("generateButton")).Click(); var addressStr = s.Driver.FindElement(By.Id("vue-address")).GetProperty("value"); var address = BitcoinAddress.Create(addressStr, ((BTCPayNetwork)s.Server.NetworkProvider.GetNetwork("BTC")).NBitcoinNetwork); await s.Server.ExplorerNode.GenerateAsync(1); for (int i = 0; i < 6; i++) { await s.Server.ExplorerNode.SendToAddressAsync(address, Money.Coins(1.0m)); } var targetTx = await s.Server.ExplorerNode.SendToAddressAsync(address, Money.Coins(1.2m)); var tx = await s.Server.ExplorerNode.GetRawTransactionAsync(targetTx); var spentOutpoint = new OutPoint(targetTx, tx.Outputs.FindIndex(txout => txout.Value == Money.Coins(1.2m))); await s.Server.ExplorerNode.GenerateAsync(1); s.GoToWalletSend(walletId); s.Driver.FindElement(By.Id("advancedSettings")).Click(); s.Driver.FindElement(By.Id("toggleInputSelection")).Click(); s.Driver.WaitForElement(By.Id(spentOutpoint.ToString())); var el = s.Driver.FindElement(By.Id(spentOutpoint.ToString())); s.Driver.FindElement(By.Id(spentOutpoint.ToString())).Click(); var bob = new Key().PubKey.Hash.GetAddress(Network.RegTest); SetTransactionOutput(s, 0, bob, 0.3m); s.Driver.FindElement(By.Id("SendMenu")).Click(); s.Driver.FindElement(By.Id("spendWithNBxplorer")).Click(); s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).ForceClick(); var happyElement = s.AssertHappyMessage(); var happyText = happyElement.Text; var txid = Regex.Match(happyText, @"\((.*)\)").Groups[1].Value; tx = await s.Server.ExplorerNode.GetRawTransactionAsync(new uint256(txid)); Assert.Single(tx.Inputs); Assert.Equal(spentOutpoint, tx.Inputs[0].PrevOut); } }
public Mnemonic GenerateWallet(string cryptoCode = "BTC", string seed = "", bool importkeys = false, bool privkeys = false, ScriptPubKeyType format = ScriptPubKeyType.Segwit) { Driver.FindElement(By.Id($"Modify{cryptoCode}")).Click(); // Replace previous wallet case if (Driver.PageSource.Contains("id=\"ChangeWalletLink\"")) { Driver.FindElement(By.Id("ChangeWalletLink")).Click(); Driver.FindElement(By.Id("continue")).Click(); } if (string.IsNullOrEmpty(seed)) { var option = privkeys ? "Hotwallet" : "Watchonly"; Logs.Tester.LogInformation($"Generating new seed ({option})"); Driver.FindElement(By.Id("GenerateWalletLink")).Click(); Driver.FindElement(By.Id($"Generate{option}Link")).Click(); } else { Logs.Tester.LogInformation("Progressing with existing seed"); Driver.FindElement(By.Id("ImportWalletOptionsLink")).Click(); Driver.FindElement(By.Id("ImportSeedLink")).Click(); Driver.FindElement(By.Id("ExistingMnemonic")).SendKeys(seed); Driver.SetCheckbox(By.Id("SavePrivateKeys"), privkeys); } Driver.FindElement(By.Id("ScriptPubKeyType")).Click(); Driver.FindElement(By.CssSelector($"#ScriptPubKeyType option[value={format}]")).Click(); Driver.ToggleCollapse("AdvancedSettings"); Driver.SetCheckbox(By.Id("ImportKeysToRPC"), importkeys); Driver.FindElement(By.Id("Continue")).Click(); // Seed backup page FindAlertMessage(); if (string.IsNullOrEmpty(seed)) { seed = Driver.FindElements(By.Id("RecoveryPhrase")).First().GetAttribute("data-mnemonic"); } // Confirm seed backup Driver.FindElement(By.Id("confirm")).Click(); Driver.FindElement(By.Id("submit")).Click(); WalletId = new WalletId(StoreId, cryptoCode); return(new Mnemonic(seed)); }
public IActionResult NewPullPayment([ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId) { if (GetDerivationSchemeSettings(walletId) == null) { return(NotFound()); } return(View(new NewPullPaymentModel { Name = "", Currency = "BTC", CustomCSSLink = "", EmbeddedCSS = "", })); }
public async Task <WalletBlobInfo> GetWalletInfo(WalletId walletId) { if (walletId == null) { throw new ArgumentNullException(nameof(walletId)); } using (var ctx = _ContextFactory.CreateContext()) { var data = await ctx.Wallets .Where(w => w.Id == walletId.ToString()) .Select(w => w) .FirstOrDefaultAsync(); return(data?.GetBlobInfo() ?? new WalletBlobInfo()); } }
public void PayInvoice(WalletId walletId, string invoiceId) { GoToInvoiceCheckout(invoiceId); var bip21 = Driver.FindElement(By.ClassName("payment__details__instruction__open-wallet__btn")) .GetAttribute("href"); Assert.Contains($"{PayjoinClient.BIP21EndpointKey}", bip21); GoToWallet(walletId); Driver.FindElement(By.Id("bip21parse")).Click(); Driver.SwitchTo().Alert().SendKeys(bip21); Driver.SwitchTo().Alert().Accept(); Driver.FindElement(By.Id("SendMenu")).Click(); Driver.FindElement(By.CssSelector("button[value=nbx-seed]")).Click(); Driver.FindElement(By.CssSelector("button[value=broadcast]")).Click(); }
public Mnemonic GenerateWallet(string cryptoCode = "BTC", string seed = "", bool importkeys = false, bool privkeys = false, ScriptPubKeyType format = ScriptPubKeyType.Segwit) { Driver.FindElement(By.Id($"Modify{cryptoCode}")).Click(); // Modify case if (Driver.PageSource.Contains("id=\"change-wallet-link\"")) { Driver.FindElement(By.Id("change-wallet-link")).Click(); } if (string.IsNullOrEmpty(seed)) { var option = privkeys ? "hotwallet" : "watchonly"; Logs.Tester.LogInformation($"Generating new seed ({option})"); Driver.FindElement(By.Id("generate-wallet-link")).Click(); Driver.FindElement(By.Id($"generate-{option}-link")).Click(); } else { Logs.Tester.LogInformation("Progressing with existing seed"); Driver.FindElement(By.Id("import-wallet-options-link")).Click(); Driver.FindElement(By.Id("import-seed-link")).Click(); Driver.FindElement(By.Id("ExistingMnemonic")).SendKeys(seed); SetCheckbox(Driver.FindElement(By.Id("SavePrivateKeys")), privkeys); } Driver.FindElement(By.Id("ScriptPubKeyType")).Click(); Driver.FindElement(By.CssSelector($"#ScriptPubKeyType option[value={format}]")).Click(); Driver.FindElement(By.Id("advanced-settings-button")).Click(); SetCheckbox(Driver.FindElement(By.Id("ImportKeysToRPC")), importkeys); Driver.FindElement(By.Id("advanced-settings-button")).Click(); // close settings again , otherwise the button might not be clickable for Selenium Logs.Tester.LogInformation("Trying to click Continue button"); Driver.FindElement(By.Id("Continue")).Click(); // Seed backup page FindAlertMessage(); if (string.IsNullOrEmpty(seed)) { seed = Driver.FindElements(By.Id("recovery-phrase")).First().GetAttribute("data-mnemonic"); } // Confirm seed backup Driver.FindElement(By.Id("confirm")).Click(); Driver.FindElement(By.Id("submit")).Click(); WalletId = new WalletId(StoreId, cryptoCode); return(new Mnemonic(seed)); }
public async Task <IActionResult> WalletRescan( [ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId) { if (walletId?.StoreId == null) { return(NotFound()); } DerivationSchemeSettings paymentMethod = GetDerivationSchemeSettings(walletId); if (paymentMethod == null) { return(NotFound()); } var vm = new RescanWalletModel(); vm.IsFullySync = _dashboard.IsFullySynched(walletId.CryptoCode, out var unused); vm.IsServerAdmin = (await _authorizationService.AuthorizeAsync(User, Policies.CanModifyServerSettings.Key)).Succeeded; vm.IsSupportedByCurrency = _dashboard.Get(walletId.CryptoCode)?.Status?.BitcoinStatus?.Capabilities?.CanScanTxoutSet == true; var explorer = ExplorerClientProvider.GetExplorerClient(walletId.CryptoCode); var scanProgress = await explorer.GetScanUTXOSetInformationAsync(paymentMethod.AccountDerivation); if (scanProgress != null) { vm.PreviousError = scanProgress.Error; if (scanProgress.Status == ScanUTXOStatus.Queued || scanProgress.Status == ScanUTXOStatus.Pending) { if (scanProgress.Progress == null) { vm.Progress = 0; } else { vm.Progress = scanProgress.Progress.OverallProgress; vm.RemainingTime = TimeSpan.FromSeconds(scanProgress.Progress.RemainingSeconds).PrettyPrint(); } } if (scanProgress.Status == ScanUTXOStatus.Complete) { vm.LastSuccess = scanProgress.Progress; vm.TimeOfScan = (scanProgress.Progress.CompletedAt.Value - scanProgress.Progress.StartedAt).PrettyPrint(); } } return(View(vm)); }
public async Task <IActionResult> WalletTransactions( [ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId, string labelFilter = null) { DerivationSchemeSettings paymentMethod = GetDerivationSchemeSettings(walletId); if (paymentMethod == null) { return(NotFound()); } var wallet = _walletProvider.GetWallet(paymentMethod.Network); var walletBlobAsync = WalletRepository.GetWalletInfo(walletId); var walletTransactionsInfoAsync = WalletRepository.GetWalletTransactionsInfo(walletId); var transactions = await wallet.FetchTransactions(paymentMethod.AccountDerivation); var walletBlob = await walletBlobAsync; var walletTransactionsInfo = await walletTransactionsInfoAsync; var model = new ListTransactionsViewModel(); foreach (var tx in transactions.UnconfirmedTransactions.Transactions.Concat(transactions.ConfirmedTransactions.Transactions).ToArray()) { var vm = new ListTransactionsViewModel.TransactionViewModel(); vm.Id = tx.TransactionId.ToString(); vm.Link = string.Format(CultureInfo.InvariantCulture, paymentMethod.Network.BlockExplorerLink, vm.Id); vm.Timestamp = tx.Timestamp; vm.Positive = tx.BalanceChange >= Money.Zero; vm.Balance = tx.BalanceChange.ToString(); vm.IsConfirmed = tx.Confirmations != 0; if (walletTransactionsInfo.TryGetValue(tx.TransactionId.ToString(), out var transactionInfo)) { var labels = walletBlob.GetLabels(transactionInfo); vm.Labels.AddRange(labels); model.Labels.AddRange(labels); vm.Comment = transactionInfo.Comment; } if (labelFilter == null || vm.Labels.Any(l => l.Value.Equals(labelFilter, StringComparison.OrdinalIgnoreCase))) { model.Transactions.Add(vm); } } model.Transactions = model.Transactions.OrderByDescending(t => t.Timestamp).ToList(); return(View(model)); }
public async Task<IActionResult> WalletPSBT([ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId, WalletPSBTViewModel vm) { var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode); vm.CryptoCode = network.CryptoCode; vm.NBXSeedAvailable = await CanUseHotWallet() && !string.IsNullOrEmpty(await ExplorerClientProvider.GetExplorerClient(network) .GetMetadataAsync<string>(GetDerivationSchemeSettings(walletId).AccountDerivation, WellknownMetadataKeys.Mnemonic)); if (await vm.GetPSBT(network.NBitcoinNetwork) is PSBT psbt) { vm.Decoded = psbt.ToString(); vm.PSBT = psbt.ToBase64(); vm.PSBTHex = psbt.ToHex(); } return View(nameof(WalletPSBT), vm ?? new WalletPSBTViewModel() { CryptoCode = walletId.CryptoCode }); }
public async Task <IActionResult> ShowOnChainWalletTransactions( string storeId, string cryptoCode, [FromQuery] TransactionStatus[]?statusFilter = null, [FromQuery] int skip = 0, [FromQuery] int limit = int.MaxValue ) { if (IsInvalidWalletRequest(cryptoCode, out var network, out var derivationScheme, out var actionResult)) { return(actionResult); } var wallet = _btcPayWalletProvider.GetWallet(network); var walletId = new WalletId(storeId, cryptoCode); var walletTransactionsInfoAsync = await _walletRepository.GetWalletTransactionsInfo(walletId); var txs = await wallet.FetchTransactions(derivationScheme.AccountDerivation); var filteredFlatList = new List <TransactionInformation>(); if (statusFilter is null || !statusFilter.Any() || statusFilter.Contains(TransactionStatus.Confirmed)) { filteredFlatList.AddRange(txs.ConfirmedTransactions.Transactions); } if (statusFilter is null || !statusFilter.Any() || statusFilter.Contains(TransactionStatus.Unconfirmed)) { filteredFlatList.AddRange(txs.UnconfirmedTransactions.Transactions); } if (statusFilter is null || !statusFilter.Any() || statusFilter.Contains(TransactionStatus.Replaced)) { filteredFlatList.AddRange(txs.ReplacedTransactions.Transactions); } var result = filteredFlatList.Skip(skip).Take(limit).Select(information => { walletTransactionsInfoAsync.TryGetValue(information.TransactionId.ToString(), out var transactionInfo); return(ToModel(transactionInfo, information, wallet)); }).ToList(); return(Ok(result)); }
private async Task <IActionResult> TryHandleSigningCommands(WalletId walletId, PSBT psbt, string command, SigningContextModel signingContext, string actionBack) { signingContext.PSBT = psbt.ToBase64(); switch (command) { case "sign": var routeBack = new Dictionary <string, string> { { "action", actionBack }, { "walletId", walletId.ToString() } }; return(View("WalletSigningOptions", new WalletSigningOptionsModel(signingContext, routeBack))); case "vault": return(ViewVault(walletId, signingContext)); case "seed": return(SignWithSeed(walletId, signingContext)); case "nbx-seed": if (await CanUseHotWallet()) { var derivationScheme = GetDerivationSchemeSettings(walletId); if (derivationScheme.IsHotWallet) { var extKey = await ExplorerClientProvider.GetExplorerClient(walletId.CryptoCode) .GetMetadataAsync <string>(derivationScheme.AccountDerivation, WellknownMetadataKeys.MasterHDKey); return(SignWithSeed(walletId, new SignWithSeedViewModel { SeedOrKey = extKey, SigningContext = signingContext })); } } TempData.SetStatusMessageModel(new StatusMessageModel { Severity = StatusMessageModel.StatusSeverity.Error, Message = "NBX seed functionality is not available" }); break; } return(null); }
public static string GetImplicitStoreId(this HttpContext httpContext) { // 1. Check in the routeData var routeData = httpContext.GetRouteData(); string storeId = null; if (routeData != null) { if (routeData.Values.TryGetValue("storeId", out var v)) { storeId = v as string; } } if (storeId == null) { if (httpContext.Request.Query.TryGetValue("storeId", out var sv)) { storeId = sv.FirstOrDefault(); } } // 2. Check in forms if (storeId == null) { if (httpContext.Request.HasFormContentType && httpContext.Request.Form != null && httpContext.Request.Form.TryGetValue("storeId", out var sv)) { storeId = sv.FirstOrDefault(); } } // 3. Checks in walletId if (storeId == null && routeData != null) { if (routeData.Values.TryGetValue("walletId", out var walletId) && WalletId.TryParse((string)walletId, out var w)) { storeId = w.StoreId; } } return(storeId); }
private IActionResult RedirectToWalletPSBT(WalletId walletId, PSBT psbt, string fileName = null) { var vm = new PostRedirectViewModel() { AspController = "Wallets", AspAction = nameof(WalletPSBT), Parameters = { new KeyValuePair <string, string>("psbt", psbt.ToBase64()) } }; if (!string.IsNullOrEmpty(fileName)) { vm.Parameters.Add(new KeyValuePair <string, string>("fileName", fileName)); } return(View("PostRedirect", vm)); }