better send tools

This commit is contained in:
Kukks
2023-01-30 17:06:19 +01:00
parent 2c84539bc3
commit 9e67a6ee06
5 changed files with 130 additions and 13 deletions

View File

@@ -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<RootedKeyPath>(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<Attachment>()
{
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()
{

View File

@@ -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)
{
<div style="display: none">
<div id="wabisabitemplate">
<button type="button"
id="privatesend"
class="btn btn-primary"
title="Optimzie coin selection for privacy">
Private coin selection
<i class="fa fa-user-secret"></i>
</button>
</div>
</div>
<datalist id="wabisabidenominations">
@foreach (var stdDenom in BlockchainAnalyzer.StdDenoms)
{
var num = new Money(stdDenom).ToDecimal(MoneyUnit.BTC);
<option value="@num">Privacy suggestion (used in coinjoins)</option>
}
</datalist>
<script type="text/javascript" nonce="@nonce">
document.addEventListener("DOMContentLoaded", function () {
const amountElements = document.querySelectorAll("[name^='Outputs'][name$='Amount']");
amountElements.forEach(value => {
value.setAttribute("list","wabisabidenominations" );
});
document.querySelector("#SignTransaction").insertAdjacentElement("beforeBegin", document.getElementById("wabisabitemplate"))
document.getElementById("privatesend").addEventListener("click", async ev => {
document.getElementById("InputSelection").value = "True";
const amountElements = document.querySelectorAll("[name^='Outputs'][name$='Amount']");
let amount = 0;
amountElements.forEach(value => {
try {
amount+= parseFloat(value.value);
}catch{}
});
if (!amount){
return;
}
const url =@Safe.Json(@Url.Action("ComputeCoinSelection", "WabisabiStore", new { storeId }));
const response = await fetch(`${url}?amount=${amount}`);
const coins = await response.json();
let selectedInputsElement = document.getElementById("SelectedInputs");
if (!selectedInputsElement){
selectedInputsElement = document.createElement("select");
selectedInputsElement.setAttribute("name", "SelectedInputs")
selectedInputsElement.setAttribute("id", "SelectedInputs")
selectedInputsElement.setAttribute("multiple", "multiple")
selectedInputsElement.style.display = "none";
ev.target.insertAdjacentElement("beforeBegin",selectedInputsElement);
}else{
while (selectedInputsElement.options.length > 0) {
selectedInputsElement.remove(0);
}
}
for (const coin of coins) {
selectedInputsElement.add(new Option(coin, coin, true, true),undefined);
}
document.getElementsByTagName("form")[0].submit();
});
});
</script>
}

View File

@@ -94,6 +94,8 @@ public class WabisabiPlugin : BaseBTCPayServerPlugin
"store-integrations-nav"));
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("Wabisabi/WabisabiDashboard",
"dashboard"));
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("Wabisabi/WabisabiWalletSend",
"onchain-wallet-send"));
applicationBuilder.AddSingleton<IPayoutProcessorFactory, WabisabiPayoutProcessor>();
Logger.SetMinimumLevel(LogLevel.Info);

View File

@@ -34,7 +34,6 @@ namespace BTCPayServer.Plugins.Wabisabi
_walletRepository = walletRepository;
_payoutProcessorService = payoutProcessorService;
_eventAggregator = eventAggregator;
_eventAggregator.Subscribe()
}
public async Task<WabisabiStoreSettings> GetWabisabiForStore(string storeId)

View File

@@ -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<IActionResult> ListCoinjoins(string storeId, CoinjoinsViewModel viewModel, [FromServices] WalletRepository walletRepository)
public async Task<IActionResult> 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
@@ -190,6 +194,7 @@ namespace BTCPayServer.Plugins.Wabisabi
return View(viewModel);
}
[XFrameOptions(XFrameOptionsAttribute.XFrameOptions.SameOrigin)]
[HttpGet("spend")]
public async Task<IActionResult> Spend(string storeId)
{
@@ -201,6 +206,7 @@ namespace BTCPayServer.Plugins.Wabisabi
return View(new SpendViewModel() { });
}
[XFrameOptions(XFrameOptionsAttribute.XFrameOptions.SameOrigin)]
[HttpPost("spend")]
public async Task<IActionResult> 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<ICoin>())
new Money((decimal) spendViewModel.Amount, MoneyUnit.BTC)) ?? Array.Empty<ICoin>())
.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,6 +332,26 @@ namespace BTCPayServer.Plugins.Wabisabi
return View(spendViewModel);
}
[HttpGet("select-coins")]
public async Task<IActionResult> 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<ICoin>())
.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
{