Skip to content

Commit

Permalink
fix(core): allow micro payments (#4696)
Browse files Browse the repository at this point in the history
* fix(core): allow micro payments

* fix: integration test

* fix: lint issues
  • Loading branch information
dolcalmi authored Dec 23, 2024
1 parent 43b1037 commit a335b40
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 15 deletions.
7 changes: 5 additions & 2 deletions core/api/src/domain/bitcoin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
InvalidCurrencyBaseAmountError,
InvalidSatoshiAmountError,
} from "@/domain/errors"
import { MAX_SATS, BtcAmountTooLargeError } from "@/domain/shared"
import { MAX_SATS, BtcAmountTooLargeError, WalletCurrency } from "@/domain/shared"

export const SATS_PER_BTC = 10 ** 8

Expand Down Expand Up @@ -57,4 +57,7 @@ export const BtcNetwork = {

// Offchain routing fees are capped at 0.5%
export const FEECAP_BASIS_POINTS = 50n // 100 basis points == 1%
export const FEEMIN = toSats(10) // sats
export const FEECAP_MIN = {
amount: 10n,
currency: WalletCurrency.Btc,
}
21 changes: 14 additions & 7 deletions core/api/src/domain/payments/ln-fees.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FEECAP_BASIS_POINTS } from "@/domain/bitcoin"
import { FEECAP_BASIS_POINTS, FEECAP_MIN } from "@/domain/bitcoin"
import { MaxFeeTooLargeForRoutelessPaymentError } from "@/domain/bitcoin/lightning"
import {
WalletCurrency,
Expand All @@ -14,15 +14,22 @@ const calc = AmountCalculator()
export const LnFees = () => {
const feeCapBasisPoints = FEECAP_BASIS_POINTS

const maxProtocolAndBankFee = <T extends WalletCurrency>(amount: PaymentAmount<T>) => {
if (amount.amount == 0n) {
return amount
}
const maxProtocolAndBankFee = <T extends WalletCurrency>(
amount: PaymentAmount<T>,
): PaymentAmount<T> => {
if (amount.amount === 0n) return amount

const defaultMaxFee = calc.mulBasisPoints(amount, feeCapBasisPoints)

const getMaxAmount = (fee: PaymentAmount<T>) => (fee.amount === 0n ? 1n : fee.amount)

const maxFee = calc.mulBasisPoints(amount, feeCapBasisPoints)
const maxFee =
amount.currency === WalletCurrency.Btc
? getMaxAmount(calc.max(defaultMaxFee, FEECAP_MIN as PaymentAmount<T>)) // allow micro payments
: getMaxAmount(defaultMaxFee)

return {
amount: maxFee.amount === 0n ? 1n : maxFee.amount,
amount: maxFee,
currency: amount.currency,
}
}
Expand Down
5 changes: 2 additions & 3 deletions core/api/test/integration/services/lnd-service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {

import { LND1_PUBKEY, MS_PER_SEC } from "@/config"

import { WalletCurrency } from "@/domain/shared"
import { ONE_SAT, WalletCurrency } from "@/domain/shared"
import { toSats } from "@/domain/bitcoin"
import {
InvoiceNotFoundError,
Expand All @@ -22,7 +22,6 @@ import {
RouteNotFoundError,
decodeInvoice,
} from "@/domain/bitcoin/lightning"
import { LnFees } from "@/domain/payments"

import { LndService } from "@/services/lnd"
import { parseLndErrorDetails } from "@/services/lnd/config"
Expand Down Expand Up @@ -304,7 +303,7 @@ describe("Lnd", () => {
const paid = await lndService.payInvoiceViaPaymentDetails({
decodedInvoice: lnInvoice,
btcPaymentAmount,
maxFeeAmount: LnFees().maxProtocolAndBankFee(btcPaymentAmount),
maxFeeAmount: ONE_SAT,
})
expect(paid).toBeInstanceOf(RouteNotFoundError)
})
Expand Down
37 changes: 34 additions & 3 deletions core/api/test/unit/domain/payments/ln-fees.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { FEECAP_MIN } from "@/domain/bitcoin"
import { AmountCalculator, ONE_CENT, ONE_SAT, WalletCurrency } from "@/domain/shared"
import { LnFees, WalletPriceRatio } from "@/domain/payments"
import { MaxFeeTooLargeForRoutelessPaymentError } from "@/domain/bitcoin/lightning"
Expand Down Expand Up @@ -29,13 +30,43 @@ describe("LnFees", () => {
})

it("handles a small amount", () => {
const btcAmount = {
let btcAmount = {
amount: 1n,
currency: WalletCurrency.Btc,
}
expect(LnFees().maxProtocolAndBankFee(btcAmount)).toEqual({
amount: 1n,
amount: FEECAP_MIN.amount,
currency: WalletCurrency.Btc,
})

btcAmount = {
amount: 1000n,
currency: WalletCurrency.Btc,
}
expect(LnFees().maxProtocolAndBankFee(btcAmount)).toEqual({
amount: FEECAP_MIN.amount,
currency: WalletCurrency.Btc,
})

const usdAmount = {
amount: 1n,
currency: WalletCurrency.Usd,
}
expect(LnFees().maxProtocolAndBankFee(usdAmount)).toEqual({
amount: 1n,
currency: WalletCurrency.Usd,
})
})

it("does not apply FEECAP_MIN to USD amounts", () => {
const usdAmount = {
amount: 1000n,
currency: WalletCurrency.Usd,
}
// With 0.5% fee cap, 1000 cents would result in 5 cents fee
expect(LnFees().maxProtocolAndBankFee(usdAmount)).toEqual({
amount: 5n,
currency: WalletCurrency.Usd,
})
})
})
Expand Down Expand Up @@ -179,7 +210,7 @@ describe("LnFees", () => {
it("fails for a 1 sat large Btc maxFee", () => {
expect(
LnFees().verifyMaxFee({
maxFeeAmount: calc.add(ONE_SAT, ONE_SAT),
maxFeeAmount: calc.add(FEECAP_MIN, ONE_SAT),
btcPaymentAmount: ONE_SAT,
usdPaymentAmount: ONE_CENT,
priceRatio,
Expand Down

0 comments on commit a335b40

Please sign in to comment.