feat(indexer): decode Soroban event XDR and upsert contributions & loans (closes #2) - #18
Merged
Mona-i merged 1 commit intoJul 24, 2026
Conversation
…ans (closes coopfinance#2) fetchAndStoreEvents had a TODO where decoded events should be persisted, so the Horizon sync loop was a no-op. Decode each event's topic/value XDR with scValToNative and route by event name onto the Prisma models: - contribution (member, amount, period) -> Contribution, idempotent on txHash - loan_requested/approved/repaid (id, borrower, amount[, status]) -> Loan, matched by (groupId, onChainId) so lifecycle updates don't duplicate rows Unsuccessful calls are skipped; a single unparseable record is logged and skipped instead of aborting the batch. Adds a ts-jest config and a spec that builds real event XDR the same way the contracts emit it.
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.
Closes #2.
Problem
src/common/stellar-indexer.service.tspolls Horizon for contract events, butfetchAndStoreEventsstopped at a// TODO: Parse and upsert…— so the whole sync loop was a no-op and the DB was never populated from on-chain data.What this does
Implements the decode + upsert step inside the existing structure (keeps the
@Cronpollers and the(groupId, contractId, eventType)signature intact — no rewrite of the polling design):scValToNative(xdr.ScVal.fromXDR(base64, "base64")). Routing is driven by the event name in the first topic, so it's robust regardless of the poller'seventTypehint.(member: Address, amount: i128, period: u32)→Contribution, idempotent viaupserton the uniquetxHash.loan_requested/loan_approved/loan_repaid(id, borrower, amount[, status])→Loan, matched by(groupId, onChainId)so lifecycle events update the same row instead of duplicating it (approvedAt/repaidAtset accordingly).in_successful_contract_call === false; a single unparseable record is logged and skipped instead of aborting the batch.The event shapes match exactly what the contracts emit (
treasury::contributepublishes("contribution", (member, amount, period));loan::request/approve/repaypublish(id, borrower, amount[, status])).Tests
Adds a
ts-jestconfig (ts-jest was already a devDependency but no jest config existed) andstellar-indexer.service.spec.ts, which builds real event XDR withnativeToScValthe same way the contracts emit it and asserts the decode + upsert routing:No new runtime dependencies (
@stellar/stellar-sdkwas already present);package-lock.jsonis untouched.