muun-recovery/vendor/github.com/muun/libwallet/fees/fees.go

142 lines
4.3 KiB
Go

package fees
import "github.com/btcsuite/btcutil"
const dustThreshold = 546
type BestRouteFees struct {
MaxCapacity btcutil.Amount
FeeProportionalMillionth uint64
FeeBase btcutil.Amount
}
type FundingOutputPolicies struct {
MaximumDebt btcutil.Amount
PotentialCollect btcutil.Amount
MaxAmountFor0Conf btcutil.Amount
}
type DebtType string
const (
DebtTypeNone DebtType = "NONE"
DebtTypeCollect DebtType = "COLLECT"
DebtTypeLend DebtType = "LEND"
)
type SwapFees struct {
RoutingFee btcutil.Amount
DebtType DebtType
DebtAmount btcutil.Amount
OutputAmount btcutil.Amount
OutputPadding btcutil.Amount
ConfirmationsNeeded uint
}
func (p *FundingOutputPolicies) FundingConfirmations(paymentAmount, lightningFee btcutil.Amount) uint {
totalAmount := paymentAmount + lightningFee
if totalAmount <= p.MaxAmountFor0Conf {
return 0
}
return 1
}
func (p *FundingOutputPolicies) DebtType(paymentAmount, lightningFee btcutil.Amount) DebtType {
numConfirmations := p.FundingConfirmations(paymentAmount, lightningFee)
totalAmount := paymentAmount + lightningFee
if numConfirmations == 0 && totalAmount <= p.MaximumDebt {
return DebtTypeLend
}
if p.PotentialCollect > 0 {
return DebtTypeCollect
}
return DebtTypeNone
}
func (p *FundingOutputPolicies) DebtAmount(paymentAmount, lightningFee btcutil.Amount) btcutil.Amount {
switch p.DebtType(paymentAmount, lightningFee) {
case DebtTypeLend:
return paymentAmount + lightningFee
case DebtTypeCollect:
return p.PotentialCollect
case DebtTypeNone:
return 0
default:
return 0
}
}
func (p *FundingOutputPolicies) MinFundingAmount(paymentAmount, lightningFee btcutil.Amount) btcutil.Amount {
inputAmount := paymentAmount + lightningFee
if p.DebtType(paymentAmount, lightningFee) == DebtTypeCollect {
inputAmount += p.DebtAmount(paymentAmount, lightningFee)
}
return inputAmount
}
func (p *FundingOutputPolicies) FundingOutputAmount(paymentAmount, lightningFee btcutil.Amount) btcutil.Amount {
minAmount := p.MinFundingAmount(paymentAmount, lightningFee)
if minAmount < dustThreshold {
return dustThreshold
}
return minAmount
}
func (p *FundingOutputPolicies) FundingOutputPadding(paymentAmount, lightningFee btcutil.Amount) btcutil.Amount {
minAmount := p.MinFundingAmount(paymentAmount, lightningFee)
outputAmount := p.FundingOutputAmount(paymentAmount, lightningFee)
return outputAmount - minAmount
}
func ComputeSwapFees(amount btcutil.Amount, bestRouteFees []BestRouteFees, policies *FundingOutputPolicies, takeFeeFromAmount bool) *SwapFees {
if takeFeeFromAmount {
// Handle edge cases for TFFA swaps. We don't allow lend for TFFA. This impacts sub-dust
// swaps because we don't allow debt for output padding. Except, the very special case of
// sub-dust TFFA swaps, in which you cant have output padding > 0 since you are using all
// your balance and all your balance is < dust. In this case, since we can't use debt nor
// output padding, if its necessary, the payment is unpayable.
policies = &FundingOutputPolicies{
MaximumDebt: 0,
PotentialCollect: policies.PotentialCollect,
MaxAmountFor0Conf: policies.MaxAmountFor0Conf,
}
}
lightningFee := computeLightningFee(amount, bestRouteFees)
outputPadding := policies.FundingOutputPadding(amount, lightningFee)
offchainFee := lightningFee + outputPadding
outputAmount := amount + offchainFee
debtType := policies.DebtType(amount, lightningFee)
debtAmount := policies.DebtAmount(amount, lightningFee)
if debtType == DebtTypeCollect {
outputAmount += debtAmount
} else if debtType == DebtTypeLend {
outputAmount = 0
}
return &SwapFees{
RoutingFee: lightningFee,
OutputPadding: outputPadding,
DebtType: debtType,
DebtAmount: debtAmount,
ConfirmationsNeeded: policies.FundingConfirmations(amount, lightningFee),
OutputAmount: outputAmount,
}
}
func computeLightningFee(amount btcutil.Amount, bestRouteFees []BestRouteFees) btcutil.Amount {
for _, fee := range bestRouteFees {
if amount <= fee.MaxCapacity {
return fee.ForAmount(amount)
}
}
lastRouteFee := bestRouteFees[len(bestRouteFees)-1]
return lastRouteFee.ForAmount(amount)
}
func (f *BestRouteFees) ForAmount(amount btcutil.Amount) btcutil.Amount {
return (btcutil.Amount(f.FeeProportionalMillionth)*amount)/1000000 + f.FeeBase
}