diff --git a/BTCPayServer/Controllers/UICustodianAccountsController.cs b/BTCPayServer/Controllers/UICustodianAccountsController.cs index 177c45d42..cde96d76e 100644 --- a/BTCPayServer/Controllers/UICustodianAccountsController.cs +++ b/BTCPayServer/Controllers/UICustodianAccountsController.cs @@ -9,15 +9,16 @@ using BTCPayServer.Abstractions.Extensions; using BTCPayServer.Abstractions.Form; using BTCPayServer.Client; using BTCPayServer.Client.Models; -using BTCPayServer.Controllers.Greenfield; using BTCPayServer.Data; using BTCPayServer.Filters; using BTCPayServer.Models.CustodianAccountViewModels; +using BTCPayServer.Payments; using BTCPayServer.Services.Custodian.Client; using BTCPayServer.Services.Rates; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; using Newtonsoft.Json.Linq; using CustodianAccountData = BTCPayServer.Data.CustodianAccountData; using StoreData = BTCPayServer.Data.StoreData; @@ -30,24 +31,28 @@ namespace BTCPayServer.Controllers public class UICustodianAccountsController : Controller { private readonly IEnumerable _custodianRegistry; - private readonly UserManager _userManager; private readonly CustodianAccountRepository _custodianAccountRepository; private readonly CurrencyNameTable _currencyNameTable; private readonly BTCPayServerClient _btcPayServerClient; + private readonly BTCPayNetworkProvider _networkProvider; + private readonly LinkGenerator _linkGenerator; public UICustodianAccountsController( CurrencyNameTable currencyNameTable, UserManager userManager, CustodianAccountRepository custodianAccountRepository, IEnumerable custodianRegistry, - BTCPayServerClient btcPayServerClient + BTCPayServerClient btcPayServerClient, + BTCPayNetworkProvider networkProvider, + LinkGenerator linkGenerator ) { _currencyNameTable = currencyNameTable ?? throw new ArgumentNullException(nameof(currencyNameTable)); - _userManager = userManager; _custodianAccountRepository = custodianAccountRepository; _custodianRegistry = custodianRegistry; _btcPayServerClient = btcPayServerClient; + _networkProvider = networkProvider; + _linkGenerator = linkGenerator; } public string CreatedCustodianAccountId { get; set; } @@ -75,7 +80,7 @@ namespace BTCPayServer.Controllers } [HttpGet("/stores/{storeId}/custodian-accounts/{accountId}.json")] - public async Task ViewCustodianAccountAjax(string storeId, string accountId) + public async Task ViewCustodianAccountJson(string storeId, string accountId) { var vm = new ViewCustodianAccountBalancesViewModel(); var custodianAccount = await _custodianAccountRepository.FindById(storeId, accountId); @@ -93,6 +98,7 @@ namespace BTCPayServer.Controllers var store = GetCurrentStore(); var storeBlob = StoreDataExtensions.GetStoreBlob(store); var defaultCurrency = storeBlob.DefaultCurrency; + vm.StoreId = store.Id; vm.DustThresholdInFiat = 1; vm.StoreDefaultFiat = defaultCurrency; try @@ -126,11 +132,13 @@ namespace BTCPayServer.Controllers var assetBalance = assetBalances[asset]; var tradableAssetPairsList = tradableAssetPairs.Where(o => o.AssetBought == asset || o.AssetSold == asset).ToList(); - var tradableAssetPairsDict = new Dictionary(tradableAssetPairsList.Count); + var tradableAssetPairsDict = + new Dictionary(tradableAssetPairsList.Count); foreach (var assetPair in tradableAssetPairsList) { tradableAssetPairsDict.Add(assetPair.ToString(), assetPair); } + assetBalance.TradableAssetPairs = tradableAssetPairsDict; if (asset.Equals(defaultCurrency)) @@ -178,20 +186,9 @@ namespace BTCPayServer.Controllers } } - vm.CanDeposit = false; if (custodian is ICanDeposit depositableCustodian) { - vm.CanDeposit = true; - var depositablePaymentMethods = depositableCustodian.GetDepositablePaymentMethods(); - foreach (var depositablePaymentMethod in depositablePaymentMethods) - { - var depositableAsset = depositablePaymentMethod.Split("-")[0]; - if (assetBalances.ContainsKey(depositableAsset)) - { - var assetBalance = assetBalances[depositableAsset]; - assetBalance.CanDeposit = true; - } - } + vm.DepositablePaymentMethods = depositableCustodian.GetDepositablePaymentMethods(); } vm.AssetBalances = assetBalances; @@ -385,7 +382,7 @@ namespace BTCPayServer.Controllers } [HttpGet("/stores/{storeId}/custodian-accounts/{accountId}/trade/prepare")] - public async Task GetTradePrepareAjax(string storeId, string accountId, + public async Task GetTradePrepareJson(string storeId, string accountId, [FromQuery] string assetToTrade, [FromQuery] string assetToTradeInto) { if (string.IsNullOrEmpty(assetToTrade) || string.IsNullOrEmpty(assetToTradeInto)) @@ -437,7 +434,7 @@ namespace BTCPayServer.Controllers { var quote = await tradingCustodian.GetQuoteForAssetAsync(assetToTrade, assetToTradeInto, config, default); - + // TODO Ask is normally a higher number than Bid!! Let's check this!! Maybe a Unit Test? vm.Ask = quote.Ask; vm.Bid = quote.Bid; @@ -476,6 +473,105 @@ namespace BTCPayServer.Controllers } } + [HttpGet("/stores/{storeId}/custodian-accounts/{accountId}/deposit/prepare")] + public async Task GetDepositPrepareJson(string storeId, string accountId, + [FromQuery] string paymentMethod) + { + if (string.IsNullOrEmpty(paymentMethod)) + { + return BadRequest(); + } + + DepositPrepareViewModel vm = new(); + var custodianAccount = await _custodianAccountRepository.FindById(storeId, accountId); + + if (custodianAccount == null) + return NotFound(); + + var custodian = _custodianRegistry.GetCustodianByCode(custodianAccount.CustodianCode); + if (custodian == null) + { + // TODO The custodian account is broken. The custodian is no longer available. Maybe delete the custodian account? + return NotFound(); + } + + try + { + if (custodian is ICanDeposit depositableCustodian) + { + var config = custodianAccount.GetBlob(); + + vm.PaymentMethod = paymentMethod; + var depositablePaymentMethods = depositableCustodian.GetDepositablePaymentMethods(); + if (!depositablePaymentMethods.Contains(paymentMethod)) + { + vm.ErrorMessage = $"Payment method \"{paymentMethod}\" is not supported by {custodian.Name}"; + return BadRequest(vm); + } + + try + { + var depositAddressResult = + await depositableCustodian.GetDepositAddressAsync(paymentMethod, config, default); + vm.Address = depositAddressResult.Address; + + var paymentMethodObj = PaymentMethodId.Parse(paymentMethod); + if (paymentMethodObj.IsBTCOnChain) + { + var network = _networkProvider.GetNetwork("BTC"); + var bip21 = network.GenerateBIP21(depositAddressResult.Address, null); + vm.Link = bip21.ToString(); + var paymentMethodId = PaymentMethodId.TryParse(paymentMethod); + if (paymentMethodId != null) + { + var walletId = new WalletId(storeId, paymentMethodId.CryptoCode); + var returnUrl = _linkGenerator.GetUriByAction( + nameof(ViewCustodianAccount), + "UICustodianAccounts", + new { storeId = custodianAccount.StoreId, accountId = custodianAccount.Id }, + Request.Scheme, + Request.Host, + Request.PathBase); + + vm.CryptoImageUrl = GetImage(paymentMethodId, network); + vm.CreateTransactionUrl = _linkGenerator.GetUriByAction( + nameof(UIWalletsController.WalletSend), + "UIWallets", + new { walletId, defaultDestination = vm.Address, returnUrl }, + Request.Scheme, + Request.Host, + Request.PathBase); + } + } + else + { + // TODO support LN + shitcoins + } + } + catch (Exception e) + { + vm.ErrorMessage = e.Message; + return new ObjectResult(vm) { StatusCode = 500 }; + } + } + } + catch (Exception) + { + return BadRequest(); + } + + return Ok(vm); + } + + private string GetImage(PaymentMethodId paymentMethodId, BTCPayNetwork network) + { + // TODO this method was copy-pasted from BTCPayServer.Controllers.UIWalletsController.GetImage(). Maybe refactor this? + var res = paymentMethodId.PaymentType == PaymentTypes.BTCLike + ? Url.Content(network.CryptoImagePath) + : Url.Content(network.LightningImagePath); + return Request.GetRelativePathOrAbsolute(res); + } + private StoreData GetCurrentStore() => HttpContext.GetStoreData(); } } diff --git a/BTCPayServer/Hosting/BTCPayServerServices.cs b/BTCPayServer/Hosting/BTCPayServerServices.cs index 3d18e81a3..63ce6f851 100644 --- a/BTCPayServer/Hosting/BTCPayServerServices.cs +++ b/BTCPayServer/Hosting/BTCPayServerServices.cs @@ -130,10 +130,7 @@ namespace BTCPayServer.Hosting services.TryAddSingleton(); services.TryAddSingleton(); services.AddSingleton(); - - services.TryAddSingleton(); - services.TryAddSingleton(); services.AddSingleton(); services.AddOptions().Configure( (options) => diff --git a/BTCPayServer/Models/CustodianAccountViewModels/AssetBalanceInfo.cs b/BTCPayServer/Models/CustodianAccountViewModels/AssetBalanceInfo.cs index 2c7bbb028..3d665f7be 100644 --- a/BTCPayServer/Models/CustodianAccountViewModels/AssetBalanceInfo.cs +++ b/BTCPayServer/Models/CustodianAccountViewModels/AssetBalanceInfo.cs @@ -15,7 +15,6 @@ public class AssetBalanceInfo public decimal FiatValue { get; set; } public Dictionary TradableAssetPairs { get; set; } public bool CanWithdraw { get; set; } - public bool CanDeposit { get; set; } public string FormattedBid { get; set; } public string FormattedAsk { get; set; } } diff --git a/BTCPayServer/Models/CustodianAccountViewModels/DepositPrepareViewModel.cs b/BTCPayServer/Models/CustodianAccountViewModels/DepositPrepareViewModel.cs new file mode 100644 index 000000000..c340245da --- /dev/null +++ b/BTCPayServer/Models/CustodianAccountViewModels/DepositPrepareViewModel.cs @@ -0,0 +1,11 @@ +namespace BTCPayServer.Models.CustodianAccountViewModels; + +public class DepositPrepareViewModel +{ + public string PaymentMethod { get; set; } + public string Address { get; set; } + public string Link { get; set; } + public string CryptoImageUrl { get; set; } + public string ErrorMessage { get; set; } + public string CreateTransactionUrl { get; set; } +} diff --git a/BTCPayServer/Models/CustodianAccountViewModels/ViewCustodianAccountBalancesViewModel.cs b/BTCPayServer/Models/CustodianAccountViewModels/ViewCustodianAccountBalancesViewModel.cs index 79b515f48..40d954199 100644 --- a/BTCPayServer/Models/CustodianAccountViewModels/ViewCustodianAccountBalancesViewModel.cs +++ b/BTCPayServer/Models/CustodianAccountViewModels/ViewCustodianAccountBalancesViewModel.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; namespace BTCPayServer.Models.CustodianAccountViewModels @@ -8,8 +7,9 @@ namespace BTCPayServer.Models.CustodianAccountViewModels public Dictionary AssetBalances { get; set; } public string AssetBalanceExceptionMessage { get; set; } + public string StoreId { get; set; } public string StoreDefaultFiat { get; set; } public decimal DustThresholdInFiat { get; set; } - public bool CanDeposit { get; set; } + public string[] DepositablePaymentMethods { get; set; } } } diff --git a/BTCPayServer/Views/UICustodianAccounts/ViewCustodianAccount.cshtml b/BTCPayServer/Views/UICustodianAccounts/ViewCustodianAccount.cshtml index 696172419..d0973ada0 100644 --- a/BTCPayServer/Views/UICustodianAccounts/ViewCustodianAccount.cshtml +++ b/BTCPayServer/Views/UICustodianAccounts/ViewCustodianAccount.cshtml @@ -1,4 +1,4 @@ -@using BTCPayServer.Views.Apps +@using BTCPayServer.Views.Apps @using BTCPayServer.Abstractions.Extensions @using BTCPayServer.Abstractions.Custodians @model BTCPayServer.Models.CustodianAccountViewModels.ViewCustodianAccountViewModel @@ -6,6 +6,11 @@ ViewData.SetActivePage(AppsNavPages.Create, "Custodian account: " + @Model?.CustodianAccount.Name); } +@section PageHeadContent +{ + +} + @section PageFootContent { } @@ -15,333 +20,398 @@
-
-