TimeCurve frontend: sale-phase detection mis-reports 'pre-start' when contract is live #48
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#48
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?
Filed per dev request from #39 section C1 findings.
Summary
Frontend phase detection reports pre-start/waiting-on-chain state even when the contract is live (
saleStart > 0 && !ended). This gates Buy CHARM interaction and prevents manual UI-driven testing despite the chain being fully active.Observed
24:36:43renders correctly, but banner text says 'Pre-start / WAITING ON CHAIN / The sale has not started yet'Contradicting evidence (same session)
saleStart > 0confirmed on-chain (section B verification)ended/v1/timecurve/buys)Suspected cause
Either:
fed6cd2didn't fully landLaunchGateguard is conservatively blocking when it doesn't recognize stateEnvironment
SKIP_ANVIL_RICH_STATE=1(active sale + 18-bot swarm)0xc46b15f4B56489a16F561c22D5F0BA8bdCa80650)fed6cd2..eac3d5frange (three-view split #40 included)Impact
Screenshot
cc @PlasticDigits
mentioned in issue #49
mentioned in issue #39
mentioned in commit
41aaa92b7ementioned in commit
2b7dd2b8e3Fix merged (main @
41aaa92)Cause:
derivePhaseused walletlatestBlock.timestamp(and wall fallback) for “chain now,” while the hero countdown and related UX useuseTimecurveHeroTimer, which is indexer-anchored (/v1/timecurve/chain-timer) with a fixed wall–chain skew. If the JSON-RPC head lags (common on local Anvil / multi-rail setups), the UI could show a correct live countdown to the round deadline but still classify the session as pre-start (Buy CHARM stuck on COMING SOON, Loading sale state…, Waiting on chain).Change: Added
ledgerSecIntForPhase()intimeCurveSimplePhase.tsand useMath.floor(hero chainNowSec)when the hero timer has loaded; otherwise keep the previous block-based time. Applied inuseTimeCurveSaleSession(simple + shared phase) andTimeCurvePage(Arena).preStartCountdownSecin the session hook now uses the same phase clock so it cannot disagree with the phase strip.Tests: Vitest —
timeCurveSimplePhasetests (including new cases forledgerSecIntForPhase).@brouie — can you re-verify on your stack?
Checklist for verification
VITE_INDEXER_URLset so the hero timer can load (same as for Recent Buys / feed).saleStart > 0,!ended, before deadline): Simple/timecurveshows Live sale (or equivalent) and Buy CHARM is not stuck on COMING SOON / Loading sale state…./timecurve/arena(or segment route): state badge is not Starts soon / Waiting on chain when the round is live; banner matches “live” semantics.saleStart): phase still shows pre-launch and countdown to open behaves sensibly.endSale: Timer expired / settlement messaging still correct.If anything still mismatches, note whether the wallet RPC and indexer point at the same chain ID and base URL. Thanks.
mentioned in issue #40
mentioned in issue #42
@PlasticDigits verified on
41aaa92.Root cause resolution makes sense —
derivePhasewas using wallet-RPC block timestamp while the hero countdown uses indexer-anchored chainNowSec. On lagging RPC heads these diverged and phase reported pre-start despite the hero countdown correctly showing live remaining seconds.Verification
Code-level:
ledgerSecIntForPhasehelper intimeCurveSimplePhase.ts— prefersMath.floor(heroChainNowSec)when available, falls back to block-based timeuseTimeCurveSaleSession.tsL293 (Simple path + shared phase)TimeCurvePage.tsxL275 (Arena) with identical pattern at L277 passingheroChainNowSecpreStartCountdownSecin session hook also uses the same phase clock now so display and phase can't disagreeTests:
timeCurveSimplePhase.test.ts: 16/16 passing (up from 13 yesterday — +3 new forledgerSecIntForPhasecovering floor math, undefined fallback, equal-value case)Consistent fix across both Simple + Arena — matches the #40 invariant that these two share phase state via the same pure helper module.
Closing as verified.
mentioned in issue #44
mentioned in issue #113
mentioned in issue #114
mentioned in issue #115
marked this issue as related to #115
mentioned in issue #124
mentioned in issue #227
mentioned in issue #232
mentioned in issue #236
mentioned in issue #237