expected fees

This commit is contained in:
boufni95 2025-11-26 18:57:55 +00:00
parent df4d7464da
commit 9e3086df0d
7 changed files with 48 additions and 15 deletions

View file

@ -1472,12 +1472,14 @@ The nostr server will send back a message response, and inside the body there wi
### PayAppUserInvoiceRequest
- __amount__: _number_
- __debit_npub__: _string_ *this field is optional
- __expected_fees__: _[CumulativeFees](#CumulativeFees)_ *this field is optional
- __invoice__: _string_
- __user_identifier__: _string_
### PayInvoiceRequest
- __amount__: _number_
- __debit_npub__: _string_ *this field is optional
- __expected_fees__: _[CumulativeFees](#CumulativeFees)_ *this field is optional
- __invoice__: _string_
### PayInvoiceResponse

View file

@ -555,15 +555,17 @@ type PayAddressResponse struct {
Txid string `json:"txId"`
}
type PayAppUserInvoiceRequest struct {
Amount int64 `json:"amount"`
Debit_npub string `json:"debit_npub"`
Invoice string `json:"invoice"`
User_identifier string `json:"user_identifier"`
Amount int64 `json:"amount"`
Debit_npub string `json:"debit_npub"`
Expected_fees *CumulativeFees `json:"expected_fees"`
Invoice string `json:"invoice"`
User_identifier string `json:"user_identifier"`
}
type PayInvoiceRequest struct {
Amount int64 `json:"amount"`
Debit_npub string `json:"debit_npub"`
Invoice string `json:"invoice"`
Amount int64 `json:"amount"`
Debit_npub string `json:"debit_npub"`
Expected_fees *CumulativeFees `json:"expected_fees"`
Invoice string `json:"invoice"`
}
type PayInvoiceResponse struct {
Amount_paid int64 `json:"amount_paid"`

View file

@ -3262,15 +3262,17 @@ export const PayAddressResponseValidate = (o?: PayAddressResponse, opts: PayAddr
export type PayAppUserInvoiceRequest = {
amount: number
debit_npub?: string
expected_fees?: CumulativeFees
invoice: string
user_identifier: string
}
export type PayAppUserInvoiceRequestOptionalField = 'debit_npub'
export const PayAppUserInvoiceRequestOptionalFields: PayAppUserInvoiceRequestOptionalField[] = ['debit_npub']
export type PayAppUserInvoiceRequestOptionalField = 'debit_npub' | 'expected_fees'
export const PayAppUserInvoiceRequestOptionalFields: PayAppUserInvoiceRequestOptionalField[] = ['debit_npub', 'expected_fees']
export type PayAppUserInvoiceRequestOptions = OptionsBaseMessage & {
checkOptionalsAreSet?: PayAppUserInvoiceRequestOptionalField[]
amount_CustomCheck?: (v: number) => boolean
debit_npub_CustomCheck?: (v?: string) => boolean
expected_fees_Options?: CumulativeFeesOptions
invoice_CustomCheck?: (v: string) => boolean
user_identifier_CustomCheck?: (v: string) => boolean
}
@ -3284,6 +3286,12 @@ export const PayAppUserInvoiceRequestValidate = (o?: PayAppUserInvoiceRequest, o
if ((o.debit_npub || opts.allOptionalsAreSet || opts.checkOptionalsAreSet?.includes('debit_npub')) && typeof o.debit_npub !== 'string') return new Error(`${path}.debit_npub: is not a string`)
if (opts.debit_npub_CustomCheck && !opts.debit_npub_CustomCheck(o.debit_npub)) return new Error(`${path}.debit_npub: custom check failed`)
if (typeof o.expected_fees === 'object' || opts.allOptionalsAreSet || opts.checkOptionalsAreSet?.includes('expected_fees')) {
const expected_feesErr = CumulativeFeesValidate(o.expected_fees, opts.expected_fees_Options, `${path}.expected_fees`)
if (expected_feesErr !== null) return expected_feesErr
}
if (typeof o.invoice !== 'string') return new Error(`${path}.invoice: is not a string`)
if (opts.invoice_CustomCheck && !opts.invoice_CustomCheck(o.invoice)) return new Error(`${path}.invoice: custom check failed`)
@ -3296,14 +3304,16 @@ export const PayAppUserInvoiceRequestValidate = (o?: PayAppUserInvoiceRequest, o
export type PayInvoiceRequest = {
amount: number
debit_npub?: string
expected_fees?: CumulativeFees
invoice: string
}
export type PayInvoiceRequestOptionalField = 'debit_npub'
export const PayInvoiceRequestOptionalFields: PayInvoiceRequestOptionalField[] = ['debit_npub']
export type PayInvoiceRequestOptionalField = 'debit_npub' | 'expected_fees'
export const PayInvoiceRequestOptionalFields: PayInvoiceRequestOptionalField[] = ['debit_npub', 'expected_fees']
export type PayInvoiceRequestOptions = OptionsBaseMessage & {
checkOptionalsAreSet?: PayInvoiceRequestOptionalField[]
amount_CustomCheck?: (v: number) => boolean
debit_npub_CustomCheck?: (v?: string) => boolean
expected_fees_Options?: CumulativeFeesOptions
invoice_CustomCheck?: (v: string) => boolean
}
export const PayInvoiceRequestValidate = (o?: PayInvoiceRequest, opts: PayInvoiceRequestOptions = {}, path: string = 'PayInvoiceRequest::root.'): Error | null => {
@ -3316,6 +3326,12 @@ export const PayInvoiceRequestValidate = (o?: PayInvoiceRequest, opts: PayInvoic
if ((o.debit_npub || opts.allOptionalsAreSet || opts.checkOptionalsAreSet?.includes('debit_npub')) && typeof o.debit_npub !== 'string') return new Error(`${path}.debit_npub: is not a string`)
if (opts.debit_npub_CustomCheck && !opts.debit_npub_CustomCheck(o.debit_npub)) return new Error(`${path}.debit_npub: custom check failed`)
if (typeof o.expected_fees === 'object' || opts.allOptionalsAreSet || opts.checkOptionalsAreSet?.includes('expected_fees')) {
const expected_feesErr = CumulativeFeesValidate(o.expected_fees, opts.expected_fees_Options, `${path}.expected_fees`)
if (expected_feesErr !== null) return expected_feesErr
}
if (typeof o.invoice !== 'string') return new Error(`${path}.invoice: is not a string`)
if (opts.invoice_CustomCheck && !opts.invoice_CustomCheck(o.invoice)) return new Error(`${path}.invoice: custom check failed`)

View file

@ -390,6 +390,7 @@ message PayAppUserInvoiceRequest {
string invoice = 2;
int64 amount = 3;
optional string debit_npub = 4;
optional CumulativeFees expected_fees = 5;
}
message SendAppUserToAppUserPaymentRequest {
@ -466,6 +467,7 @@ message PayInvoiceRequest{
string invoice = 1;
int64 amount = 2;
optional string debit_npub = 3;
optional CumulativeFees expected_fees = 4;
}
message PayInvoiceResponse{

View file

@ -107,6 +107,7 @@ export default class {
invoice: req.invoice,
user_identifier: ctx.app_user_id,
debit_npub: req.debit_npub,
expected_fees: req.expected_fees,
})
}

View file

@ -170,8 +170,8 @@ export class LiquidityProvider {
return Object.values(this.pendingPayments).reduce((a, b) => a + b, 0)
}
GetServiceFee = (amount: number) => {
const fees = this.GetFees()
GetServiceFee = (amount: number, f?: Types.CumulativeFees) => {
const fees = f ? f : this.GetFees()
const serviceFeeRate = fees.serviceFeeBps / 10000
const serviceFee = Math.ceil(serviceFeeRate * amount)
return Math.max(serviceFee, fees.networkFeeFixed)
@ -221,7 +221,8 @@ export class LiquidityProvider {
if (!this.IsReady()) {
throw new Error("liquidity provider is not ready yet, disabled or unreachable")
}
const providerServiceFee = this.GetServiceFee(decodedAmount)
const fees = this.GetFees()
const providerServiceFee = this.GetServiceFee(decodedAmount, fees)
this.pendingPayments[invoice] = decodedAmount + providerServiceFee
const timeout = setTimeout(() => {
if (!this.pendingPaymentsAck[invoice]) {
@ -231,8 +232,9 @@ export class LiquidityProvider {
this.lastSeenBeacon = 0
}, 1000 * 10)
this.pendingPaymentsAck[invoice] = true
const res = await this.client.PayInvoice({ invoice, amount: 0 })
const res = await this.client.PayInvoice({ invoice, amount: 0, expected_fees: fees })
delete this.pendingPaymentsAck[invoice]
clearTimeout(timeout)
if (res.status === 'ERROR') {
this.log("error paying invoice", res.reason)
throw new Error(res.reason)

View file

@ -273,6 +273,14 @@ export default class {
if (maybeBanned.locked) {
throw new Error("user is banned, cannot send payment")
}
if (req.expected_fees) {
const { networkFeeFixed, serviceFeeBps } = req.expected_fees
const serviceFixed = this.settings.getSettings().lndSettings.feeFixedLimit
const serviceBps = this.settings.getSettings().serviceFeeSettings.outgoingAppUserInvoiceFeeBps
if (serviceFixed !== networkFeeFixed || serviceBps !== serviceFeeBps) {
throw new Error("fees do not match the expected fees")
}
}
const decoded = await this.lnd.DecodeInvoice(req.invoice)
if (decoded.numSatoshis !== 0 && req.amount !== 0) {
throw new Error("invoice has value, do not provide amount the the request")