Skip to content

Fix: 4 issues (#1721, #1722, #1723, #1725) - #1846

Merged
yahia008 merged 2 commits into
StellarCheckMate:mainfrom
Manuel1234477:simple/1721-1722-1723-1725
Oct 19, 2026
Merged

yahia008 merged 2 commits into
StellarCheckMate:mainfrom
Manuel1234477:simple/1721-1722-1723-1725

Conversation

@Manuel1234477

Copy link
Copy Markdown
Contributor

Closes #1721
Closes #1722
Closes #1723
Closes #1725

Issues

#1721 Frontend: match status polling uses setInterval and never clears it on unmount

Category: Frontend - Bug
Priority: Low
Estimated Time: 20 minutes

Description:
The match status hook (or component) starts a setInterval for polling but does not return a cleanup function, causing the interval to continue running after the component unmounts and leading to React state updates on unmounted components.

Tasks:

  • Return a cleanup that calls clearInterval from the useEffect
  • Add a test asserting no state updates occur after unmount

#1722 Test: set_minimum_stake / set_maximum_stake enforce bounds on create_match

Category: Escrow Contract - Testing
Priority: Low
Estimated Time: 30 minutes

Description:
The min and max stake admin setters exist but there are no tests asserting create_match rejects stakes outside the configured range.

Tasks:

  • Test: stake below minimum returns Error::InvalidAmount
  • Test: stake above maximum returns Error::InvalidAmount
  • Test: stake exactly at boundaries is accepted

#1723 Performance: event indexer rebuilds the full match state from events on every /matches/:id request

Category: Event Indexer - Performance
Priority: Medium
Estimated Time: 2 hours

Description:
The /matches/:id handler replays all events for the match to reconstruct the current state on every request. This is O(events) per request and grows unbounded.

Tasks:

  • Add a matches table that stores the latest known state for each match
  • Update it incrementally on event ingestion
  • Serve /matches/:id from the materialized table
  • Add a test

#1725 Performance: oracle service pollers do not batch active-match queries, causing N RPC calls per cycle

Category: Oracle Service - Performance
Priority: Medium
Estimated Time: 2 hours

Description:
The Lichess and Chess.com pollers fetch game status one-by-one for each active match. With many concurrent matches this can exhaust the platform's rate limit and slow the polling cycle.

Tasks:

  • Group active match game IDs by platform and use the batch game-status endpoints where available
  • Add back-pressure when the batch response indicates rate limiting
  • Add a test

Summary

The four issues are independent and can be implemented in parallel. For #1721, the existing hook already has a cleanup in the visible portion; verify the full file and ensure the mounted-ref guard and interval cleanup are both correct, then add the unmount tests. For #1722, reuse the existing setup() fixture and admin setters; assert exact Error::InvalidAmount variants. For #1723, the materialized table must be populated by the ingestion path, not lazily on read, and the read path must not fall back to event replay; keep the existing API response shape and cache invalidation. For #1725, the default trait method keeps backward compatibility for any other providers; batch sizes should respect the existing rate limiter and concurrency semaphore, and rate-limit responses must not be retried in a tight loop.

Changes

  • Fix the match status polling hook so the polling setInterval is always cleared on unmount. Ensure the useEffect that sets up polling returns a cleanup function that calls clearInterval(intervalRef.current) and nulls the ref, and that the mounted ref is set to false on unmount so in-flight fetchMatch promises cannot call setState after unmount. Also make sure a terminal-state poll clears the interval exactly once and does not leave a dangling timer. (frontend/src/hooks/useMatchStatus.ts)
  • Add tests for the polling cleanup: (1) unmounting the hook stops further getMatchFn calls when timers are advanced, and (2) resolving a pending getMatchFn promise after unmount does not update result.current (loading/match/error remain at their pre-unmount values). Use fake timers and renderHook/unmount as in the existing cleanup tests. (frontend/src/hooks/useMatchStatus.test.ts)
  • Add escrow contract tests for stake bound enforcement on create_match. Using the admin setters set_minimum_stake and set_maximum_stake, configure a known min/max, then assert: a stake below the minimum returns Err(Ok(Error::InvalidAmount)); a stake above the maximum returns Err(Ok(Error::InvalidAmount)); and stakes exactly equal to the minimum and exactly equal to the maximum are accepted (return a match id). Cover the case where only one bound is configured and the case where both are configured. (contracts/escrow/src/tests/match_validation.rs)
  • Add a matches materialized-state table to the indexer schema (created in the same schema-initialization path as the existing events/leader_state tables), with columns for match id (primary key), player1, player2, status, winner, stake_amount, token, game_id, platform, created_ledger, completed_ledger, and updated_at. Add an upsert method (e.g. upsert_match_state) that writes the latest known state for a match, and change build_match_info / get_match_by_id to read the materialized row instead of replaying all events. Keep the event log for history but stop using it to reconstruct current state on the read path. (services/event-indexer/src/db.rs)
  • Add a MatchStateRow (or similar) model representing a row of the new matches materialized table, with fields matching the table columns, so the db layer can map rows to MatchInfo without replaying events. (services/event-indexer/src/models.rs)
  • Update the event ingestion path so that after each event is inserted, the materialized matches row for that match is updated incrementally (upsert with the event's status/winner/player/stake/token/game_id/platform/ledger fields). This keeps the table current without a full replay. Ensure the update happens in the same transaction or immediately after the idempotent insert so replays remain safe. (services/event-indexer/src/main.rs)
  • Change the GET /matches/:id handler to serve the match from the materialized matches table via the new db method instead of reconstructing state from events. Preserve the existing 404/500 response shapes and cache invalidation behavior. (services/event-indexer/src/api.rs)
  • Add a test verifying that GET /matches/:id reads from the materialized matches table: insert a match state row directly, request the endpoint, and assert the returned MatchInfo reflects the materialized row (and that it does not depend on replaying events). Also keep the existing not-found and validation tests passing. (services/event-indexer/tests/get_match_endpoint_tests.rs)
  • Add a batch game-status method to the GameProvider trait (e.g. fetch_results_batch(&self, game_ids: &[String]) -> Vec<(String, Result<Winner, ProviderError>)> with a default implementation that loops over fetch_result, so existing providers keep working). Update ProviderRegistry to expose a batch fetch that groups by provider and preserves per-game error reporting. (oracle-service/src/oracle/provider.rs)
  • Implement fetch_results_batch for the Lichess client using the batch game-status endpoint (e.g. /api/games/status?ids=... or the export endpoint with multiple ids), chunking game IDs into bounded batches, acquiring one rate-limit token and one concurrency permit per batch request, and mapping per-game results/errors back to the input IDs. On a 429/rate-limit response, surface a rate-limited error so the poller can apply back-pressure. (oracle-service/src/oracle/lichess_client.rs)
  • Implement fetch_results_batch for the Chess.com client using its batch game-status endpoint where available, chunking IDs into bounded batches, acquiring one rate-limit token and one concurrency permit per batch request, and mapping per-game results/errors back to the input IDs. On a 429/rate-limit response, surface a rate-limited error so the poller can apply back-pressure. (oracle-service/src/oracle/chess_com_client.rs)
  • Refactor the poller's verification tick to group due active matches by platform and call the batch fetch once per platform group instead of one RPC per match. Handle batch-level rate-limit errors by applying back-pressure (e.g. skip the rest of the cycle or delay the next tick) and reschedule the affected entries for retry; keep per-game permanent/transient error classification unchanged. (oracle-service/src/poller.rs)
  • Add a test covering batched polling and back-pressure: with multiple active matches on the same platform, assert the poller issues one batch request per platform group rather than N individual requests, and that a rate-limited batch response causes the poller to back off and retry the affected matches on a later cycle. (oracle-service/tests/rate_limiting.rs)

@yahia008
yahia008 merged commit 2e2b383 into StellarCheckMate:main Oct 19, 2026
8 of 16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants