renames
This commit is contained in:
parent
ad8cd91aad
commit
efa3976657
3 changed files with 46 additions and 41 deletions
|
|
@ -170,7 +170,7 @@ export default class {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getReceiveServiceFee = (action: Types.UserOperationType, amount: number, appUser: 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 Math.ceil(this.settings.getSettings().serviceFeeSettings.incomingTxFee * amount)
|
||||||
|
|
@ -178,34 +178,42 @@ export default class {
|
||||||
// Incoming invoice fees are always 0 (not configurable)
|
// Incoming invoice fees are always 0 (not configurable)
|
||||||
return 0
|
return 0
|
||||||
case Types.UserOperationType.INCOMING_USER_TO_USER:
|
case Types.UserOperationType.INCOMING_USER_TO_USER:
|
||||||
if (appUser) {
|
if (managedUser) {
|
||||||
return Math.ceil(this.settings.getSettings().serviceFeeSettings.userToUserFee * amount)
|
return Math.ceil(this.settings.getSettings().serviceFeeSettings.userToUserFee * amount)
|
||||||
}
|
}
|
||||||
return Math.ceil(this.settings.getSettings().serviceFeeSettings.appToUserFee * amount)
|
return Math.ceil(this.settings.getSettings().serviceFeeSettings.rootToUserFee * amount)
|
||||||
default:
|
default:
|
||||||
throw new Error("Unknown receive action type")
|
throw new Error("Unknown receive action type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getInvoicePaymentServiceFee = (amount: number, appUser: boolean): number => {
|
getInvoicePaymentServiceFee = (amount: number, managedUser: boolean): number => {
|
||||||
if (appUser) {
|
if (!managedUser) {
|
||||||
return Math.ceil(this.settings.getSettings().serviceFeeSettings.outgoingAppUserInvoiceFee * amount)
|
return 0 // Root doesn't pay service fee to themselves
|
||||||
}
|
}
|
||||||
return Math.ceil(this.settings.getSettings().serviceFeeSettings.outgoingAppInvoiceFee * amount)
|
return Math.ceil(this.settings.getSettings().serviceFeeSettings.serviceFee * amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
getSendServiceFee = (action: Types.UserOperationType, amount: number, appUser: 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:
|
||||||
throw new Error("Sending a transaction is not supported")
|
// Internal address payment, treat like user-to-user
|
||||||
case Types.UserOperationType.OUTGOING_INVOICE:
|
if (managedUser) {
|
||||||
const fee = this.getInvoicePaymentServiceFee(amount, appUser)
|
|
||||||
return Math.max(fee, this.settings.getSettings().lndSettings.serviceFeeFloor)
|
|
||||||
case Types.UserOperationType.OUTGOING_USER_TO_USER:
|
|
||||||
if (appUser) {
|
|
||||||
return Math.ceil(this.settings.getSettings().serviceFeeSettings.userToUserFee * amount)
|
return Math.ceil(this.settings.getSettings().serviceFeeSettings.userToUserFee * amount)
|
||||||
}
|
}
|
||||||
return Math.ceil(this.settings.getSettings().serviceFeeSettings.appToUserFee * amount)
|
return Math.ceil(this.settings.getSettings().serviceFeeSettings.rootToUserFee * amount)
|
||||||
|
case Types.UserOperationType.OUTGOING_INVOICE:
|
||||||
|
const fee = this.getInvoicePaymentServiceFee(amount, managedUser)
|
||||||
|
// Only managed users pay the service fee floor
|
||||||
|
if (!managedUser) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return Math.max(fee, this.settings.getSettings().lndSettings.serviceFeeFloor)
|
||||||
|
case Types.UserOperationType.OUTGOING_USER_TO_USER:
|
||||||
|
if (managedUser) {
|
||||||
|
return Math.ceil(this.settings.getSettings().serviceFeeSettings.userToUserFee * amount)
|
||||||
|
}
|
||||||
|
return Math.ceil(this.settings.getSettings().serviceFeeSettings.rootToUserFee * amount)
|
||||||
default:
|
default:
|
||||||
throw new Error("Unknown service action type")
|
throw new Error("Unknown service action type")
|
||||||
}
|
}
|
||||||
|
|
@ -264,9 +272,9 @@ export default class {
|
||||||
}
|
}
|
||||||
|
|
||||||
GetFees = (): Types.CumulativeFees => {
|
GetFees = (): Types.CumulativeFees => {
|
||||||
const { outgoingAppUserInvoiceFeeBps } = this.settings.getSettings().serviceFeeSettings
|
const { serviceFeeBps } = this.settings.getSettings().serviceFeeSettings
|
||||||
const { serviceFeeFloor } = this.settings.getSettings().lndSettings
|
const { serviceFeeFloor } = this.settings.getSettings().lndSettings
|
||||||
return { outboundFeeFloor: serviceFeeFloor, serviceFeeBps: outgoingAppUserInvoiceFeeBps }
|
return { outboundFeeFloor: serviceFeeFloor, serviceFeeBps: serviceFeeBps }
|
||||||
}
|
}
|
||||||
|
|
||||||
GetMaxPayableInvoice(balance: number): Types.CumulativeFees & { max: number } {
|
GetMaxPayableInvoice(balance: number): Types.CumulativeFees & { max: number } {
|
||||||
|
|
@ -293,7 +301,7 @@ export default class {
|
||||||
if (req.expected_fees) {
|
if (req.expected_fees) {
|
||||||
const { outboundFeeFloor, serviceFeeBps } = req.expected_fees
|
const { outboundFeeFloor, serviceFeeBps } = req.expected_fees
|
||||||
const serviceFixed = this.settings.getSettings().lndSettings.serviceFeeFloor
|
const serviceFixed = this.settings.getSettings().lndSettings.serviceFeeFloor
|
||||||
const serviceBps = this.settings.getSettings().serviceFeeSettings.outgoingAppUserInvoiceFeeBps
|
const serviceBps = this.settings.getSettings().serviceFeeSettings.serviceFeeBps
|
||||||
if (serviceFixed !== outboundFeeFloor || serviceBps !== serviceFeeBps) {
|
if (serviceFixed !== outboundFeeFloor || serviceBps !== serviceFeeBps) {
|
||||||
throw new Error("fees do not match the expected fees")
|
throw new Error("fees do not match the expected fees")
|
||||||
}
|
}
|
||||||
|
|
@ -306,8 +314,8 @@ export default class {
|
||||||
throw new Error("invoice has no value, an amount must be provided in the request")
|
throw new Error("invoice has no value, an amount must be provided in the request")
|
||||||
}
|
}
|
||||||
const payAmount = req.amount !== 0 ? req.amount : Number(decoded.numSatoshis)
|
const payAmount = req.amount !== 0 ? req.amount : Number(decoded.numSatoshis)
|
||||||
const isAppUserPayment = userId !== linkedApplication.owner.user_id
|
const isManagedUser = userId !== linkedApplication.owner.user_id
|
||||||
const serviceFee = this.getSendServiceFee(Types.UserOperationType.OUTGOING_INVOICE, payAmount, isAppUserPayment)
|
const serviceFee = this.getSendServiceFee(Types.UserOperationType.OUTGOING_INVOICE, payAmount, isManagedUser)
|
||||||
const internalInvoice = await this.storage.paymentStorage.GetInvoiceOwner(req.invoice)
|
const internalInvoice = await this.storage.paymentStorage.GetInvoiceOwner(req.invoice)
|
||||||
if (internalInvoice && internalInvoice.paid_at_unix > 0) {
|
if (internalInvoice && internalInvoice.paid_at_unix > 0) {
|
||||||
throw new Error("this invoice was already paid")
|
throw new Error("this invoice was already paid")
|
||||||
|
|
@ -333,7 +341,7 @@ export default class {
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
const feeDiff = serviceFee - paymentInfo.networkFee
|
const feeDiff = serviceFee - paymentInfo.networkFee
|
||||||
if (isAppUserPayment && feeDiff > 0) {
|
if (isManagedUser && feeDiff > 0) {
|
||||||
await this.storage.userStorage.IncrementUserBalance(linkedApplication.owner.user_id, feeDiff, "fees")
|
await this.storage.userStorage.IncrementUserBalance(linkedApplication.owner.user_id, feeDiff, "fees")
|
||||||
}
|
}
|
||||||
const user = await this.storage.userStorage.GetUser(userId)
|
const user = await this.storage.userStorage.GetUser(userId)
|
||||||
|
|
@ -431,8 +439,8 @@ export default class {
|
||||||
const decoded = await this.lnd.DecodeInvoice(res.createdResponse.invoice)
|
const decoded = await this.lnd.DecodeInvoice(res.createdResponse.invoice)
|
||||||
const swapFee = decoded.numSatoshis - chainTotal
|
const swapFee = decoded.numSatoshis - chainTotal
|
||||||
const app = await this.storage.applicationStorage.GetApplication(ctx.app_id)
|
const app = await this.storage.applicationStorage.GetApplication(ctx.app_id)
|
||||||
const isAppUserPayment = ctx.user_id !== app.owner.user_id
|
const isManagedUser = ctx.user_id !== app.owner.user_id
|
||||||
const serviceFee = this.getSendServiceFee(Types.UserOperationType.OUTGOING_INVOICE, decoded.numSatoshis, isAppUserPayment)
|
const serviceFee = this.getSendServiceFee(Types.UserOperationType.OUTGOING_INVOICE, decoded.numSatoshis, isManagedUser)
|
||||||
const newSwap = await this.storage.paymentStorage.AddTransactionSwap({
|
const newSwap = await this.storage.paymentStorage.AddTransactionSwap({
|
||||||
app_user_id: ctx.app_user_id,
|
app_user_id: ctx.app_user_id,
|
||||||
swap_quote_id: res.createdResponse.id,
|
swap_quote_id: res.createdResponse.id,
|
||||||
|
|
@ -546,14 +554,14 @@ 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 serviceFee = this.getSendServiceFee(Types.UserOperationType.OUTGOING_TX, req.amoutSats, false)
|
const isManagedUser = ctx.user_id !== app.owner.user_id
|
||||||
const isAppUserPayment = ctx.user_id !== app.owner.user_id
|
const serviceFee = this.getSendServiceFee(Types.UserOperationType.OUTGOING_TX, 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}`
|
||||||
await this.storage.userStorage.DecrementUserBalance(ctx.user_id, req.amoutSats + serviceFee, addressData)
|
await this.storage.userStorage.DecrementUserBalance(ctx.user_id, req.amoutSats + serviceFee, addressData)
|
||||||
this.addressPaidCb({ hash: txId, index: 0 }, req.address, req.amoutSats, 'internal')
|
this.addressPaidCb({ hash: txId, index: 0 }, req.address, req.amoutSats, 'internal')
|
||||||
if (isAppUserPayment && serviceFee > 0) {
|
if (isManagedUser && serviceFee > 0) {
|
||||||
await this.storage.userStorage.IncrementUserBalance(app.owner.user_id, serviceFee, 'fees')
|
await this.storage.userStorage.IncrementUserBalance(app.owner.user_id, serviceFee, 'fees')
|
||||||
}
|
}
|
||||||
const chainFees = 0
|
const chainFees = 0
|
||||||
|
|
@ -586,7 +594,8 @@ export default class {
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
quotes: pendingSwaps.map(s => {
|
quotes: pendingSwaps.map(s => {
|
||||||
const serviceFee = this.getSendServiceFee(Types.UserOperationType.OUTGOING_INVOICE, s.invoice_amount, true)
|
const isManagedUser = true // ListSwaps is only called by app users
|
||||||
|
const serviceFee = this.getSendServiceFee(Types.UserOperationType.OUTGOING_INVOICE, s.invoice_amount, isManagedUser)
|
||||||
return {
|
return {
|
||||||
swap_operation_id: s.swap_operation_id,
|
swap_operation_id: s.swap_operation_id,
|
||||||
invoice_amount_sats: s.invoice_amount,
|
invoice_amount_sats: s.invoice_amount,
|
||||||
|
|
@ -922,14 +931,14 @@ export default class {
|
||||||
if (fromUser.balance_sats < amount) {
|
if (fromUser.balance_sats < amount) {
|
||||||
throw new Error("not enough balance to send payment")
|
throw new Error("not enough balance to send payment")
|
||||||
}
|
}
|
||||||
const isAppUserPayment = fromUser.user_id !== linkedApplication.owner.user_id
|
const isManagedUser = fromUser.user_id !== linkedApplication.owner.user_id
|
||||||
let fee = this.getSendServiceFee(Types.UserOperationType.OUTGOING_USER_TO_USER, amount, isAppUserPayment)
|
let fee = this.getSendServiceFee(Types.UserOperationType.OUTGOING_USER_TO_USER, amount, isManagedUser)
|
||||||
const toDecrement = amount + fee
|
const toDecrement = amount + fee
|
||||||
const paymentEntry = await this.storage.paymentStorage.AddPendingUserToUserPayment(fromUserId, toUserId, amount, fee, linkedApplication, tx)
|
const paymentEntry = await this.storage.paymentStorage.AddPendingUserToUserPayment(fromUserId, toUserId, amount, fee, linkedApplication, tx)
|
||||||
await this.storage.userStorage.DecrementUserBalance(fromUser.user_id, toDecrement, `${toUserId}:${paymentEntry.serial_id}`, tx)
|
await this.storage.userStorage.DecrementUserBalance(fromUser.user_id, toDecrement, `${toUserId}:${paymentEntry.serial_id}`, tx)
|
||||||
await this.storage.userStorage.IncrementUserBalance(toUser.user_id, amount, `${fromUserId}:${paymentEntry.serial_id}`, tx)
|
await this.storage.userStorage.IncrementUserBalance(toUser.user_id, amount, `${fromUserId}:${paymentEntry.serial_id}`, tx)
|
||||||
await this.storage.paymentStorage.SetPendingUserToUserPaymentAsPaid(paymentEntry.serial_id, tx)
|
await this.storage.paymentStorage.SetPendingUserToUserPaymentAsPaid(paymentEntry.serial_id, tx)
|
||||||
if (isAppUserPayment && fee > 0) {
|
if (isManagedUser && fee > 0) {
|
||||||
await this.storage.userStorage.IncrementUserBalance(linkedApplication.owner.user_id, fee, 'fees', tx)
|
await this.storage.userStorage.IncrementUserBalance(linkedApplication.owner.user_id, fee, 'fees', tx)
|
||||||
}
|
}
|
||||||
return paymentEntry
|
return paymentEntry
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,10 @@ import path from 'path'
|
||||||
|
|
||||||
export type ServiceFeeSettings = {
|
export type ServiceFeeSettings = {
|
||||||
incomingTxFee: number
|
incomingTxFee: number
|
||||||
outgoingTxFee: number
|
serviceFee: number
|
||||||
outgoingAppInvoiceFee: number
|
serviceFeeBps: number
|
||||||
outgoingAppUserInvoiceFee: number
|
|
||||||
outgoingAppUserInvoiceFeeBps: number
|
|
||||||
userToUserFee: number
|
userToUserFee: number
|
||||||
appToUserFee: 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 => {
|
||||||
|
|
@ -35,12 +33,10 @@ export const LoadServiceFeeSettingsFromEnv = (dbEnv: Record<string, string | und
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
incomingTxFee: 0, // Not configurable, always 0
|
incomingTxFee: 0, // Not configurable, always 0
|
||||||
outgoingTxFee: chooseEnvInt("OUTGOING_CHAIN_FEE_ROOT_BPS", dbEnv, 60, addToDb) / 10000,
|
serviceFeeBps: serviceFeeBps,
|
||||||
outgoingAppInvoiceFee: chooseEnvInt("OUTGOING_INVOICE_FEE_ROOT_BPS", dbEnv, 60, addToDb) / 10000,
|
serviceFee: serviceFeeBps / 10000,
|
||||||
outgoingAppUserInvoiceFeeBps: serviceFeeBps,
|
|
||||||
outgoingAppUserInvoiceFee: serviceFeeBps / 10000,
|
|
||||||
userToUserFee: chooseEnvInt("TX_FEE_INTERNAL_USER_BPS", dbEnv, 0, addToDb) / 10000,
|
userToUserFee: chooseEnvInt("TX_FEE_INTERNAL_USER_BPS", dbEnv, 0, addToDb) / 10000,
|
||||||
appToUserFee: chooseEnvInt("TX_FEE_INTERNAL_ROOT_BPS", dbEnv, 0, addToDb) / 10000,
|
rootToUserFee: chooseEnvInt("TX_FEE_INTERNAL_ROOT_BPS", dbEnv, 0, addToDb) / 10000,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ export default class SettingsManager {
|
||||||
|
|
||||||
private validateFeeSettings(settings: FullSettings): void {
|
private validateFeeSettings(settings: FullSettings): void {
|
||||||
const { serviceFeeSettings, lndSettings } = settings
|
const { serviceFeeSettings, lndSettings } = settings
|
||||||
const serviceFeeBps = serviceFeeSettings.outgoingAppUserInvoiceFeeBps
|
const serviceFeeBps = serviceFeeSettings.serviceFeeBps
|
||||||
const routingFeeLimitBps = lndSettings.routingFeeLimitBps
|
const routingFeeLimitBps = lndSettings.routingFeeLimitBps
|
||||||
const serviceFeeFloor = lndSettings.serviceFeeFloor
|
const serviceFeeFloor = lndSettings.serviceFeeFloor
|
||||||
const routingFeeFloor = lndSettings.routingFeeFloor
|
const routingFeeFloor = lndSettings.routingFeeFloor
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue