fix: On-chain event indexer is defined but never started — vault state in the database is never updated from chain#276
Conversation
…e in the database is never updated from chain - Suncrest-Labs#264: [Backend/Feature] On-chain event indexer is defined but never started — vault state in the database is never updated from chain Closes Suncrest-Labs#264
|
@wheval 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! 🚀 |
0xDeon
left a comment
There was a problem hiding this comment.
Three blocking issues: (1) startLedger resets to 0 on every API restart — all balance updates are additive so replaying history doubles every vault balance in the database. (2) The existing EventPoller.PollEvents in internal/stellar/events.go remains a stub returning empty events — this PR adds a parallel implementation in main.go instead of fixing the actual infrastructure. (3) No tests on any function that writes to the financial database.
| startEventIndexer(shutdownCtx, baseLogger, db, cfg.Stellar().RPCURL()) | ||
|
|
||
| serverErr := make(chan error, 1) | ||
| go func() { |
There was a problem hiding this comment.
startLedger is a local variable — it resets to 0 on every API restart. The deposit and withdraw update queries are additive (total_deposited + amount), so every restart replays all historical events from ledger 0 and doubles every vault balance. The last indexed ledger must be persisted to the database (e.g. a system_state table) and read on startup so the indexer resumes from where it left off rather than replaying from the beginning.
| logger.Error("event indexer fetch failed", "error", err) | ||
| continue | ||
| } | ||
|
|
There was a problem hiding this comment.
This update is not idempotent. If events are replayed (restart, catchup), total_deposited and current_balance accumulate duplicate additions. Either persist processed event IDs and skip duplicates, or use absolute SET values sourced from on-chain state instead of additive increments.
| return fmt.Errorf("withdraw event missing parseable amount") | ||
| } | ||
| _, err := db.ExecContext( | ||
| ctx, |
There was a problem hiding this comment.
float64 cannot represent large financial amounts precisely (max 15 significant digits). On-chain Stellar amounts are integers — if the event value arrives as a JSON number, parse it as a string via fmt.Sprintf("%v", v) or reject it. Using decimal.NewFromFloat on a float64 can silently corrupt amounts for large vaults.
Persist the event indexer cursor, dedupe processed chain events, and harden amount parsing to prevent replay-driven balance inflation and precision loss. Also implement the real EventPoller getEvents path and add tests for DB write safety and event parsing. Closes Suncrest-Labs#264 Made-with: Cursor
Summary
Implementation notes
Closing
Closes #264