Skip to content

fix: On-chain event indexer is defined but never started — vault state in the database is never updated from chain#276

Open
wheval wants to merge 2 commits intoSuncrest-Labs:mainfrom
wheval:internal/stellar/event_indexer.go
Open

fix: On-chain event indexer is defined but never started — vault state in the database is never updated from chain#276
wheval wants to merge 2 commits intoSuncrest-Labs:mainfrom
wheval:internal/stellar/event_indexer.go

Conversation

@wheval
Copy link
Copy Markdown
Contributor

@wheval wheval commented Apr 23, 2026

Summary

Implementation notes

Closing

Closes #264

…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 wheval requested a review from 0xDeon as a code owner April 23, 2026 11:45
@drips-wave
Copy link
Copy Markdown

drips-wave Bot commented Apr 23, 2026

@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! 🚀

Learn more about application limits

Copy link
Copy Markdown
Contributor

@0xDeon 0xDeon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread apps/api/cmd/api/main.go
startEventIndexer(shutdownCtx, baseLogger, db, cfg.Stellar().RPCURL())

serverErr := make(chan error, 1)
go func() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread apps/api/cmd/api/main.go
logger.Error("event indexer fetch failed", "error", err)
continue
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread apps/api/cmd/api/main.go
return fmt.Errorf("withdraw event missing parseable amount")
}
_, err := db.ExecContext(
ctx,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Backend/Feature] On-chain event indexer is defined but never started — vault state in the database is never updated from chain

2 participants