fixes and cleanup
This commit is contained in:
parent
81229b3385
commit
824e98f007
11 changed files with 52 additions and 112 deletions
|
|
@ -1169,8 +1169,8 @@ The nostr server will send back a message response, and inside the body there wi
|
||||||
- __invitation_link__: _string_
|
- __invitation_link__: _string_
|
||||||
|
|
||||||
### CumulativeFees
|
### CumulativeFees
|
||||||
- __outboundFeeFloor__: _number_
|
|
||||||
- __serviceFeeBps__: _number_
|
- __serviceFeeBps__: _number_
|
||||||
|
- __serviceFeeFloor__: _number_
|
||||||
|
|
||||||
### DebitAuthorization
|
### DebitAuthorization
|
||||||
- __authorized__: _boolean_
|
- __authorized__: _boolean_
|
||||||
|
|
|
||||||
|
|
@ -230,8 +230,8 @@ type CreateOneTimeInviteLinkResponse struct {
|
||||||
Invitation_link string `json:"invitation_link"`
|
Invitation_link string `json:"invitation_link"`
|
||||||
}
|
}
|
||||||
type CumulativeFees struct {
|
type CumulativeFees struct {
|
||||||
Outboundfeefloor int64 `json:"outboundFeeFloor"`
|
|
||||||
Servicefeebps int64 `json:"serviceFeeBps"`
|
Servicefeebps int64 `json:"serviceFeeBps"`
|
||||||
|
Servicefeefloor int64 `json:"serviceFeeFloor"`
|
||||||
}
|
}
|
||||||
type DebitAuthorization struct {
|
type DebitAuthorization struct {
|
||||||
Authorized bool `json:"authorized"`
|
Authorized bool `json:"authorized"`
|
||||||
|
|
|
||||||
|
|
@ -1303,25 +1303,25 @@ export const CreateOneTimeInviteLinkResponseValidate = (o?: CreateOneTimeInviteL
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CumulativeFees = {
|
export type CumulativeFees = {
|
||||||
outboundFeeFloor: number
|
|
||||||
serviceFeeBps: number
|
serviceFeeBps: number
|
||||||
|
serviceFeeFloor: number
|
||||||
}
|
}
|
||||||
export const CumulativeFeesOptionalFields: [] = []
|
export const CumulativeFeesOptionalFields: [] = []
|
||||||
export type CumulativeFeesOptions = OptionsBaseMessage & {
|
export type CumulativeFeesOptions = OptionsBaseMessage & {
|
||||||
checkOptionalsAreSet?: []
|
checkOptionalsAreSet?: []
|
||||||
outboundFeeFloor_CustomCheck?: (v: number) => boolean
|
|
||||||
serviceFeeBps_CustomCheck?: (v: number) => boolean
|
serviceFeeBps_CustomCheck?: (v: number) => boolean
|
||||||
|
serviceFeeFloor_CustomCheck?: (v: number) => boolean
|
||||||
}
|
}
|
||||||
export const CumulativeFeesValidate = (o?: CumulativeFees, opts: CumulativeFeesOptions = {}, path: string = 'CumulativeFees::root.'): Error | null => {
|
export const CumulativeFeesValidate = (o?: CumulativeFees, opts: CumulativeFeesOptions = {}, path: string = 'CumulativeFees::root.'): Error | null => {
|
||||||
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
|
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
|
||||||
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
|
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
|
||||||
|
|
||||||
if (typeof o.outboundFeeFloor !== 'number') return new Error(`${path}.outboundFeeFloor: is not a number`)
|
|
||||||
if (opts.outboundFeeFloor_CustomCheck && !opts.outboundFeeFloor_CustomCheck(o.outboundFeeFloor)) return new Error(`${path}.outboundFeeFloor: custom check failed`)
|
|
||||||
|
|
||||||
if (typeof o.serviceFeeBps !== 'number') return new Error(`${path}.serviceFeeBps: is not a number`)
|
if (typeof o.serviceFeeBps !== 'number') return new Error(`${path}.serviceFeeBps: is not a number`)
|
||||||
if (opts.serviceFeeBps_CustomCheck && !opts.serviceFeeBps_CustomCheck(o.serviceFeeBps)) return new Error(`${path}.serviceFeeBps: custom check failed`)
|
if (opts.serviceFeeBps_CustomCheck && !opts.serviceFeeBps_CustomCheck(o.serviceFeeBps)) return new Error(`${path}.serviceFeeBps: custom check failed`)
|
||||||
|
|
||||||
|
if (typeof o.serviceFeeFloor !== 'number') return new Error(`${path}.serviceFeeFloor: is not a number`)
|
||||||
|
if (opts.serviceFeeFloor_CustomCheck && !opts.serviceFeeFloor_CustomCheck(o.serviceFeeFloor)) return new Error(`${path}.serviceFeeFloor: custom check failed`)
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -857,7 +857,7 @@ message SwapsList {
|
||||||
}
|
}
|
||||||
|
|
||||||
message CumulativeFees {
|
message CumulativeFees {
|
||||||
int64 outboundFeeFloor = 2;
|
int64 serviceFeeFloor = 2;
|
||||||
int64 serviceFeeBps = 3;
|
int64 serviceFeeBps = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -434,7 +434,7 @@ export default class {
|
||||||
// Force use of provider when bypass is enabled
|
// Force use of provider when bypass is enabled
|
||||||
const mustUseProvider = this.liquidProvider.getSettings().useOnlyLiquidityProvider || useProvider
|
const mustUseProvider = this.liquidProvider.getSettings().useOnlyLiquidityProvider || useProvider
|
||||||
if (mustUseProvider) {
|
if (mustUseProvider) {
|
||||||
const res = await this.liquidProvider.PayInvoice(invoice, decodedAmount, from)
|
const res = await this.liquidProvider.PayInvoice(invoice, decodedAmount, from, feeLimit)
|
||||||
const providerDst = this.liquidProvider.GetProviderDestination()
|
const providerDst = this.liquidProvider.GetProviderDestination()
|
||||||
return { feeSat: res.service_fee, valueSat: res.amount_paid, paymentPreimage: res.preimage, providerDst }
|
return { feeSat: res.service_fee, valueSat: res.amount_paid, paymentPreimage: res.preimage, providerDst }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,14 +69,14 @@ export default class {
|
||||||
throw new Error(`app user ${ctx.user_id} not found`) // TODO: fix logs doxing
|
throw new Error(`app user ${ctx.user_id} not found`) // TODO: fix logs doxing
|
||||||
}
|
}
|
||||||
const nostrSettings = this.settings.getSettings().nostrRelaySettings
|
const nostrSettings = this.settings.getSettings().nostrRelaySettings
|
||||||
const { max, outboundFeeFloor, serviceFeeBps } = this.applicationManager.paymentManager.GetMaxPayableInvoice(user.balance_sats)
|
const { max, serviceFeeFloor, serviceFeeBps } = this.applicationManager.paymentManager.GetMaxPayableInvoice(user.balance_sats)
|
||||||
return {
|
return {
|
||||||
userId: ctx.user_id,
|
userId: ctx.user_id,
|
||||||
balance: user.balance_sats,
|
balance: user.balance_sats,
|
||||||
max_withdrawable: max,
|
max_withdrawable: max,
|
||||||
user_identifier: appUser.identifier,
|
user_identifier: appUser.identifier,
|
||||||
network_max_fee_bps: 0,
|
network_max_fee_bps: 0,
|
||||||
network_max_fee_fixed: outboundFeeFloor,
|
network_max_fee_fixed: serviceFeeFloor,
|
||||||
service_fee_bps: serviceFeeBps,
|
service_fee_bps: serviceFeeBps,
|
||||||
noffer: nofferEncode({ pubkey: app.nostr_public_key!, offer: appUser.identifier, priceType: OfferPriceType.Spontaneous, relay: nostrSettings.relays[0] }),
|
noffer: nofferEncode({ pubkey: app.nostr_public_key!, offer: appUser.identifier, priceType: OfferPriceType.Spontaneous, relay: nostrSettings.relays[0] }),
|
||||||
ndebit: ndebitEncode({ pubkey: app.nostr_public_key!, pointer: appUser.identifier, relay: nostrSettings.relays[0] }),
|
ndebit: ndebitEncode({ pubkey: app.nostr_public_key!, pointer: appUser.identifier, relay: nostrSettings.relays[0] }),
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,7 @@ export default class {
|
||||||
|
|
||||||
const ndebitString = ndebitEncode({ pubkey: app.nostr_public_key!, pointer: u.identifier, relay: nostrSettings.relays[0] })
|
const ndebitString = ndebitEncode({ pubkey: app.nostr_public_key!, pointer: u.identifier, relay: nostrSettings.relays[0] })
|
||||||
log("🔗 [DEBUG] Generated ndebit for user", { userId: u.user.user_id, ndebit: ndebitString })
|
log("🔗 [DEBUG] Generated ndebit for user", { userId: u.user.user_id, ndebit: ndebitString })
|
||||||
const { max, outboundFeeFloor, serviceFeeBps } = this.paymentManager.GetMaxPayableInvoice(u.user.balance_sats)
|
const { max, serviceFeeFloor, serviceFeeBps } = this.paymentManager.GetMaxPayableInvoice(u.user.balance_sats)
|
||||||
return {
|
return {
|
||||||
identifier: u.identifier,
|
identifier: u.identifier,
|
||||||
info: {
|
info: {
|
||||||
|
|
@ -163,7 +163,7 @@ export default class {
|
||||||
max_withdrawable: max,
|
max_withdrawable: max,
|
||||||
user_identifier: u.identifier,
|
user_identifier: u.identifier,
|
||||||
network_max_fee_bps: 0,
|
network_max_fee_bps: 0,
|
||||||
network_max_fee_fixed: outboundFeeFloor,
|
network_max_fee_fixed: serviceFeeFloor,
|
||||||
service_fee_bps: serviceFeeBps,
|
service_fee_bps: serviceFeeBps,
|
||||||
noffer: nofferEncode({ pubkey: app.nostr_public_key!, offer: u.identifier, priceType: OfferPriceType.Spontaneous, relay: nostrSettings.relays[0] }),
|
noffer: nofferEncode({ pubkey: app.nostr_public_key!, offer: u.identifier, priceType: OfferPriceType.Spontaneous, relay: nostrSettings.relays[0] }),
|
||||||
ndebit: ndebitEncode({ pubkey: app.nostr_public_key!, pointer: u.identifier, relay: nostrSettings.relays[0] }),
|
ndebit: ndebitEncode({ pubkey: app.nostr_public_key!, pointer: u.identifier, relay: nostrSettings.relays[0] }),
|
||||||
|
|
@ -214,14 +214,14 @@ export default class {
|
||||||
const app = await this.storage.applicationStorage.GetApplication(appId)
|
const app = await this.storage.applicationStorage.GetApplication(appId)
|
||||||
const user = await this.storage.applicationStorage.GetApplicationUser(app, req.user_identifier)
|
const user = await this.storage.applicationStorage.GetApplicationUser(app, req.user_identifier)
|
||||||
const nostrSettings = this.settings.getSettings().nostrRelaySettings
|
const nostrSettings = this.settings.getSettings().nostrRelaySettings
|
||||||
const { max, outboundFeeFloor, serviceFeeBps } = this.paymentManager.GetMaxPayableInvoice(user.user.balance_sats)
|
const { max, serviceFeeFloor, serviceFeeBps } = this.paymentManager.GetMaxPayableInvoice(user.user.balance_sats)
|
||||||
return {
|
return {
|
||||||
max_withdrawable: max, identifier: req.user_identifier, info: {
|
max_withdrawable: max, identifier: req.user_identifier, info: {
|
||||||
userId: user.user.user_id, balance: user.user.balance_sats,
|
userId: user.user.user_id, balance: user.user.balance_sats,
|
||||||
max_withdrawable: max,
|
max_withdrawable: max,
|
||||||
user_identifier: user.identifier,
|
user_identifier: user.identifier,
|
||||||
network_max_fee_bps: 0,
|
network_max_fee_bps: 0,
|
||||||
network_max_fee_fixed: outboundFeeFloor,
|
network_max_fee_fixed: serviceFeeFloor,
|
||||||
service_fee_bps: serviceFeeBps,
|
service_fee_bps: serviceFeeBps,
|
||||||
noffer: nofferEncode({ pubkey: app.nostr_public_key!, offer: user.identifier, priceType: OfferPriceType.Spontaneous, relay: nostrSettings.relays[0] }),
|
noffer: nofferEncode({ pubkey: app.nostr_public_key!, offer: user.identifier, priceType: OfferPriceType.Spontaneous, relay: nostrSettings.relays[0] }),
|
||||||
ndebit: ndebitEncode({ pubkey: app.nostr_public_key!, pointer: user.identifier, relay: nostrSettings.relays[0] }),
|
ndebit: ndebitEncode({ pubkey: app.nostr_public_key!, pointer: user.identifier, relay: nostrSettings.relays[0] }),
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ export class LiquidityProvider {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
this.feesCache = {
|
this.feesCache = {
|
||||||
outboundFeeFloor: res.network_max_fee_fixed,
|
serviceFeeFloor: res.network_max_fee_fixed,
|
||||||
serviceFeeBps: res.service_fee_bps
|
serviceFeeBps: res.service_fee_bps
|
||||||
}
|
}
|
||||||
this.latestReceivedBalance = res.balance
|
this.latestReceivedBalance = res.balance
|
||||||
|
|
@ -151,11 +151,11 @@ export class LiquidityProvider {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
const balance = this.latestReceivedBalance
|
const balance = this.latestReceivedBalance
|
||||||
const { outboundFeeFloor, serviceFeeBps } = this.feesCache
|
const { serviceFeeFloor, serviceFeeBps } = this.feesCache
|
||||||
const div = 1 + (serviceFeeBps / 10000)
|
const div = 1 + (serviceFeeBps / 10000)
|
||||||
const maxWithoutFixed = Math.floor(balance / div)
|
const maxWithoutFixed = Math.floor(balance / div)
|
||||||
const fee = balance - maxWithoutFixed
|
const fee = balance - maxWithoutFixed
|
||||||
return balance - Math.max(fee, outboundFeeFloor)
|
return balance - Math.max(fee, serviceFeeFloor)
|
||||||
}
|
}
|
||||||
|
|
||||||
GetLatestBalance = () => {
|
GetLatestBalance = () => {
|
||||||
|
|
@ -173,7 +173,7 @@ export class LiquidityProvider {
|
||||||
const fees = f ? f : this.GetFees()
|
const fees = f ? f : this.GetFees()
|
||||||
const serviceFeeRate = fees.serviceFeeBps / 10000
|
const serviceFeeRate = fees.serviceFeeBps / 10000
|
||||||
const serviceFee = Math.ceil(serviceFeeRate * amount)
|
const serviceFee = Math.ceil(serviceFeeRate * amount)
|
||||||
return Math.max(serviceFee, fees.outboundFeeFloor)
|
return Math.max(serviceFee, fees.serviceFeeFloor)
|
||||||
}
|
}
|
||||||
|
|
||||||
CanProviderPay = async (amount: number, localServiceFee: number): Promise<boolean> => {
|
CanProviderPay = async (amount: number, localServiceFee: number): Promise<boolean> => {
|
||||||
|
|
@ -215,13 +215,16 @@ export class LiquidityProvider {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PayInvoice = async (invoice: string, decodedAmount: number, from: 'user' | 'system') => {
|
PayInvoice = async (invoice: string, decodedAmount: number, from: 'user' | 'system', feeLimit?: number) => {
|
||||||
try {
|
try {
|
||||||
if (!this.IsReady()) {
|
if (!this.IsReady()) {
|
||||||
throw new Error("liquidity provider is not ready yet, disabled or unreachable")
|
throw new Error("liquidity provider is not ready yet, disabled or unreachable")
|
||||||
}
|
}
|
||||||
const fees = this.GetFees()
|
const fees = this.GetFees()
|
||||||
const providerServiceFee = this.GetServiceFee(decodedAmount, fees)
|
const providerServiceFee = this.GetServiceFee(decodedAmount, fees)
|
||||||
|
if (feeLimit && providerServiceFee > feeLimit) {
|
||||||
|
throw new Error("provider service fee is greater than the fee limit")
|
||||||
|
}
|
||||||
this.pendingPayments[invoice] = decodedAmount + providerServiceFee
|
this.pendingPayments[invoice] = decodedAmount + providerServiceFee
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
if (!this.pendingPaymentsAck[invoice]) {
|
if (!this.pendingPaymentsAck[invoice]) {
|
||||||
|
|
|
||||||
|
|
@ -173,7 +173,7 @@ export default class {
|
||||||
getReceiveServiceFee = (action: Types.UserOperationType, amount: number, managedUser: boolean): number => {
|
getReceiveServiceFee = (action: Types.UserOperationType, amount: number, managedUser: boolean): number => {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case Types.UserOperationType.INCOMING_TX:
|
case Types.UserOperationType.INCOMING_TX:
|
||||||
return Math.ceil(this.settings.getSettings().serviceFeeSettings.incomingTxFee * amount)
|
return 0
|
||||||
case Types.UserOperationType.INCOMING_INVOICE:
|
case Types.UserOperationType.INCOMING_INVOICE:
|
||||||
// Incoming invoice fees are always 0 (not configurable)
|
// Incoming invoice fees are always 0 (not configurable)
|
||||||
return 0
|
return 0
|
||||||
|
|
@ -197,18 +197,14 @@ export default class {
|
||||||
getSendServiceFee = (action: Types.UserOperationType, amount: number, managedUser: boolean): number => {
|
getSendServiceFee = (action: Types.UserOperationType, amount: number, managedUser: boolean): number => {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case Types.UserOperationType.OUTGOING_TX:
|
case Types.UserOperationType.OUTGOING_TX:
|
||||||
// Internal address payment, treat like user-to-user
|
throw new Error("OUTGOING_TX is not a valid send service fee action")
|
||||||
if (managedUser) {
|
|
||||||
return Math.ceil(this.settings.getSettings().serviceFeeSettings.userToUserFee * amount)
|
|
||||||
}
|
|
||||||
return Math.ceil(this.settings.getSettings().serviceFeeSettings.rootToUserFee * amount)
|
|
||||||
case Types.UserOperationType.OUTGOING_INVOICE:
|
case Types.UserOperationType.OUTGOING_INVOICE:
|
||||||
const fee = this.getInvoicePaymentServiceFee(amount, managedUser)
|
const fee = this.getInvoicePaymentServiceFee(amount, managedUser)
|
||||||
// Only managed users pay the service fee floor
|
// Only managed users pay the service fee floor
|
||||||
if (!managedUser) {
|
if (!managedUser) {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return Math.max(fee, this.settings.getSettings().lndSettings.serviceFeeFloor)
|
return Math.max(fee, this.settings.getSettings().serviceFeeSettings.serviceFeeFloor)
|
||||||
case Types.UserOperationType.OUTGOING_USER_TO_USER:
|
case Types.UserOperationType.OUTGOING_USER_TO_USER:
|
||||||
if (managedUser) {
|
if (managedUser) {
|
||||||
return Math.ceil(this.settings.getSettings().serviceFeeSettings.userToUserFee * amount)
|
return Math.ceil(this.settings.getSettings().serviceFeeSettings.userToUserFee * amount)
|
||||||
|
|
@ -272,18 +268,17 @@ export default class {
|
||||||
}
|
}
|
||||||
|
|
||||||
GetFees = (): Types.CumulativeFees => {
|
GetFees = (): Types.CumulativeFees => {
|
||||||
const { serviceFeeBps } = this.settings.getSettings().serviceFeeSettings
|
const { serviceFeeBps, serviceFeeFloor } = this.settings.getSettings().serviceFeeSettings
|
||||||
const { serviceFeeFloor } = this.settings.getSettings().lndSettings
|
return { serviceFeeFloor, serviceFeeBps }
|
||||||
return { outboundFeeFloor: serviceFeeFloor, serviceFeeBps: serviceFeeBps }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GetMaxPayableInvoice(balance: number): Types.CumulativeFees & { max: number } {
|
GetMaxPayableInvoice(balance: number): Types.CumulativeFees & { max: number } {
|
||||||
const { outboundFeeFloor, serviceFeeBps } = this.GetFees()
|
const { serviceFeeFloor, serviceFeeBps } = this.GetFees()
|
||||||
const div = 1 + (serviceFeeBps / 10000)
|
const div = 1 + (serviceFeeBps / 10000)
|
||||||
const maxWithoutFixed = Math.floor(balance / div)
|
const maxWithoutFixed = Math.floor(balance / div)
|
||||||
const fee = balance - maxWithoutFixed
|
const fee = balance - maxWithoutFixed
|
||||||
const max = balance - Math.max(fee, outboundFeeFloor)
|
const max = balance - Math.max(fee, serviceFeeFloor)
|
||||||
return { max, outboundFeeFloor, serviceFeeBps }
|
return { max, serviceFeeFloor, serviceFeeBps }
|
||||||
}
|
}
|
||||||
async DecodeInvoice(req: Types.DecodeInvoiceRequest): Promise<Types.DecodeInvoiceResponse> {
|
async DecodeInvoice(req: Types.DecodeInvoiceRequest): Promise<Types.DecodeInvoiceResponse> {
|
||||||
const decoded = await this.lnd.DecodeInvoice(req.invoice)
|
const decoded = await this.lnd.DecodeInvoice(req.invoice)
|
||||||
|
|
@ -299,10 +294,10 @@ export default class {
|
||||||
throw new Error("user is banned, cannot send payment")
|
throw new Error("user is banned, cannot send payment")
|
||||||
}
|
}
|
||||||
if (req.expected_fees) {
|
if (req.expected_fees) {
|
||||||
const { outboundFeeFloor, serviceFeeBps } = req.expected_fees
|
const { serviceFeeFloor, serviceFeeBps } = req.expected_fees
|
||||||
const serviceFixed = this.settings.getSettings().lndSettings.serviceFeeFloor
|
const serviceFixed = this.settings.getSettings().serviceFeeSettings.serviceFeeFloor
|
||||||
const serviceBps = this.settings.getSettings().serviceFeeSettings.serviceFeeBps
|
const serviceBps = this.settings.getSettings().serviceFeeSettings.serviceFeeBps
|
||||||
if (serviceFixed !== outboundFeeFloor || serviceBps !== serviceFeeBps) {
|
if (serviceFixed !== serviceFeeFloor || serviceBps !== serviceFeeBps) {
|
||||||
throw new Error("fees do not match the expected fees")
|
throw new Error("fees do not match the expected fees")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -522,21 +517,11 @@ export default class {
|
||||||
this.swaps.reverseSwaps.SubscribeToTransactionSwap(data, result => {
|
this.swaps.reverseSwaps.SubscribeToTransactionSwap(data, result => {
|
||||||
swapResult = result
|
swapResult = result
|
||||||
})
|
})
|
||||||
// Validate that the invoice amount matches what was quoted
|
|
||||||
const decoded = await this.lnd.DecodeInvoice(txSwap.invoice)
|
|
||||||
if (decoded.numSatoshis !== txSwap.invoice_amount) {
|
|
||||||
throw new Error("swap invoice amount does not match quote")
|
|
||||||
}
|
|
||||||
const fees = this.GetFees()
|
|
||||||
let payment: Types.PayInvoiceResponse
|
let payment: Types.PayInvoiceResponse
|
||||||
try {
|
try {
|
||||||
payment = await this.PayInvoice(ctx.user_id, {
|
payment = await this.PayInvoice(ctx.user_id, {
|
||||||
amount: 0,
|
amount: 0,
|
||||||
invoice: txSwap.invoice,
|
invoice: txSwap.invoice
|
||||||
expected_fees: {
|
|
||||||
outboundFeeFloor: fees.outboundFeeFloor,
|
|
||||||
serviceFeeBps: fees.serviceFeeBps
|
|
||||||
}
|
|
||||||
}, app, { swapOperationId: req.swap_operation_id })
|
}, app, { swapOperationId: req.swap_operation_id })
|
||||||
if (!swapResult.ok) {
|
if (!swapResult.ok) {
|
||||||
this.log("invoice payment successful, but swap failed")
|
this.log("invoice payment successful, but swap failed")
|
||||||
|
|
@ -572,7 +557,7 @@ export default class {
|
||||||
const { blockHeight } = await this.lnd.GetInfo()
|
const { blockHeight } = await this.lnd.GetInfo()
|
||||||
const app = await this.storage.applicationStorage.GetApplication(ctx.app_id)
|
const app = await this.storage.applicationStorage.GetApplication(ctx.app_id)
|
||||||
const isManagedUser = ctx.user_id !== app.owner.user_id
|
const isManagedUser = ctx.user_id !== app.owner.user_id
|
||||||
const serviceFee = this.getSendServiceFee(Types.UserOperationType.OUTGOING_TX, req.amoutSats, isManagedUser)
|
const serviceFee = this.getSendServiceFee(Types.UserOperationType.OUTGOING_USER_TO_USER, req.amoutSats, isManagedUser)
|
||||||
|
|
||||||
const txId = crypto.randomBytes(32).toString("hex")
|
const txId = crypto.randomBytes(32).toString("hex")
|
||||||
const addressData = `${req.address}:${txId}`
|
const addressData = `${req.address}:${txId}`
|
||||||
|
|
|
||||||
|
|
@ -3,38 +3,22 @@ import os from 'os'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
|
||||||
export type ServiceFeeSettings = {
|
export type ServiceFeeSettings = {
|
||||||
incomingTxFee: number
|
|
||||||
serviceFee: number
|
serviceFee: number
|
||||||
serviceFeeBps: number
|
serviceFeeBps: number
|
||||||
|
serviceFeeFloor: number
|
||||||
userToUserFee: number
|
userToUserFee: number
|
||||||
rootToUserFee: number
|
rootToUserFee: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LoadServiceFeeSettingsFromEnv = (dbEnv: Record<string, string | undefined>, addToDb?: EnvCacher): ServiceFeeSettings => {
|
export const LoadServiceFeeSettingsFromEnv = (dbEnv: Record<string, string | undefined>, addToDb?: EnvCacher): ServiceFeeSettings => {
|
||||||
// Support both old and new env var names for backward compatibility (new name takes precedence)
|
const oldServiceFeeBps = chooseEnvInt("OUTGOING_INVOICE_FEE_USER_BPS", dbEnv, 60, addToDb)
|
||||||
// Check if new name exists first (in process.env or dbEnv)
|
const serviceFeeBps = chooseEnvInt("SERVICE_FEE_BPS", dbEnv, oldServiceFeeBps, addToDb)
|
||||||
const newExists = process.env["SERVICE_FEE_BPS"] !== undefined || dbEnv["SERVICE_FEE_BPS"] !== undefined
|
const oldRoutingFeeFloor = chooseEnvInt('OUTBOUND_MAX_FEE_EXTRA_SATS', dbEnv, 10, addToDb)
|
||||||
let serviceFeeBps: number
|
const serviceFeeFloor = chooseEnvInt("SERVICE_FEE_FLOOR_SATS", dbEnv, oldRoutingFeeFloor, addToDb)
|
||||||
if (newExists) {
|
|
||||||
// New name exists, use it
|
|
||||||
serviceFeeBps = chooseEnvInt("SERVICE_FEE_BPS", dbEnv, 60, addToDb)
|
|
||||||
} else {
|
|
||||||
// New name doesn't exist, check old name for backward compatibility
|
|
||||||
const oldExists = process.env["OUTGOING_INVOICE_FEE_USER_BPS"] !== undefined || dbEnv["OUTGOING_INVOICE_FEE_USER_BPS"] !== undefined
|
|
||||||
if (oldExists) {
|
|
||||||
// Old name exists, use it and migrate to new name in DB
|
|
||||||
const oldValue = chooseEnvInt("OUTGOING_INVOICE_FEE_USER_BPS", dbEnv, 60) // Don't add old name to DB
|
|
||||||
serviceFeeBps = oldValue
|
|
||||||
if (addToDb) addToDb("SERVICE_FEE_BPS", oldValue.toString()) // Migrate to new name
|
|
||||||
} else {
|
|
||||||
// Neither exists, use default with new name
|
|
||||||
serviceFeeBps = chooseEnvInt("SERVICE_FEE_BPS", dbEnv, 60, addToDb)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
incomingTxFee: 0, // Not configurable, always 0
|
serviceFeeBps,
|
||||||
serviceFeeBps: serviceFeeBps,
|
|
||||||
serviceFee: serviceFeeBps / 10000,
|
serviceFee: serviceFeeBps / 10000,
|
||||||
|
serviceFeeFloor,
|
||||||
userToUserFee: chooseEnvInt("TX_FEE_INTERNAL_USER_BPS", dbEnv, 0, addToDb) / 10000,
|
userToUserFee: chooseEnvInt("TX_FEE_INTERNAL_USER_BPS", dbEnv, 0, addToDb) / 10000,
|
||||||
rootToUserFee: chooseEnvInt("TX_FEE_INTERNAL_ROOT_BPS", dbEnv, 0, addToDb) / 10000,
|
rootToUserFee: chooseEnvInt("TX_FEE_INTERNAL_ROOT_BPS", dbEnv, 0, addToDb) / 10000,
|
||||||
}
|
}
|
||||||
|
|
@ -91,7 +75,6 @@ const networks = ['mainnet', 'testnet', 'regtest'] as const
|
||||||
export type BTCNetwork = (typeof networks)[number]
|
export type BTCNetwork = (typeof networks)[number]
|
||||||
export type LndSettings = {
|
export type LndSettings = {
|
||||||
lndLogDir: string
|
lndLogDir: string
|
||||||
serviceFeeFloor: number
|
|
||||||
routingFeeLimitBps: number
|
routingFeeLimitBps: number
|
||||||
routingFeeFloor: number
|
routingFeeFloor: number
|
||||||
mockLnd: boolean
|
mockLnd: boolean
|
||||||
|
|
@ -125,42 +108,11 @@ export const LoadLndNodeSettingsFromEnv = (dbEnv: Record<string, string | undefi
|
||||||
|
|
||||||
export const LoadLndSettingsFromEnv = (dbEnv: Record<string, string | undefined>, addToDb?: EnvCacher): LndSettings => {
|
export const LoadLndSettingsFromEnv = (dbEnv: Record<string, string | undefined>, addToDb?: EnvCacher): LndSettings => {
|
||||||
const network = chooseEnv('BTC_NETWORK', dbEnv, 'mainnet', addToDb) as BTCNetwork
|
const network = chooseEnv('BTC_NETWORK', dbEnv, 'mainnet', addToDb) as BTCNetwork
|
||||||
|
const oldRoutingFeeFloor = chooseEnvInt('OUTBOUND_MAX_FEE_EXTRA_SATS', dbEnv, 5, addToDb)
|
||||||
// Routing fee floor: new name takes precedence, fall back to old name for backward compatibility
|
const routingFeeFloor = chooseEnvInt('ROUTING_FEE_FLOOR_SATS', dbEnv, oldRoutingFeeFloor, addToDb)
|
||||||
const routingFeeFloorNewExists = process.env['ROUTING_FEE_FLOOR_SATS'] !== undefined || dbEnv['ROUTING_FEE_FLOOR_SATS'] !== undefined
|
|
||||||
let routingFeeFloor: number
|
|
||||||
if (routingFeeFloorNewExists) {
|
|
||||||
routingFeeFloor = chooseEnvInt('ROUTING_FEE_FLOOR_SATS', dbEnv, 5, addToDb)
|
|
||||||
} else {
|
|
||||||
const routingFeeFloorOldExists = process.env['OUTBOUND_MAX_FEE_EXTRA_SATS'] !== undefined || dbEnv['OUTBOUND_MAX_FEE_EXTRA_SATS'] !== undefined
|
|
||||||
if (routingFeeFloorOldExists) {
|
|
||||||
const oldValue = chooseEnvInt('OUTBOUND_MAX_FEE_EXTRA_SATS', dbEnv, 5) // Don't add old name to DB
|
|
||||||
routingFeeFloor = oldValue
|
|
||||||
if (addToDb) addToDb('ROUTING_FEE_FLOOR_SATS', oldValue.toString()) // Migrate to new name
|
|
||||||
} else {
|
|
||||||
routingFeeFloor = chooseEnvInt('ROUTING_FEE_FLOOR_SATS', dbEnv, 5, addToDb)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Service fee floor: new name takes precedence, fall back to old name for backward compatibility
|
|
||||||
const serviceFeeFloorNewExists = process.env['SERVICE_FEE_FLOOR_SATS'] !== undefined || dbEnv['SERVICE_FEE_FLOOR_SATS'] !== undefined
|
|
||||||
let serviceFeeFloor: number
|
|
||||||
if (serviceFeeFloorNewExists) {
|
|
||||||
serviceFeeFloor = chooseEnvInt('SERVICE_FEE_FLOOR_SATS', dbEnv, 10, addToDb)
|
|
||||||
} else {
|
|
||||||
const serviceFeeFloorOldExists = process.env['OUTBOUND_MAX_FEE_EXTRA_SATS'] !== undefined || dbEnv['OUTBOUND_MAX_FEE_EXTRA_SATS'] !== undefined
|
|
||||||
if (serviceFeeFloorOldExists) {
|
|
||||||
const oldValue = chooseEnvInt('OUTBOUND_MAX_FEE_EXTRA_SATS', dbEnv, 10) // Don't add old name to DB
|
|
||||||
serviceFeeFloor = oldValue
|
|
||||||
if (addToDb) addToDb('SERVICE_FEE_FLOOR_SATS', oldValue.toString()) // Migrate to new name
|
|
||||||
} else {
|
|
||||||
serviceFeeFloor = chooseEnvInt('SERVICE_FEE_FLOOR_SATS', dbEnv, 10, addToDb)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const routingFeeLimitBps = chooseEnvInt('ROUTING_FEE_LIMIT_BPS', dbEnv, 50, addToDb)
|
const routingFeeLimitBps = chooseEnvInt('ROUTING_FEE_LIMIT_BPS', dbEnv, 50, addToDb)
|
||||||
return {
|
return {
|
||||||
lndLogDir: chooseEnv('LND_LOG_DIR', dbEnv, resolveHome("/.lnd/logs/bitcoin/mainnet/lnd.log"), addToDb),
|
lndLogDir: chooseEnv('LND_LOG_DIR', dbEnv, resolveHome("/.lnd/logs/bitcoin/mainnet/lnd.log"), addToDb),
|
||||||
serviceFeeFloor,
|
|
||||||
routingFeeLimitBps,
|
routingFeeLimitBps,
|
||||||
routingFeeFloor,
|
routingFeeFloor,
|
||||||
mockLnd: false,
|
mockLnd: false,
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ export default class SettingsManager {
|
||||||
const { serviceFeeSettings, lndSettings } = settings
|
const { serviceFeeSettings, lndSettings } = settings
|
||||||
const serviceFeeBps = serviceFeeSettings.serviceFeeBps
|
const serviceFeeBps = serviceFeeSettings.serviceFeeBps
|
||||||
const routingFeeLimitBps = lndSettings.routingFeeLimitBps
|
const routingFeeLimitBps = lndSettings.routingFeeLimitBps
|
||||||
const serviceFeeFloor = lndSettings.serviceFeeFloor
|
const serviceFeeFloor = serviceFeeSettings.serviceFeeFloor
|
||||||
const routingFeeFloor = lndSettings.routingFeeFloor
|
const routingFeeFloor = lndSettings.routingFeeFloor
|
||||||
|
|
||||||
if (routingFeeLimitBps > serviceFeeBps) {
|
if (routingFeeLimitBps > serviceFeeBps) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue