check chain
This commit is contained in:
parent
d562ba0586
commit
dff50783bb
2 changed files with 170 additions and 4 deletions
|
|
@ -72,7 +72,8 @@ export const initMainHandler = async (log: PubLogger, settingsManager: SettingsM
|
|||
if (stop) {
|
||||
return
|
||||
}
|
||||
await mainHandler.paymentManager.checkPendingPayments()
|
||||
await mainHandler.paymentManager.checkPaymentStatus()
|
||||
await mainHandler.paymentManager.checkMissedChainTxs()
|
||||
await mainHandler.paymentManager.CleanupOldUnpaidInvoices()
|
||||
await mainHandler.appUserManager.CleanupInactiveUsers()
|
||||
await mainHandler.appUserManager.CleanupNeverActiveUsers()
|
||||
|
|
|
|||
|
|
@ -75,11 +75,11 @@ export default class {
|
|||
this.swaps.Stop()
|
||||
}
|
||||
|
||||
checkPendingPayments = async () => {
|
||||
const log = getLogger({ component: 'pendingPaymentsOnStart' })
|
||||
checkPaymentStatus = async () => {
|
||||
const log = getLogger({ component: 'checkPaymentStatus' })
|
||||
const pendingPayments = await this.storage.paymentStorage.GetPendingPayments()
|
||||
for (const p of pendingPayments) {
|
||||
log("checking state of payment: ", p.invoice)
|
||||
log("checking status of payment: ", p.invoice)
|
||||
if (p.internal) {
|
||||
log("found pending internal payment", p.serial_id)
|
||||
} else if (p.liquidityProvider) {
|
||||
|
|
@ -170,6 +170,171 @@ export default class {
|
|||
}
|
||||
}
|
||||
|
||||
checkMissedChainTxs = async () => {
|
||||
const log = getLogger({ component: 'checkMissedChainTxs' })
|
||||
|
||||
// Skip chain tx check when bypass is enabled
|
||||
if (this.liquidityManager.settings.getSettings().liquiditySettings.useOnlyLiquidityProvider) {
|
||||
log("USE_ONLY_LIQUIDITY_PROVIDER enabled, skipping chain tx check")
|
||||
return
|
||||
}
|
||||
|
||||
log("checking for missed confirmed chain transactions...")
|
||||
|
||||
// Get transactions from LND (only includes transactions relevant to the wallet)
|
||||
// Use startHeight 0 to check all transactions
|
||||
const startHeight = 0
|
||||
|
||||
try {
|
||||
const { transactions } = await this.lnd.GetTransactions(startHeight)
|
||||
log(`retrieved ${transactions.length} transactions from LND`)
|
||||
|
||||
let recoveredCount = 0
|
||||
|
||||
for (const tx of transactions) {
|
||||
// Only process confirmed transactions (at least 1 confirmation)
|
||||
// Pending transactions (0 confirmations) are handled by SubscribeAddressPaid()
|
||||
// and can be dropped from mempool, so we don't process them here
|
||||
if (tx.numConfirmations === 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check each output detail for addresses we care about
|
||||
if (!tx.outputDetails || tx.outputDetails.length === 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
for (const output of tx.outputDetails) {
|
||||
// Only process outputs that are our addresses and have an address
|
||||
if (!output.address || !output.isOurAddress) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if this address belongs to a user (lazy lookup - only for addresses in transactions)
|
||||
const userAddress = await this.storage.paymentStorage.GetAddressOwner(output.address)
|
||||
if (!userAddress) {
|
||||
// Not a user address, skip
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if we already have this transaction in the database
|
||||
const existingTx = await this.storage.paymentStorage.GetAddressReceivingTransactionOwner(
|
||||
output.address,
|
||||
tx.txHash
|
||||
)
|
||||
|
||||
if (existingTx) {
|
||||
// Transaction already recorded, skip
|
||||
continue
|
||||
}
|
||||
|
||||
// This is a missed transaction - process it
|
||||
const amount = Number(output.amount)
|
||||
const outputIndex = Number(output.outputIndex)
|
||||
log(`processing missed chain tx: address=${output.address}, txHash=${tx.txHash}, amount=${amount}, outputIndex=${outputIndex}`)
|
||||
|
||||
try {
|
||||
await this.storage.StartTransaction(async dbTx => {
|
||||
if (!userAddress.linkedApplication) {
|
||||
log(ERROR, "found address with no linked application during recovery:", output.address)
|
||||
return
|
||||
}
|
||||
|
||||
const isManagedUser = userAddress.user.user_id !== userAddress.linkedApplication.owner.user_id
|
||||
const fee = this.getReceiveServiceFee(Types.UserOperationType.INCOMING_TX, amount, isManagedUser)
|
||||
const blockHeight = tx.blockHeight || 0
|
||||
|
||||
// Add the transaction record
|
||||
const addedTx = await this.storage.paymentStorage.AddAddressReceivingTransaction(
|
||||
userAddress,
|
||||
tx.txHash,
|
||||
outputIndex,
|
||||
amount,
|
||||
fee,
|
||||
false, // not internal
|
||||
blockHeight,
|
||||
dbTx
|
||||
)
|
||||
|
||||
// Update balance
|
||||
const addressData = `${output.address}:${tx.txHash}`
|
||||
this.storage.eventsLog.LogEvent({
|
||||
type: 'address_paid',
|
||||
userId: userAddress.user.user_id,
|
||||
appId: userAddress.linkedApplication.app_id,
|
||||
appUserId: "",
|
||||
balance: userAddress.user.balance_sats,
|
||||
data: addressData,
|
||||
amount
|
||||
})
|
||||
|
||||
await this.storage.userStorage.IncrementUserBalance(
|
||||
userAddress.user.user_id,
|
||||
amount - fee,
|
||||
addressData,
|
||||
dbTx
|
||||
)
|
||||
|
||||
if (fee > 0) {
|
||||
await this.storage.userStorage.IncrementUserBalance(
|
||||
userAddress.linkedApplication.owner.user_id,
|
||||
fee,
|
||||
'fees',
|
||||
dbTx
|
||||
)
|
||||
}
|
||||
|
||||
// Send operation to Nostr
|
||||
const operationId = `${Types.UserOperationType.INCOMING_TX}-${addedTx.serial_id}`
|
||||
const op = {
|
||||
amount,
|
||||
paidAtUnix: tx.timeStamp ? Number(tx.timeStamp) : Math.floor(Date.now() / 1000),
|
||||
inbound: true,
|
||||
type: Types.UserOperationType.INCOMING_TX,
|
||||
identifier: userAddress.address,
|
||||
operationId,
|
||||
network_fee: 0,
|
||||
service_fee: fee,
|
||||
confirmed: tx.numConfirmations > 0,
|
||||
tx_hash: tx.txHash,
|
||||
internal: false
|
||||
}
|
||||
|
||||
// Note: sendOperationToNostr is not available here, but the operation will be synced
|
||||
// when the user next syncs their operations
|
||||
|
||||
this.utils.stateBundler.AddTxPoint(
|
||||
'addressWasPaid',
|
||||
amount,
|
||||
{ used: 'lnd', from: 'system', timeDiscount: true },
|
||||
userAddress.linkedApplication.app_id
|
||||
)
|
||||
|
||||
recoveredCount++
|
||||
log(`successfully processed missed chain tx: address=${output.address}, txHash=${tx.txHash}, amount=${amount}`)
|
||||
}, "process missed chain tx")
|
||||
} catch (err: any) {
|
||||
log(ERROR, `failed to process missed chain tx for address=${output.address}, txHash=${tx.txHash}:`, err.message || err)
|
||||
this.utils.stateBundler.AddTxPointFailed(
|
||||
'addressWasPaid',
|
||||
amount,
|
||||
{ used: 'lnd', from: 'system' },
|
||||
userAddress.linkedApplication?.app_id
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (recoveredCount > 0) {
|
||||
log(`processed ${recoveredCount} missed chain tx(s)`)
|
||||
} else {
|
||||
log("no missed chain transactions found")
|
||||
}
|
||||
} catch (err: any) {
|
||||
log(ERROR, "failed to check for missed chain transactions:", err.message || err)
|
||||
}
|
||||
}
|
||||
|
||||
getReceiveServiceFee = (action: Types.UserOperationType, amount: number, managedUser: boolean): number => {
|
||||
switch (action) {
|
||||
case Types.UserOperationType.INCOMING_TX:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue