Files
psf-memo-indexer/src/use-cases/index-blocks.js
T

130 lines
3.8 KiB
JavaScript
Raw Normal View History

2026-06-02 16:47:09 -07:00
/*
Business logic for indexing blocks with Memo transactions.
*/
import RetryQueue from '@chris.troutner/retry-queue'
2026-06-02 17:11:42 -07:00
import PQueue from 'p-queue'
import config from '../../config/index.js'
2026-06-02 16:47:09 -07:00
import FilterBlock from './filter-block.js'
import { findMemoOutputs, getSignerAddress } from '../lib/memo-parser.js'
2026-06-03 12:00:11 -07:00
import { logMemoTxResult } from '../lib/debug-log.js'
2026-06-02 16:47:09 -07:00
import { dispatchMemoAction } from './action-types/index.js'
class IndexBlocks {
constructor (localConfig = {}) {
if (!localConfig.adapters) {
throw new Error('Adapters required for index-blocks.js')
}
this.adapters = localConfig.adapters
this.filterBlock = new FilterBlock({ adapters: this.adapters })
this.retryQueue = new RetryQueue()
2026-06-02 17:11:42 -07:00
this.pQueue = new PQueue({ concurrency: config.memoTxConcurrency })
2026-06-02 16:47:09 -07:00
this.processBlock = this.processBlock.bind(this)
this.processMemoTx = this.processMemoTx.bind(this)
this.processMemoTxs = this.processMemoTxs.bind(this)
}
2026-06-03 12:00:11 -07:00
async getProcessErrorMessage (txid) {
try {
const record = await this.adapters.processErrorDb.get(txid)
return record && record.error ? record.error : null
} catch (err) {
return null
}
}
async processMemoTx (txid, blockHeight, seen = Date.now()) {
let actions = []
2026-06-02 16:47:09 -07:00
try {
try {
await this.adapters.ptxDb.get(txid)
2026-06-03 12:00:11 -07:00
logMemoTxResult({ txid, actions, skipped: true, success: true })
2026-06-02 16:47:09 -07:00
return true
} catch (err) {
// not processed yet
}
const txDetails = await this.adapters.transaction.get(txid)
2026-06-03 12:00:11 -07:00
const memoOutputs = findMemoOutputs(txDetails)
actions = memoOutputs.map((output) => output.action)
2026-06-02 16:47:09 -07:00
const signerAddr = getSignerAddress(txDetails)
if (!signerAddr) {
2026-06-03 12:00:11 -07:00
const error = 'could not find input address for memo tx'
2026-06-02 16:47:09 -07:00
await this.adapters.processErrorDb.create(txid, {
2026-06-03 12:00:11 -07:00
error,
2026-06-02 16:47:09 -07:00
ts: Date.now()
})
2026-06-03 12:00:11 -07:00
logMemoTxResult({ txid, actions, success: false, error })
2026-06-02 16:47:09 -07:00
return false
}
2026-06-03 12:00:11 -07:00
const processedAt = Date.now()
2026-06-02 16:47:09 -07:00
for (const decoded of memoOutputs) {
await dispatchMemoAction({
adapters: this.adapters,
txid,
txDetails,
signerAddr,
decoded,
seen,
blockHeight
})
}
2026-06-03 12:00:11 -07:00
const handlerError = await this.getProcessErrorMessage(txid)
if (handlerError) {
logMemoTxResult({ txid, actions, success: false, error: handlerError })
await this.adapters.ptxDb.create(txid, { processedAt, blockHeight })
return true
}
await this.adapters.ptxDb.create(txid, { processedAt, blockHeight })
logMemoTxResult({ txid, actions, success: true })
2026-06-02 16:47:09 -07:00
return true
} catch (err) {
2026-06-03 12:00:11 -07:00
logMemoTxResult({ txid, actions, success: false, error: err.message })
2026-06-02 16:47:09 -07:00
console.error(`Error processing memo tx ${txid}:`, err.message)
throw err
}
}
2026-06-03 12:00:11 -07:00
async processMemoTxs (txids, blockHeight, seen) {
2026-06-02 17:11:42 -07:00
const tasks = txids.map((txid) => async () => {
2026-06-03 12:00:11 -07:00
await this.processMemoTx(txid, blockHeight, seen)
2026-06-02 17:11:42 -07:00
})
await this.pQueue.addAll(tasks)
2026-06-02 16:47:09 -07:00
return true
}
async processBlock (blockHeight) {
const blockHash = await this.retryQueue.addToQueue(
this.adapters.rpc.getBlockHash,
{ height: blockHeight }
)
const block = await this.retryQueue.addToQueue(
this.adapters.rpc.getBlock,
{ hash: blockHash }
)
const txs = block.tx
const now = new Date()
console.log(
`\nIndexing block ${blockHeight} with ${txs.length} txs. ${now.toLocaleString()}`
)
const memoTxs = await this.filterBlock.filterMemoTxs(txs)
if (memoTxs.length) {
console.log(`Memo txs in block: ${memoTxs.length}`)
2026-06-03 12:00:11 -07:00
const blockSeen = block.time * 1000
await this.processMemoTxs(memoTxs, blockHeight, blockSeen)
2026-06-02 16:47:09 -07:00
}
return true
}
}
export default IndexBlocks