import type { Selectable } from 'kysely'
import { nanoid } from 'nanoid'
import type { Address } from 'viem'
import type * as Db from '../Db.js'
import type * as db_Schema from '../Schema.js'
/** Columns of the `sponsorship_reconciliation_cursors` table. */
export type Table = db_Schema.SponsorshipReconciliationCursor
/** A durable failed-sponsorship scan position. */
export type Record = Selectable
/** Loads or initializes a versioned scan for one chain and fee payer. */
export async function load(db: Db.Db, options: load.Options): Promise {
await db.kysely
.insertInto('sponsorship_reconciliation_cursors')
.values({
...options,
blockNumber: '0',
complete: true,
retryCount: 0,
retryKey: null,
revision: nanoid(),
transactionIndex: 0,
updatedAt: new Date().toISOString(),
})
.onConflict((oc) => oc.columns(['chainId', 'feePayer']).doNothing())
.execute()
return db.kysely
.selectFrom('sponsorship_reconciliation_cursors')
.selectAll()
.where('chainId', '=', options.chainId)
.where('feePayer', '=', options.feePayer)
.executeTakeFirstOrThrow()
}
export declare namespace load {
/** Identity of one failed-sponsorship scan. */
type Options = {
/** Chain whose fee-payer transactions are scanned. */
chainId: number
/** Lowercase managed fee-payer address. */
feePayer: Address
}
}
/** Saves progress only when the loaded revision is current, returning whether it changed. */
export async function save(db: Db.Db, record: Record): Promise {
// Retain completed rows so an older invocation cannot recreate a cleared scan.
const result = await db.kysely
.updateTable('sponsorship_reconciliation_cursors')
.set({
blockNumber: record.blockNumber,
complete: record.complete,
retryCount: record.retryCount,
retryKey: record.retryKey,
revision: nanoid(),
transactionIndex: record.transactionIndex,
updatedAt: new Date().toISOString(),
})
.where('chainId', '=', record.chainId)
.where('feePayer', '=', record.feePayer)
.where('revision', '=', record.revision)
.executeTakeFirst()
return result.numUpdatedRows > 0n
}