Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support non BTC denominated lnurlw/bolt cards in pull payments #5837

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
57 changes: 47 additions & 10 deletions BTCPayServer/Controllers/UILNURLController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
using BTCPayServer.Plugins.Crowdfund;
using BTCPayServer.Plugins.PointOfSale;
using BTCPayServer.Plugins.PointOfSale.Models;
using BTCPayServer.Rating;
using BTCPayServer.Services;
using BTCPayServer.Services.Apps;
using BTCPayServer.Services.Invoices;
Expand Down Expand Up @@ -116,20 +117,37 @@ internal async Task<IActionResult> GetLNURLForPullPayment(string cryptoCode, str
{
return NotFound();
}

var unit = blob.Currency == "SATS" ? LightMoneyUnit.Satoshi : LightMoneyUnit.BTC;
var store = await _storeRepository.FindStore(pp.StoreId);
RateFetcher r;
// var unit = blob.Currency == "SATS" ? LightMoneyUnit.Satoshi : LightMoneyUnit.BTC;
var progress = _pullPaymentHostedService.CalculatePullPaymentProgress(pp, DateTimeOffset.UtcNow);
var remaining = progress.Limit - progress.Completed - progress.Awaiting;
LightMoney balance = null;
switch (blob.Currency)
{
case "SATS":
balance = LightMoney.FromUnit(remaining, LightMoneyUnit.Satoshi);
break;
case "BTC":
balance = LightMoney.FromUnit(remaining, LightMoneyUnit.BTC);
break;
default:

var storeBlob = store.GetStoreBlob();
r = new RateFetcher(null);
var currencyPair = new CurrencyPair(blob.Currency, "BTC");
var rate = await r.FetchRate(currencyPair, storeBlob.GetRateRules(_btcPayNetworkProvider), cancellationToken);

balance = LightMoney.FromUnit(remaining * rate.BidAsk.Center, LightMoneyUnit.BTC);
break;
}
var request = new LNURLWithdrawRequest
{
MaxWithdrawable = LightMoney.FromUnit(remaining, unit),
MaxWithdrawable = balance,
K1 = k1,
BalanceCheck = new Uri(Request.GetCurrentUrl()),
CurrentBalance = LightMoney.FromUnit(remaining, unit),
MinWithdrawable =
LightMoney.FromUnit(
Math.Min(await _lightningLikePayoutHandler.GetMinimumPayoutAmount(pmi, null), remaining),
unit),
CurrentBalance = balance,
MinWithdrawable = LightMoney.Satoshis(Math.Min(1, balance.ToDecimal(LightMoneyUnit.Satoshi))),
Tag = "withdrawRequest",
Callback = new Uri(Request.GetCurrentUrl()),
// It's not `pp.GetBlob().Description` because this would be HTML
Expand All @@ -148,7 +166,7 @@ internal async Task<IActionResult> GetLNURLForPullPayment(string cryptoCode, str

if (result.MinimumAmount < request.MinWithdrawable || result.MinimumAmount > request.MaxWithdrawable)
return BadRequest(new LNUrlStatusResponse { Status = "ERROR", Reason = $"Payment request was not within bounds ({request.MinWithdrawable.ToUnit(LightMoneyUnit.Satoshi)} - {request.MaxWithdrawable.ToUnit(LightMoneyUnit.Satoshi)} sats)" });
var store = await _storeRepository.FindStore(pp.StoreId);

var pm = store!.GetSupportedPaymentMethods(_btcPayNetworkProvider)
.OfType<LightningSupportedPaymentMethod>()
.FirstOrDefault(method => method.PaymentId == pmi);
Expand All @@ -157,13 +175,32 @@ internal async Task<IActionResult> GetLNURLForPullPayment(string cryptoCode, str
return NotFound();
}

decimal? prAmount = null;
switch (blob.Currency)
{
case "SATS":
prAmount = result.MinimumAmount.ToDecimal(LightMoneyUnit.Satoshi);
break;
case "BTC":
prAmount = result.MinimumAmount.ToDecimal(LightMoneyUnit.BTC);
break;
default:

var storeBlob = store.GetStoreBlob();
r = new RateFetcher(null);
var currencyPair = new CurrencyPair(blob.Currency, "BTC");
var rate = await r.FetchRate(currencyPair, storeBlob.GetRateRules(_btcPayNetworkProvider), cancellationToken);

prAmount = result.MinimumAmount.ToDecimal(LightMoneyUnit.BTC) * rate.BidAsk.Center;
break;
}
var claimResponse = await _pullPaymentHostedService.Claim(new ClaimRequest
{
Destination = new BoltInvoiceClaimDestination(pr, result),
PaymentMethodId = pmi,
PullPaymentId = pullPaymentId,
StoreId = pp.StoreId,
Value = result.MinimumAmount.ToDecimal(unit)
Value = prAmount
});

if (claimResponse.Result != ClaimRequest.ClaimResult.Ok)
Expand Down
6 changes: 3 additions & 3 deletions BTCPayServer/HostedServices/PullPaymentHostedService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ public class CreatePullPayment

public class PullPaymentHostedService : BaseAsyncService
{
private readonly string[] _lnurlSupportedCurrencies = { "BTC", "SATS" };
// private readonly string[] _lnurlSupportedCurrencies = { "BTC", "SATS" };

public class CancelRequest
{
public CancelRequest(string pullPaymentId)
Expand Down Expand Up @@ -378,7 +378,7 @@ public bool SupportsLNURL(PullPaymentBlob blob)
var pms = blob.SupportedPaymentMethods.FirstOrDefault(id =>
id.PaymentType == LightningPaymentType.Instance &&
_networkProvider.DefaultNetwork.CryptoCode == id.CryptoCode);
return pms is not null && _lnurlSupportedCurrencies.Contains(blob.Currency);
return pms is not null;
}

public Task<RateResult> GetRate(PayoutData payout, string explicitRateRule, CancellationToken cancellationToken)
Expand Down