Skip to content

Reconcile milestone status against real on-chain assertion state - #179

Open
JohnArayaE wants to merge 1 commit into
drydocs:mainfrom
JohnArayaE:fix/147-reconcile-milestone-status
Open

Reconcile milestone status against real on-chain assertion state#179
JohnArayaE wants to merge 1 commit into
drydocs:mainfrom
JohnArayaE:fix/147-reconcile-milestone-status

Conversation

@JohnArayaE

Copy link
Copy Markdown
Contributor

Summary

  • JobsContext's submit/dispute/vote/finalize actions set local milestone status from the assumed result of their own transaction, without ever reading get_assertion_state back — so status never advanced when someone else's action (or a quietly-expired challenge window) was what actually changed the assertion.
  • Every action now reconciles from a real get_assertion_state read after it runs, and any milestone with an assertionId that isn't settled (released/returned) polls that same read every 30s, plus a manual "Refresh" button in the UI.
  • Added a client-side, env-configurable VITE_CHALLENGE_WINDOW_SECS (default matches the canonical testnet deployment, 21600s) to show a "ready to finalize" hint from a real Assertion.opened_at read — the contract has no getter for its own configured window, so this is a hint only; finalize itself remains the real gate and rejects early calls.

Test plan

  • pnpm exec tsc -b — no errors
  • pnpm lint (oxlint) — 0 warnings, 0 errors

Closes #147

JobsContext previously set local milestone status from the assumed result of a transaction, without ever reading get_assertion_state back. Now every action (submit/dispute/vote/finalize) reconciles from a real read afterward, and any milestone with an assertionId that isn't settled gets polled every 30s so status advances even when another party's action or an expired challenge window is what changed it. Adds a client-side, env-configurable challenge-window hint for finalize eligibility, since the contract has no getter for it.

Closes drydocs#147
refreshMilestone(jobId, milestone.id, address);
}, POLL_INTERVAL_MS);
return () => clearInterval(id);
}, [address, assertionId, settled, jobId, milestone.id, refreshMilestone]);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

refreshMilestone's identity in JobsContext.tsx gets recreated on every jobs state change, so this effect's dependency array means any single milestone's poll tick or manual refresh resets the setInterval timer for every other actively-polling MilestoneRow on the page. With several submitted or disputed milestones open at once, if refreshes across them land more often than every POLL_INTERVAL_MS in aggregate, no individual milestone's timer ever survives a full interval uninterrupted, so its background reconciliation can be indefinitely postponed even though this looks like it guarantees a poll every interval. Consider stabilizing refreshMilestone with useCallback or useRef so one milestone's refresh doesn't reset another's timer.

* `Assertion.opened_at` read; it never gates the `finalize` call itself —
* the contract remains the source of truth and rejects it if called early.
*/
export const CHALLENGE_WINDOW_SECS: number = import.meta.env.VITE_CHALLENGE_WINDOW_SECS

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is parsed with a bare Number() and no NaN or negative check. If VITE_CHALLENGE_WINDOW_SECS is set to a non-numeric value, Number() yields NaN, and every readyToFinalize comparison against it is then permanently false, silently hiding the ready to finalize hint for the life of the deployment with no error surfaced anywhere. Please validate the parsed value and fall back to the default (or throw) on NaN.

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.

[Bug] Demo app never reconciles milestone status against real on-chain state

2 participants