From 6c9c7328bb2e49b7c83904ab6ed7e985be4965b6 Mon Sep 17 00:00:00 2001 From: Andrew Camilleri Date: Thu, 13 May 2021 10:50:08 +0200 Subject: [PATCH] =?UTF-8?q?Fix:=20Payout=20Transaction=20not=20matching=20?= =?UTF-8?q?when=20rate=20provided=20longer=20decima=E2=80=A6=20(#2518)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix: Payout Transaction not matching when rate provided longer decimal precision fixes #2513 most likely * Do not sue rounding and ensure crypto amount is saved with correct decimal places * Round pull payment payout to the nearest digit supported by network Co-authored-by: nicolas.dorier --- BTCPayServer.Tests/GreenfieldAPITests.cs | 19 +++++++++++++++++++ .../BitcoinLike/BitcoinLikePayoutHandler.cs | 5 ++++- .../PullPaymentHostedService.cs | 6 ++++-- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/BTCPayServer.Tests/GreenfieldAPITests.cs b/BTCPayServer.Tests/GreenfieldAPITests.cs index f903aa073..558c0c6a3 100644 --- a/BTCPayServer.Tests/GreenfieldAPITests.cs +++ b/BTCPayServer.Tests/GreenfieldAPITests.cs @@ -471,6 +471,25 @@ namespace BTCPayServer.Tests { Revision = payout.Revision })); + + // Create one pull payment with an amount of 9 decimals + var test3 = await client.CreatePullPayment(storeId, new Client.Models.CreatePullPaymentRequest() + { + Name = "Test 2", + Amount = 12.303228134m, + Currency = "BTC", + PaymentMethods = new[] { "BTC" } + }); + destination = (await tester.ExplorerNode.GetNewAddressAsync()).ToString(); + payout = await unauthenticated.CreatePayout(test3.Id, new CreatePayoutRequest() + { + Destination = destination, + PaymentMethod = "BTC" + }); + payout = await client.ApprovePayout(storeId, payout.Id, new ApprovePayoutRequest()); + // The payout should round the value of the payment down to the network of the payment method + Assert.Equal(12.30322814m, payout.PaymentMethodAmount); + Assert.Equal(12.303228134m, payout.Amount); } } diff --git a/BTCPayServer/Data/Payouts/BitcoinLike/BitcoinLikePayoutHandler.cs b/BTCPayServer/Data/Payouts/BitcoinLike/BitcoinLikePayoutHandler.cs index edfbf81dc..7a3208cb7 100644 --- a/BTCPayServer/Data/Payouts/BitcoinLike/BitcoinLikePayoutHandler.cs +++ b/BTCPayServer/Data/Payouts/BitcoinLike/BitcoinLikePayoutHandler.cs @@ -227,7 +227,10 @@ public class BitcoinLikePayoutHandler : IPayoutHandler if (!payoutByDestination.TryGetValue(destination.Key, out var payout)) continue; var payoutBlob = payout.GetBlob(_jsonSerializerSettings); - if (destination.Value != payoutBlob.CryptoAmount) + if (payoutBlob.CryptoAmount is null || + // The round up here is not strictly necessary, this is temporary to fix existing payout before we + // were properly roundup the crypto amount + destination.Value != BTCPayServer.Extensions.RoundUp(payoutBlob.CryptoAmount.Value, network.Divisibility)) continue; var proof = ParseProof(payout) as PayoutTransactionOnChainBlob; if (proof is null) diff --git a/BTCPayServer/HostedServices/PullPaymentHostedService.cs b/BTCPayServer/HostedServices/PullPaymentHostedService.cs index a4de13950..a52dd1d2c 100644 --- a/BTCPayServer/HostedServices/PullPaymentHostedService.cs +++ b/BTCPayServer/HostedServices/PullPaymentHostedService.cs @@ -274,13 +274,14 @@ namespace BTCPayServer.HostedServices var cryptoAmount = payoutBlob.Amount / req.Rate; var payoutHandler = _payoutHandlers.First(handler => handler.CanHandle(paymentMethod)); var dest = await payoutHandler.ParseClaimDestination(paymentMethod, payoutBlob.Destination); + decimal minimumCryptoAmount = await payoutHandler.GetMinimumPayoutAmount(paymentMethod, dest); if (cryptoAmount < minimumCryptoAmount) { req.Completion.TrySetResult(PayoutApproval.Result.TooLowAmount); return; } - payoutBlob.CryptoAmount = cryptoAmount; + payoutBlob.CryptoAmount = BTCPayServer.Extensions.RoundUp(cryptoAmount, _networkProvider.GetNetwork(paymentMethod.CryptoCode).Divisibility); payout.SetBlob(payoutBlob, _jsonSerializerSettings); await ctx.SaveChangesAsync(); req.Completion.SetResult(PayoutApproval.Result.Ok); @@ -298,6 +299,7 @@ namespace BTCPayServer.HostedServices DateTimeOffset now = DateTimeOffset.UtcNow; await using var ctx = _dbContextFactory.CreateContext(); var pp = await ctx.PullPayments.FindAsync(req.ClaimRequest.PullPaymentId); + if (pp is null || pp.Archived) { req.Completion.TrySetResult(new ClaimRequest.ClaimResponse(ClaimRequest.ClaimResult.Archived)); @@ -316,7 +318,7 @@ namespace BTCPayServer.HostedServices var ppBlob = pp.GetBlob(); var payoutHandler = _payoutHandlers.FirstOrDefault(handler => handler.CanHandle(req.ClaimRequest.PaymentMethodId)); - if (!ppBlob.SupportedPaymentMethods.Contains(req.ClaimRequest.PaymentMethodId) || payoutHandler is null ) + if (!ppBlob.SupportedPaymentMethods.Contains(req.ClaimRequest.PaymentMethodId) || payoutHandler is null) { req.Completion.TrySetResult(new ClaimRequest.ClaimResponse(ClaimRequest.ClaimResult.PaymentMethodNotSupported)); return;