diff --git a/Plugins/BTCPayServer.Plugins.Wabisabi/BTCPayWallet.cs b/Plugins/BTCPayServer.Plugins.Wabisabi/BTCPayWallet.cs index 131d907..e621a39 100644 --- a/Plugins/BTCPayServer.Plugins.Wabisabi/BTCPayWallet.cs +++ b/Plugins/BTCPayServer.Plugins.Wabisabi/BTCPayWallet.cs @@ -336,6 +336,7 @@ public class BTCPayWallet : IWallet, IDestinationProvider { var stopwatch = new Stopwatch(); stopwatch.Start(); + var txHash = result.UnsignedCoinJoin.GetHash(); var kp = await ExplorerClient.GetMetadataAsync(DerivationScheme, WellknownMetadataKeys.AccountKeyPath); @@ -409,7 +410,7 @@ public class BTCPayWallet : IWallet, IDestinationProvider { Round = result.RoundId.ToString(), CoordinatorName = coordinatorName, - Transaction = result.UnsignedCoinJoin.GetHash().ToString(), + Transaction = txHash.ToString(), CoinsIn = smartTx.WalletInputs.Select(coin => new CoinjoinData.CoinjoinDataCoin() { AnonymitySet = coin.AnonymitySet, @@ -455,12 +456,12 @@ public class BTCPayWallet : IWallet, IDestinationProvider { await _walletRepository.AddWalletTransactionAttachment( new WalletId(storeIdForutxo, "BTC"), - result.UnsignedCoinJoin.GetHash(), + txHash, new List() { new Attachment("coinjoin", result.RoundId.ToString(), JObject.FromObject(new CoinjoinData() { - Transaction = result.UnsignedCoinJoin.GetHash().ToString(), + Transaction = txHash.ToString(), Round = result.RoundId.ToString(), CoinsOut = mixedCoins.Select(coin => new CoinjoinData.CoinjoinDataCoin() { diff --git a/Plugins/BTCPayServer.Plugins.Wabisabi/Views/Shared/Wabisabi/WabisabiWalletSend.cshtml b/Plugins/BTCPayServer.Plugins.Wabisabi/Views/Shared/Wabisabi/WabisabiWalletSend.cshtml new file mode 100644 index 0000000..5997a23 --- /dev/null +++ b/Plugins/BTCPayServer.Plugins.Wabisabi/Views/Shared/Wabisabi/WabisabiWalletSend.cshtml @@ -0,0 +1,89 @@ +@using BTCPayServer.Security +@using NBitcoin +@using BTCPayServer.Abstractions.Contracts +@using BTCPayServer.Plugins.Wabisabi +@using WalletWasabi.Blockchain.Analysis +@model BTCPayServer.Models.WalletViewModels.WalletSendModel + +@inject ContentSecurityPolicies contentSecurityPolicies +@inject WalletProvider _walletProvider +@inject IScopeProvider ScopeProvider +@{ + var nonce = RandomUtils.GetUInt256().ToString().Substring(0, 32); + contentSecurityPolicies.Add("script-src", $"'nonce-{nonce}'"); + contentSecurityPolicies.AllowUnsafeHashes(); + var storeId = ScopeProvider.GetCurrentStoreId(); + var w = await _walletProvider.GetWalletAsync(storeId); +} + +@if (w is not null) +{ + +
+
+ +
+ +
+ + @foreach (var stdDenom in BlockchainAnalyzer.StdDenoms) + { + var num = new Money(stdDenom).ToDecimal(MoneyUnit.BTC); + + } + + + + +} \ No newline at end of file diff --git a/Plugins/BTCPayServer.Plugins.Wabisabi/WabisabiPlugin.cs b/Plugins/BTCPayServer.Plugins.Wabisabi/WabisabiPlugin.cs index 3ea5979..4170cb6 100644 --- a/Plugins/BTCPayServer.Plugins.Wabisabi/WabisabiPlugin.cs +++ b/Plugins/BTCPayServer.Plugins.Wabisabi/WabisabiPlugin.cs @@ -94,6 +94,8 @@ public class WabisabiPlugin : BaseBTCPayServerPlugin "store-integrations-nav")); applicationBuilder.AddSingleton(new UIExtension("Wabisabi/WabisabiDashboard", "dashboard")); + applicationBuilder.AddSingleton(new UIExtension("Wabisabi/WabisabiWalletSend", + "onchain-wallet-send")); applicationBuilder.AddSingleton(); Logger.SetMinimumLevel(LogLevel.Info); diff --git a/Plugins/BTCPayServer.Plugins.Wabisabi/WabisabiService.cs b/Plugins/BTCPayServer.Plugins.Wabisabi/WabisabiService.cs index f6377e0..9bf6b2b 100644 --- a/Plugins/BTCPayServer.Plugins.Wabisabi/WabisabiService.cs +++ b/Plugins/BTCPayServer.Plugins.Wabisabi/WabisabiService.cs @@ -34,7 +34,6 @@ namespace BTCPayServer.Plugins.Wabisabi _walletRepository = walletRepository; _payoutProcessorService = payoutProcessorService; _eventAggregator = eventAggregator; - _eventAggregator.Subscribe() } public async Task GetWabisabiForStore(string storeId) diff --git a/Plugins/BTCPayServer.Plugins.Wabisabi/WabisabiStoreController.cs b/Plugins/BTCPayServer.Plugins.Wabisabi/WabisabiStoreController.cs index b5b6aff..d9703e4 100644 --- a/Plugins/BTCPayServer.Plugins.Wabisabi/WabisabiStoreController.cs +++ b/Plugins/BTCPayServer.Plugins.Wabisabi/WabisabiStoreController.cs @@ -11,6 +11,8 @@ using BTCPayServer.Abstractions.Contracts; using BTCPayServer.Client; using BTCPayServer.Client.Models; using BTCPayServer.Common; +using BTCPayServer.Filters; +using BTCPayServer.Models.WalletViewModels; using BTCPayServer.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -90,7 +92,7 @@ namespace BTCPayServer.Plugins.Wabisabi if (Uri.TryCreate(relay, UriKind.Absolute, out var relayUri)) { - ViewBag.DiscoveredCoordinators =await Nostr.Discover(relayUri, + ViewBag.DiscoveredCoordinators = await Nostr.Discover(relayUri, _explorerClientProvider.GetExplorerClient("BTC").Network.NBitcoinNetwork, coordSettings.Key?.CreateXOnlyPubKey().ToHex(), CancellationToken.None); } @@ -177,10 +179,12 @@ namespace BTCPayServer.Plugins.Wabisabi return View(vm); } } + [Route("coinjoins")] - public async Task ListCoinjoins(string storeId, CoinjoinsViewModel viewModel, [FromServices] WalletRepository walletRepository) + public async Task ListCoinjoins(string storeId, CoinjoinsViewModel viewModel, + [FromServices] WalletRepository walletRepository) { - var objects =await _WabisabiService.GetCoinjoinHistory(storeId); + var objects = await _WabisabiService.GetCoinjoinHistory(storeId); viewModel ??= new CoinjoinsViewModel(); viewModel.Coinjoins = objects @@ -189,7 +193,8 @@ namespace BTCPayServer.Plugins.Wabisabi viewModel.Total = objects.Count(); return View(viewModel); } - + + [XFrameOptions(XFrameOptionsAttribute.XFrameOptions.SameOrigin)] [HttpGet("spend")] public async Task Spend(string storeId) { @@ -201,6 +206,7 @@ namespace BTCPayServer.Plugins.Wabisabi return View(new SpendViewModel() { }); } + [XFrameOptions(XFrameOptionsAttribute.XFrameOptions.SameOrigin)] [HttpPost("spend")] public async Task Spend(string storeId, SpendViewModel spendViewModel, string command) { @@ -274,7 +280,7 @@ namespace BTCPayServer.Plugins.Wabisabi { if (spendViewModel.SelectedCoins?.Any() is true) { - coins = (CoinsView)coins.FilterBy(coin => + coins = (CoinsView) coins.FilterBy(coin => spendViewModel.SelectedCoins.Contains(coin.Outpoint.ToString())); } } @@ -290,11 +296,11 @@ namespace BTCPayServer.Plugins.Wabisabi var defaultCoinSelector = new DefaultCoinSelector(); var defaultSelection = (defaultCoinSelector.Select(coins.Select(coin => coin.Coin).ToArray(), - new Money((decimal)spendViewModel.Amount, MoneyUnit.BTC)) ?? Array.Empty()) - .ToArray(); + new Money((decimal) spendViewModel.Amount, MoneyUnit.BTC)) ?? Array.Empty()) + .ToArray(); var selector = new SmartCoinSelector(coins.ToList()); var smartSelection = selector.Select(defaultSelection, - new Money((decimal)spendViewModel.Amount, MoneyUnit.BTC)); + new Money((decimal) spendViewModel.Amount, MoneyUnit.BTC)); spendViewModel.SelectedCoins = smartSelection.Select(coin => coin.Outpoint.ToString()).ToArray(); return View(spendViewModel); } @@ -326,7 +332,27 @@ namespace BTCPayServer.Plugins.Wabisabi return View(spendViewModel); } + [HttpGet("select-coins")] + public async Task ComputeCoinSelection(string storeId, decimal amount) + { + if ((await _walletProvider.GetWalletAsync(storeId)) is not BTCPayWallet wallet) + { + return NotFound(); + } + + var coins = await wallet.GetAllCoins(); + var defaultCoinSelector = new DefaultCoinSelector(); + var defaultSelection = + (defaultCoinSelector.Select(coins.Select(coin => coin.Coin).ToArray(), + new Money(amount, MoneyUnit.BTC)) ?? Array.Empty()) + .ToArray(); + var selector = new SmartCoinSelector(coins.ToList()); + var smartSelection = selector.Select(defaultSelection, + new Money((decimal) amount, MoneyUnit.BTC)); + return Ok(smartSelection.Select(coin => coin.Outpoint.ToString()).ToArray()); + } + public class SpendViewModel { public string Destination { get; set; } @@ -334,4 +360,4 @@ namespace BTCPayServer.Plugins.Wabisabi public string[] SelectedCoins { get; set; } } } -} +} \ No newline at end of file