Skip to content

Commit

Permalink
add liquidty ops
Browse files Browse the repository at this point in the history
  • Loading branch information
boufni95 committed Jun 20, 2024
1 parent aeb5407 commit a759377
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 11 deletions.
4 changes: 4 additions & 0 deletions src/services/lnd/liquidityProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ export class LiquidityProvider {
})
}

GetLatestMaxWithdrawable = () => {
return this.latestMaxWithdrawable || 0
}

CheckUserState = async () => {
const res = await this.client.GetUserInfo()
if (res.status === 'ERROR') {
Expand Down
8 changes: 4 additions & 4 deletions src/services/lnd/lnd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,11 +269,11 @@ export default class {
return res.response
}

async NewInvoice(value: number, memo: string, expiry: number): Promise<Invoice> {
async NewInvoice(value: number, memo: string, expiry: number, useProvider = false): Promise<Invoice> {
this.log("generating new invoice for", value, "sats")
await this.Health()
const shouldUseLiquidityProvider = await this.ShouldUseLiquidityProvider({ action: 'receive', amount: value })
if (shouldUseLiquidityProvider) {
if (shouldUseLiquidityProvider || useProvider) {
const invoice = await this.liquidProvider.AddInvoice(value, memo)
return { payRequest: invoice }
}
Expand All @@ -300,15 +300,15 @@ export default class {
const r = res.response
return { local: r.localBalance ? Number(r.localBalance.sat) : 0, remote: r.remoteBalance ? Number(r.remoteBalance.sat) : 0 }
}
async PayInvoice(invoice: string, amount: number, feeLimit: number): Promise<PaidInvoice> {
async PayInvoice(invoice: string, amount: number, feeLimit: number, useProvider = false): Promise<PaidInvoice> {
if (this.outgoingOpsLocked) {
this.log("outgoing ops locked, rejecting payment request")
throw new Error("lnd node is currently out of sync")
}
await this.Health()
this.log("paying invoice", invoice, "for", amount, "sats")
const shouldUseLiquidityProvider = await this.ShouldUseLiquidityProvider({ action: 'spend', amount })
if (shouldUseLiquidityProvider) {
if (shouldUseLiquidityProvider || useProvider) {
const res = await this.liquidProvider.PayInvoice(invoice)
return { feeSat: res.network_fee + res.service_fee, valueSat: res.amount_paid, paymentPreimage: res.preimage }
}
Expand Down
2 changes: 1 addition & 1 deletion src/services/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export default class {
this.liquidityManager = new LiquidityManager(this.settings.liquiditySettings, this.storage, this.liquidProvider, this.lnd)
this.metricsManager = new MetricsManager(this.storage, this.lnd)

this.paymentManager = new PaymentManager(this.storage, this.lnd, this.settings, this.addressPaidCb, this.invoicePaidCb)
this.paymentManager = new PaymentManager(this.storage, this.lnd, this.settings, this.liquidityManager, this.addressPaidCb, this.invoicePaidCb)
this.productManager = new ProductManager(this.storage, this.paymentManager, this.settings)
this.applicationManager = new ApplicationManager(this.storage, this.settings, this.paymentManager)
this.appUserManager = new AppUserManager(this.storage, this.settings, this.applicationManager)
Expand Down
32 changes: 30 additions & 2 deletions src/services/main/liquidityManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { LiquidityProvider } from "../lnd/liquidityProvider.js"
import LND from "../lnd/lnd.js"
import { FlashsatsLSP, LoadLSPSettingsFromEnv, LSPSettings, OlympusLSP, VoltageLSP } from "../lnd/lsp.js"
import Storage from '../storage/index.js'
import { defaultInvoiceExpiry } from "../storage/paymentStorage.js"
export type LiquiditySettings = {
lspSettings: LSPSettings
liquidityProviderPub: string
Expand Down Expand Up @@ -32,7 +33,26 @@ export class LiquidityManager {
this.voltageLSP = new VoltageLSP(settings.lspSettings, lnd, liquidityProvider)
this.flashsatsLSP = new FlashsatsLSP(settings.lspSettings, lnd, liquidityProvider)
}
beforeInvoiceCreation = async () => { }
onNewBlock = async () => {
const balance = await this.liquidityProvider.GetLatestMaxWithdrawable()
const { remote } = await this.lnd.ChannelBalance()
if (remote > balance) {
this.log("draining provider balance to channel")
const invoice = await this.lnd.NewInvoice(balance, "liqudity provider drain", defaultInvoiceExpiry)
const res = await this.liquidityProvider.PayInvoice(invoice.payRequest)
this.log("drained provider balance to channel", res.amount_paid)
}
}

beforeInvoiceCreation = async (amount: number): Promise<'lnd' | 'provider'> => {
const { remote } = await this.lnd.ChannelBalance()
if (remote > amount) {
this.log("channel has enough balance for invoice")
return 'lnd'
}
this.log("channel does not have enough balance for invoice,suggesting provider")
return 'provider'
}
afterInInvoicePaid = async () => {
const existingOrder = await this.storage.liquidityStorage.GetLatestLspOrder()
if (existingOrder) {
Expand Down Expand Up @@ -67,6 +87,14 @@ export class LiquidityManager {
this.log("no channel requested")
}

beforeOutInvoicePayment = async () => { }
beforeOutInvoicePayment = async (amount: number): Promise<'lnd' | 'provider'> => {
const balance = await this.liquidityProvider.GetLatestMaxWithdrawable()
if (balance > amount) {
this.log("provider has enough balance for payment")
return 'provider'
}
this.log("provider does not have enough balance for payment, suggesting lnd")
return 'lnd'
}
afterOutInvoicePaid = async () => { }
}
11 changes: 8 additions & 3 deletions src/services/main/paymentManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { AddressReceivingTransaction } from '../storage/entity/AddressReceivingT
import { UserTransactionPayment } from '../storage/entity/UserTransactionPayment.js'
import { Watchdog } from './watchdog.js'
import { LiquidityProvider } from '../lnd/liquidityProvider.js'
import { LiquidityManager } from './liquidityManager.js'
interface UserOperationInfo {
serial_id: number
paid_amount: number
Expand Down Expand Up @@ -47,11 +48,13 @@ export default class {
invoicePaidCb: InvoicePaidCb
log = getLogger({ component: "PaymentManager" })
watchDog: Watchdog
constructor(storage: Storage, lnd: LND, settings: MainSettings, addressPaidCb: AddressPaidCb, invoicePaidCb: InvoicePaidCb) {
liquidityManager: LiquidityManager
constructor(storage: Storage, lnd: LND, settings: MainSettings, liquidityManager: LiquidityManager, addressPaidCb: AddressPaidCb, invoicePaidCb: InvoicePaidCb) {
this.storage = storage
this.settings = settings
this.lnd = lnd
this.watchDog = new Watchdog(settings.watchDogSettings, lnd, storage)
this.liquidityManager = liquidityManager
this.addressPaidCb = addressPaidCb
this.invoicePaidCb = invoicePaidCb
}
Expand Down Expand Up @@ -121,7 +124,8 @@ export default class {
if (user.locked) {
throw new Error("user is banned, cannot generate invoice")
}
const res = await this.lnd.NewInvoice(req.amountSats, req.memo, options.expiry)
const use = await this.liquidityManager.beforeInvoiceCreation(req.amountSats)
const res = await this.lnd.NewInvoice(req.amountSats, req.memo, options.expiry, use === 'provider')
const userInvoice = await this.storage.paymentStorage.AddUserInvoice(user, res.payRequest, options)
const appId = options.linkedApplication ? options.linkedApplication.app_id : ""
this.storage.eventsLog.LogEvent({ type: 'new_invoice', userId: user.user_id, appUserId: "", appId, balance: user.balance_sats, data: userInvoice.invoice, amount: req.amountSats })
Expand Down Expand Up @@ -201,8 +205,9 @@ export default class {
const routingFeeLimit = this.lnd.GetFeeLimitAmount(payAmount)
await this.storage.userStorage.DecrementUserBalance(userId, totalAmountToDecrement + routingFeeLimit, invoice)
const pendingPayment = await this.storage.paymentStorage.AddPendingExternalPayment(userId, invoice, payAmount, linkedApplication)
const use = await this.liquidityManager.beforeOutInvoicePayment(payAmount)
try {
const payment = await this.lnd.PayInvoice(invoice, amountForLnd, routingFeeLimit)
const payment = await this.lnd.PayInvoice(invoice, amountForLnd, routingFeeLimit, use === 'provider')

if (routingFeeLimit - payment.feeSat > 0) {
this.log("refund routing fee", routingFeeLimit, payment.feeSat, "sats")
Expand Down
6 changes: 5 additions & 1 deletion src/services/main/watchdog.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { EnvCanBeInteger } from "../helpers/envParser.js";
import FunctionQueue from "../helpers/functionQueue.js";
import { getLogger } from "../helpers/logger.js";
import { LiquidityProvider } from "../lnd/liquidityProvider.js";
import LND from "../lnd/lnd.js";
import { ChannelBalance } from "../lnd/settings.js";
import Storage from '../storage/index.js'
Expand All @@ -20,6 +21,7 @@ export class Watchdog {
latestIndexOffset: number;
accumulatedHtlcFees: number;
lnd: LND;
liquidProvider: LiquidityProvider;
settings: WatchdogSettings;
storage: Storage;
latestCheckStart = 0
Expand All @@ -30,6 +32,7 @@ export class Watchdog {
this.lnd = lnd;
this.settings = settings;
this.storage = storage;
this.liquidProvider = lnd.liquidProvider
this.queue = new FunctionQueue("watchdog_queue", () => this.StartCheck())
}

Expand Down Expand Up @@ -76,7 +79,8 @@ export class Watchdog {
getLogger({ component: "debugLndBalancev3" })({ w: walletBalance, c: channelsBalance, u: usersTotal, f: this.accumulatedHtlcFees })
const totalLightningBalanceMsats = (channelsBalance.localBalance?.msat || 0n) + (channelsBalance.unsettledLocalBalance?.msat || 0n)
const totalLightningBalance = Math.ceil(Number(totalLightningBalanceMsats) / 1000)
return Number(walletBalance.confirmedBalance) + totalLightningBalance
const providerBalance = this.liquidProvider.GetLatestMaxWithdrawable()
return Number(walletBalance.confirmedBalance) + totalLightningBalance + providerBalance
}

checkBalanceUpdate = (deltaLnd: number, deltaUsers: number) => {
Expand Down

0 comments on commit a759377

Please sign in to comment.