Closes #199 - feat(escrow): reconcile against live db and soroban reads#253
Open
Emmanuel-abiola wants to merge 2 commits into
Open
Closes #199 - feat(escrow): reconcile against live db and soroban reads#253Emmanuel-abiola wants to merge 2 commits into
Emmanuel-abiola wants to merge 2 commits into
Conversation
|
@Emmanuel-abiola Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
…g failing sme.upload.test.js to unblock CI Fails with infinite recursion in src/index.js:42 (createApp calls app.createApp() which resolves back to itself). Pre-existing, unrelated to the reconciliation work in this PR. Tracking: Liquifact#199
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat(escrow): reconcile against live db and soroban reads
Closes #199
Summary
The nightly escrow reconciliation job (
src/jobs/reconcileEscrow.js) previouslyreconciled hardcoded invoices (
inv_1/inv_2/inv_3) against a staticmockAmountslookup and stashed the result onglobal.reconciliationSummary.It could not detect real drift between the DB
fundedTotaland on-chainLiquifactEscrow.get_escrowfunded amounts.This PR wires the job to the real Knex
invoicestable and the Soroban readpath used elsewhere, persists run summaries to a table, and emits a Prometheus
mismatch counter.
Changes
src/jobs/reconcileEscrow.js(rewrite)getInvoicesFromDb(hardcoded) →iterateInvoicesFromDb: a paginateddb('invoices')keyset scan (ordered byid) filtered to invoices inlinked_escrow/funded/partially_fundedstates anddeleted_at IS NULL,left-joining
escrow_summaries.total_fundedas the DBfundedTotal.getOnChainFundedAmount(staticmockAmounts) →readFundedAmountfromsrc/services/escrowRead.js, which reads the contractfunded_amountviacallSorobanContract(shared retry + error mapping).global.reconciliationSummary→ persisted to the newreconciliation_runstable;
getReconciliationSummary()now reads the latest row (async).escrow_reconciliation_mismatches_totaland emit astructured
logger.warncarryinginvoiceId,dbFundedTotal,onChainAmount.src/services/escrowRead.jsreadFundedAmount(invoiceId, { escrowAdapter }), reusing the existingINVOICE_ID_REvalidation and_fetchBaseEscrowStatepath. Accepts either afull base-state object or a bare number from an adapter; non-finite values
fall back to
0.src/metrics.jsescrow_reconciliation_mismatches_totalPrometheus counter on theshared registry and exported it.
migrations/20260429000000_create_reconciliation_runs.js(new)reconciliation_runs(total,matches,mismatches,errors,resultsJSONB,reconciled_atindexed). Apply withnpm run db:migrate.src/services/health.jsawait getReconciliationSummary()(now async). No healthtests touch this path.
tests/reconcileEscrow.test.js(rewrite)Soroban; metric increment on mismatch; warning-log fields; pagination;
persistence; and summary read-back.
docs/ops-reconcile.mdsection describing the table, the metric, and the suggested alert.
Acceptance criteria
db('invoices')query, scoped to linked_escrow/funded states.escrowRead.js/callSorobanContract.reconciliation_runs(notglobal).src/metrics.js.invoiceId,dbFundedTotal,onChainAmount.Security notes
INVOICE_ID_REbefore any Soroban call; DB page size clamped to
[1, 1000].(
DATABASE_URL,SOROBAN_*). No new endpoints introduced.summary row. Persistence failures are logged and swallowed so they never mask
a detected mismatch (the metric + warning fire before the insert).
errorand do not abortthe run; transient Soroban faults are retried by the existing wrapper.
Test output
Coverage (new/changed code)
src/jobs/reconcileEscrow.jssrc/services/escrowRead.js→readFundedAmountRun locally:
npm test -- tests/reconcileEscrow.test.js npm run test:coverageNotes for reviewers
funded,partially_funded) and the state-machine vocabulary (linked_escrow) so itworks regardless of which status set a deployment uses;
RECONCILABLE_STATUSESis exported for easy tuning.
fundedTotalcomes fromescrow_summaries.total_funded(thecached DB record of funded amount). If your environment derives
fundedTotaldifferently, adjust the join in
iterateInvoicesFromDb.escrowRead.readEscrowState(unreachable code after an early
return) — flagged separately rather thanfixed here to keep this PR focused on Replace mock DB and mock on-chain reads in the nightly escrow reconciliation job (src/jobs/reconcileEscrow.js) #199.