Make on-chain reconciliation correct and complete (#211) - #212
Conversation
Make on-chain reconciliation correct and complete (#211) Events are a lossy notification channel, not durable history. This makes the durable contract-state path a first-class source and teaches the code to tell "no payments" apart from "cannot know". lib/stellar/events.ts - Exhaustive pagination: the cursor, not a short page, terminates the walk. Previously a page with fewer than `limit` records ended the loop even with a cursor outstanding, silently dropping records on an active trip. - Hitting MAX_PAGES now reports `truncated` instead of exiting silently. - Retention expiry is detected (`isRetentionWindowError`) and surfaced as `retentionExpired` rather than being swallowed into an empty result that looks authoritative. lib/settlement/reconcile.ts - Documented state-vs-event authority rule: durable contract state wins on disagreement; an event-only payment is still accepted (simulation reads an older snapshot, and settlement is monotonic + idempotent). - On key collision the state record is kept as canonical. - Reports `stateArchived`, `eventsRetentionExpired`, and `degraded` so the UI can render "unknown" rather than "unpaid" when neither source can speak. hooks/useContractEvents.ts - Contract state is re-read on first load, whenever events are pruned or truncated, and on a slow timer, so a long-lived session stays correct as it ages past the RPC retention window. - Exposes `degraded` / `stateArchived`. - Merge is explicitly idempotent and order-independent. hooks/usePollBudget.ts (new) - Process-wide concurrency cap and minimum inter-poll gap, so polling cost does not grow with the number of open trips and the visibility-change refresh no longer produces a burst. Tests cover retention expiry, pagination overflow and cursor edge cases, and event/state disagreement. Note: they were not executed here - this checkout has no installed node_modules and no dependencies were added. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> @
🤖 CodeAnt AI — Review Status
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Thanks for using CodeAnt! 🎉We're free for open-source projects. if you're enjoying it, help us grow by sharing. Share on X · |
| // 2. Read live RPC event stream (real-time notifications) | ||
| // 2. Read the live RPC event stream — fresher, but lossy. | ||
| try { | ||
| const eventsResult = await fetchContractEvents(0, tripId); |
There was a problem hiding this comment.
Suggestion: Passing 0 limits events to the latest 600 ledgers, so older trips can return an empty valid page and incorrectly avoid degraded status. [logic error]
Assessment: 🟠 Major · 🔁 Occurrence: Sometimes
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** lib/settlement/reconcile.ts
**Line:** 373:373
**Comment:**
*Logic Error: Passing `0` limits events to the latest 600 ledgers, so older trips can return an empty valid page and incorrectly avoid `degraded` status.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix| if (needsState) { | ||
| try { | ||
| const stateRes = await getContractPayments(tripId); | ||
| lastStateReadRef.current = Date.now(); |
There was a problem hiding this comment.
Suggestion: Updating lastStateReadRef for unsuccessful reads makes later polls treat missing durable state as authoritative and clear degraded incorrectly. [incorrect condition logic]
Assessment: 🟠 Major · 🔁 Occurrence: Sometimes
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** hooks/useContractEvents.ts
**Line:** 115:115
**Comment:**
*Incorrect Condition Logic: Updating `lastStateReadRef` for unsuccessful reads makes later polls treat missing durable state as authoritative and clear `degraded` incorrectly.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix| setEvents((prev) => { | ||
| const known = new Set(prev.map(buildPaymentEventKey)); | ||
| const toAdd = allNewEvents.filter((e) => !known.has(buildPaymentEventKey(e))); | ||
| const seen = new Set<string>(); | ||
| const toAdd: ContractPaymentEvent[] = []; | ||
| for (const e of allNewEvents) { | ||
| const key = buildPaymentEventKey(e); | ||
| if (known.has(key) || seen.has(key)) continue; | ||
| seen.add(key); | ||
| toAdd.push(e); | ||
| } | ||
| return toAdd.length > 0 ? [...prev, ...toAdd] : prev; |
There was a problem hiding this comment.
Suggestion: State records are appended after events, but this merge skips existing keys, so an event record remains instead of being replaced by the canonical state record. [api mismatch]
Assessment: 🟠 Major · 🔁 Occurrence: Sometimes
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** hooks/useContractEvents.ts
**Line:** 139:149
**Comment:**
*Api Mismatch: State records are appended after events, but this merge skips existing keys, so an event record remains instead of being replaced by the canonical state record.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix| }, [tripId, fetch_]); | ||
|
|
||
| return { events, latestLedger, isLoading, error, refresh }; | ||
| return { events, latestLedger, isLoading, error, degraded, stateArchived, refresh }; |
There was a problem hiding this comment.
Suggestion: The new degraded and stateArchived values are returned but not passed to the page or settlement components, so unknown settlement status still appears as unpaid. [incomplete implementation]
Assessment: 🟠 Major · 🔁 Occurrence: Sometimes
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** hooks/useContractEvents.ts
**Line:** 227:227
**Comment:**
*Incomplete Implementation: The new `degraded` and `stateArchived` values are returned but not passed to the page or settlement components, so unknown settlement status still appears as unpaid.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
CodeAnt Nitpicks1 code suggestion1. The retention matcher classifies broad errors such as “not found”, “outside”, and any
|
User description
close #211
Events are a lossy notification channel, not durable history. This makes the durable contract-state path a first-class source and teaches the code to tell "no payments" apart from "cannot know".
lib/stellar/events.ts
limitrecords ended the loop even with a cursor outstanding, silently dropping records on an active trip.truncatedinstead of exiting silently.isRetentionWindowError) and surfaced asretentionExpiredrather than being swallowed into an empty result that looks authoritative.lib/settlement/reconcile.ts
stateArchived,eventsRetentionExpired, anddegradedso the UI can render "unknown" rather than "unpaid" when neither source can speak.hooks/useContractEvents.ts
degraded/stateArchived.hooks/usePollBudget.ts (new)
Tests cover retention expiry, pagination overflow and cursor edge cases, and event/state disagreement. Note: they were not executed here - this checkout has no installed node_modules and no dependencies were added.
@
CodeAnt-AI Description
Make on-chain payment reconciliation complete and distinguish unknown status
What Changed
Impact
✅ Fewer missed on-chain payments✅ Clearer unknown settlement status✅ Lower polling load with multiple open trips💡 Usage Guide
Checking Your Pull Request
Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.
Talking to CodeAnt AI
Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:
This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.
Example
Preserve Org Learnings with CodeAnt
You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:
This helps CodeAnt AI learn and adapt to your team's coding style and standards.
Example
Retrigger review
Ask CodeAnt AI to review the PR again, by typing:
Check Your Repository Health
To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.