Transition Gap Analysis #342
Labels
No labels
agent:fix_bugfix
agent:fix_conflicts
agent:fix_security
agent:gap_analysis
agent:implement
agent:open_issues
agents
agent:security_audit
agent:verify
area
timecurve
arena
arena-v2
audit
block:feature_review
block:security
bug
contracts
design
devops
docs
documentation
enhancement
epic
feature
frontend
indexer
low priority
megaeth
operations
ops
performance
QA
qa checklist
referrals
removal
security
smart-contracts
tech-debt
testing
time-arena
timecurve
visual-qa
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
code/yieldomega#342
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Commonly smart contracts and ux break during state transitions, many of which exist in our dapp including level ups, epoch rolls, prize distrbutuin/claiming, leaderboard changes, and more. Check the behavior, testing, and documentation of state transitions both in contracts, frontend, and indexer, make sure they are properly documented and tested, and propose recommended fixes for gaps.
Gap Analysis — State Transitions (#342)
Issue: Transition Gap Analysis
Date: 2026-06-25
Branch:
main(synced)Report:
gaps/GAP_1782397039.mdExecutive summary
Yieldomega’s Arena v2 state machine is well-specified and heavily tested on-chain. Podium rolls, Last Buy epoch re-anchors, CRED claims, XP/level progression, WarBow settlement, and autoroll-on-buy are covered by ~120+ Forge tests, a rich invariant matrix (
INV-TIME-ARENA-*), and product docs (time-arena.md,arena-v2.md).The largest gaps sit at transition boundaries across layers, not in core happy-path contract logic:
chain_timer) can advance epochs/deadlines before log ingest catches up; the frontend polls indexer data withplaceholderDatathat latches stale snapshots. Users can see frozen00:00timers, old epoch numbers, or stale leaderboards during rollovers.rollPodiumEpochis documented in copy only; no CTA or settling indicator.GET /v1/arena/activityexcludes level-ups, CRED claims, podium epoch rolls, and feature unlocks.PodiumTimerArmedis emitted on-chain but not decoded.claimCredfireswriteContractAsyncwithout receipt wait or stats invalidation (unlikeclaimWarBowFlag).PodiumVaultspool wiring, pause behavior duringrollPodiumEpoch/claimCred, production MegaETH TWAP hard-reset path integrated withTimeArena.buy, and sparse/empty podium rolls.Severity distribution: 0 Critical · 5 High · 12 Medium · 8 Low · 4 Info (accepted/documented).
Test suite status (this run):
forge testdoub.csv, expected); 2 Warbow benchmark tests failed (timer not armed,level) — likely env/setup flake, not production regressionscargo testtypecheck+lint+npm testScope
Per issue #342: analyze behavior, testing, and documentation of state transitions across contracts, frontend, and indexer — including level ups, epoch rolls, prize distribution/claiming, leaderboard changes, sale phase changes, timer rollovers, pause boundaries, and reorg handling.
Transition inventory (current state)
startArena)TimeArenaArenaStarted/v1/arena/timers_reanchorEpochCharmPriceLastBuyEpochStarted,LastBuyEpochCharmAnchoredlast-buy-epoch-pricing_armPodiumTimer,_extendPodiumTimerPodiumTimerArmednot decodedrollPodiumEpoch,_autorollExpiredPodiumsPodiumEpochRolled/podiums, session-summaryclaimCredCredClaimedArenaCharmCredCard— no post-tx refresh_finishBuy+ArenaXpXpGained,LevelUplevel_history_finishBuygatesFeatureUnlockedWarbowPodiumFinalizedclaimWarBowFlag_updateTopThree, WarBow ranking/podiums,/buyssetPausedPausedSetpausedarenaStarttimestampderivePhaseFSMFindings by area
Contracts — HIGH
H-C1: Non-commingled
PodiumVaultspools untestedPodiumVaults.setActivePool/setSeedPool/setFuturePoolallow external pool addresses. All Forge tests use default commingled wiring (address(vaults)). Misconfigured production deploy could desync ledger vs ERC-20 balance, causing payout/roll failures or stranded DOUB.Files:
contracts/src/arena/PodiumVaults.solFix: Integration test with separate EOAs — full buy → roll → payout → tranche promotion cycle.
H-C2: Production MegaETH TWAP hard-reset path not integrated in
TimeArenatestsTimeArena._sampleCharmAnchor()branches onblock.chainid == 4326→ArenaCharmPriceTwap.compute. Anvil tests use spot oracle. TWAP is tested in isolation (ArenaCharmPriceTwap.t.sol) with optional fork smoke, but noTimeArena.buyhard-reset test on chain 4326.Risk: DOUB owed at epoch boundary could diverge between test and production paths.
Fix:
vm.chainId(4326)test with mocked TWAP or fork; assertlastBuyEpoch, anchor, anddoubOwedForBuy.Contracts — MEDIUM
M-C1: Pause boundary asymmetry undocumented and untested
rollPodiumEpoch,claimCred, andtopUpPodiumPoolsbypass_requireLive()/ pause. Pause tests (INV-TIME-ARENA-PAUSE-MUTATING) cover buy/WarBow/flag only.Fix: Document intended ops behavior in
pause-and-final-signoff.md; addtest_pause_allows_roll_and_claimortest_pause_blocks_rollper product decision.M-C2: Sparse/empty podium roll lightly exercised
No explicit tests for
poolBal == 0or zero-address winners during roll. Tranche promotion still runs.Fix:
test_roll_empty_pool_tranche_promotion,test_roll_partial_winners.M-C3: Same-tx multi-level unlock edge case
_finishBuyusesgateLevel = lvlAfterafter XP — a wallet with large XP could unlock L2–L5 timer arms in one buy. Staged tests exist; no single-buy L1→L4+ test.M-C4: UUPS upgrade smoke missing
Only auth tests for upgrades. No post-upgrade roll/claim/buy smoke.
M-C5: Warbow benchmark tests failing in CI run
TimeArenaWarbowBenchmark.t.sol:test_benchmark_warbow_10k_player_ranking(timer not armed),test_benchmark_warbow_steal_10k_player_ranking(level). Gas benchmarks, not correctness — but failures obscure CI signal.Contracts — LOW / INFO
rollSeedToActivestandalone path not isolated (#261).PlayCredonly 2 unit tests; no arena-integrated double-mint edge cases.ReferralRegistry.registerCodeduring live epoch roll — no fullTimeArenaintegration test.TimeArenaFork.t.sol) reads head state only.timer expiredreverts (#312).Documentation (contracts): Strong —
docs/product/time-arena.md,arena-v2.md,PARAMETERS.md,invariants-and-business-logic.md,docs/architecture/data-flows.md(podium roll flow).Frontend — HIGH
H-F1: No play-route UX for epoch roll / timer expiry
Timers count to
00:00with no settling or pending roll state. Copy inpodiumCopy.tsxexplains mechanics but there is no CTA, spinner, or “waiting for settlement” indicator. Users see frozen countdown until someone rolls on-chain and indexer catches up.Files:
useArenaHeroTimer.ts,ArenaTimerChips.tsx,podiumCopy.tsxH-F2: Transition visibility bounded by indexer poll lag
All display transitions are poll-driven (1–5s fast, backoff to 30s on failure). React Query
placeholderData: (prev) => prevkeeps old epoch/standings during refresh. Epoch bump, leaderboard swap, and claimable CRED appear late.Files: indexer query hooks across
usePodiumReads.ts,useArenaHeroTimer.ts,useArenaPlayerLevel.tsH-F3: Timer clock source split between hero and side chips
Hero uses wall-clock skew (
chainNowSecfrompolled_at_ms+block_timestamp_sec). Non–Last Buy chips use staticblock_timestamp_secat last poll — can disagree with hero between polls.Files:
ArenaTimerChips.tsx(L76–86),useTimerPodiumSlideMeta.ts(L77–82)Frontend — MEDIUM
M-F1: Play route lacks indexer-offline surfacing
useIndexerConnectivity/ offline banners exist on protocol page but notArenaSimplePage.tsx(/). During outage, play UI shows latched stale data with no degraded-mode banner (#96).M-F2:
claimCredhas no post-tx lifecycleArenaCharmCredCard.tsxcallswriteContractAsynconly — nowaitForWriteReceipt, noinvalidateArenaWalletStatsQueries. Contrast withsubmitClaimWarBowFlaginuseArenaSaleSession.ts.M-F3: No E2E for critical transitions
E2E covers mount/load and buy feel (
anvil-arena-05-play-feel.spec.ts,anvil-indexer-first.spec.ts). Missing: pre-launch→live flip, timer zero, CRED/flag claim refresh, competitor buy changing leaderboard rank.M-F4: Buy RPC event watch active under indexer-first
useWatchContractEvent(Buy)enabled regardless of indexer — triggers RPC refetches alongside polls; potential flicker under load.M-F5: WarBow finalize — read-only on play route
Operators use protocol/contract directly; no UI feedback when finalize completes.
Frontend — LOW
indexerFirstDisplay.test.tsis static source analysis — runtime RPC reintroduction not caught.Documentation (frontend):
docs/frontend/arena-views.mdcovers level-up celebration (#335), wallet profile (#336). No dedicated “transition states” UX spec.Unit test coverage: Good for phase FSM, timer display, level-up celebration logic, buy effect toasts, WYWA activity gate. Gap is runtime transition E2E, not pure-function coverage.
Indexer — HIGH
H-I1: Dual source of truth — head poller vs log ingest
/v1/arena/timersand/podiumsepochs come fromchain_timer(latest RPC Multicall3). Event tables advance only tochain_pointer. After epoch roll, UI can show new epoch/deadlines beforePodiumEpochRolled, live podium rows, or activity reflect the transition.Files:
chain_timer.rs,chain_timer_poll.rs,api_arena.rs,ingestion.rsH-I2: No unified transition feed
GET /v1/arena/activityunion SQL includes onlybuy,steal,guard,revenge. Excludeslevel_up,cred_claim,podium_epoch,epoch_started,feature_unlocked. Frontend cannot indexer-first drive celebration/toast stacks for non-buy transitions.Files:
api_arena.rs(ACTIVITY_UNION_SQL)Indexer — MEDIUM
M-I1: Logs not sorted by
log_indexbefore persistingestion.rsiterates RPC return order with nosort_by(log_index). Same-blockLastBuyEpochStartedafterBuycould mis-taglast_buy_epochon misordered RPC (#278 risk).M-I2:
PodiumTimerArmednot decodedEmitted in
TimeArena.sol; absent fromdecoder.rs/persist.rs. Timer “armed” transition invisible to API.M-I3: No confirmation tiers despite design doc
REORG_STRATEGY.mddefinesrealtime/soft/finaltiers; no API filter onblock_number <= head - N. Leaderboards and wallet stats serve pre-finality data on ~1s MegaETH blocks.M-I4: No automated reorg E2E
Tests call
rollback_afterdirectly; no Anvil reorg walk (find_common_ancestor+ re-ingest) script.M-I5: Ingest RPC side-effects can fail whole block
WarBow BP + live podium
eth_calls inside block transaction — transient RPC failure rolls back entire block including transition events.M-I6: Public
/v1/statushides sync lagchain_pointer/last_indexed_at_msomitted unless ops flag — clients cannot detect ingest lag.Indexer — LOW
FeatureUnlocked/PausedSetindexed but not API-listed historically.fetchArenaPrizeDistributionsreturnsnull).CredClaimed/LevelUp/PodiumEpochRolledend-to-end (verify scripts cover subsets).Integration tests:
integration_stage2.rs— 6 tests pass including reorg rollback for level history (#336), last-buy epoch assignment (#278), session summary podium finalize (#338). Gaps: log ordering, live reorg, transition HTTP surfaces.Verify scripts (transition-related):
verify-last-buy-epoch-anvil.shverify-podium-live-anvil.shverify-podium-timers-anvil.shverify-wallet-profile-anvil.shverify-vault-funding-anvil.shMissing:
verify-indexer-reorg-anvil.sh, transition activity feed smoke.Cross-cutting / architecture
Testing gaps summary
DRY, readability, best practices
ArenaXp,TimeMath,ArenaPodiumSettlement,ArenaBuyRouting). Frontend has some duplication between hero timer skew and chip timer sources (H-F3) — unifychainNowSec.claimCredshould mirror establishedclaimWarBowFlagreceipt+invalidation pattern.Recommended next steps (priority order)
P0 — Close cross-layer transition gaps
live,expired-pending-roll,settling,epoch-advanced,claimable. Map to indexer fields (podium_epochs,podium_prediction,claimable_cred_epoch).indexed_head_block(orindexed_through_block) to/v1/arena/timersand/podiums; frontend shows subtle sync indicator whenindexed_head_block < chain_head_block./v1/arena/activityor add/v1/arena/transitions— Includelevel_up,cred_claim,podium_epoch,epoch_started,feature_unlockedfor indexer-first celebration/toast stacks.P1 — Harden correctness at boundaries
(block_number, tx_index, log_index)inindexer/src/ingestion.rsbefore persist.PodiumTimerArmedin indexer if product needs armed-state UX beyond head poller.claimCredfrontend — receipt wait +invalidateArenaWalletStatsQueries(mirrorclaimWarBowFlag).chainNowSecinArenaTimerChips/useTimerPodiumSlideMeta.rollPodiumEpoch/claimCred/topUpPodiumPools.P2 — Test and ops coverage
YIELDOMEGA_E2E_INDEXER=1: warp Anvil past deadline, scripted roll, assert epoch increment + leaderboard refresh + claim button state.scripts/verify-indexer-reorg-anvil.sh— Anvil reset + re-ingest smoke.forge testsignal.P3 — Documentation
docs/frontend/arena-views.mdlinking on-chain events → indexer fields → UI states.docs/operations/pause-and-final-signoff.md.Severity summary
Verification commands (this analysis)
Generated by gap analysis workflow for GitLab #342. No MR opened per issue instructions.
All recommendations approved.
Pause should block roll.
mentioned in issue #343
mentioned in issue #344
mentioned in issue #345
mentioned in issue #346
mentioned in issue #347
mentioned in issue #348
mentioned in issue #349
mentioned in issue #351
mentioned in issue #352
mentioned in issue #353
mentioned in issue #354
mentioned in issue #355
mentioned in issue #350
Child issues opened from gap analysis
All P0–P3 recommendations were approved on 2026-06-25 (“All recommendations approved. Pause should block roll.”). No items were rejected, requested for revision, or left undecided. No prior child issues existed.
Opened (13 bundled issues)
Skipped
Not opened as separate issues (subsumed or out of P0–P17 scope)
Lower-severity findings (L-C*, L-F*, L-I*, M-C3/C4, M-I3/I5, INFO rows) remain documented in the gap report (
gaps/GAP_1782397039.md) and parent comment; they were not in the approved priority list unless covered by a bundle above.Suggested implementation order
No MR opened per parent instructions.
mentioned in merge request !517
mentioned in merge request !518
mentioned in merge request !519
mentioned in merge request !520
mentioned in merge request !521
mentioned in merge request !522
mentioned in merge request !525
mentioned in commit
a73b577518mentioned in merge request !526
mentioned in merge request !528