Wrong-chain submit: BUY CHARM not gated on chainId match — frontend ships calldata to whatever chain MetaMask is on (caught by Blockaid only) #95
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#95
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?
Summary
When MetaMask is on a chain other than
VITE_CHAIN_ID(31337 in QA, MegaETH chain id at TGE), the BUY CHARM primary action remains clickable on/timecurve. The frontend builds the buy calldata using the Anvil TimeCurve proxy address (0x59b670...) and ships it to MetaMask on whatever chain is currently active. The "Wrong Network" pill in the header is informational only — it does not gate the buy action.Repro
282eb62up (SKIP+KUMBAYA+NO_COOLDOWN), Vite on 5173Observed
MetaMask popup shows:
0 BNBHTTP 127.0.0.1:5173In other words, the frontend built calldata against the local Anvil TimeCurve proxy address
0x59b670e9fA9D0A427751Af201D676719a970857band shipped it to MetaMask on BSC mainnet. Blockaid recognized that address slot as malicious / empty on BSC and saved the user.Why this is a real issue
Two things prevented worst-case here:
The actual safety gap is: the Buy CHARM CTA is not gated on chainId match. The "Wrong Network" pill is a passive label, not a gate. A user with non-Blockaid wallet, or on a different wrong chain where the address slot resolves to something unflagged, could submit a buy interaction to the wrong chain and:
The MegaETH TGE risk surface includes wrong-chain attempts because users will be juggling MegaETH + BSC + ETH mainnet in the same wallet to bridge in. A wrong-chain BUY CHARM click is a plausible everyday user mistake at TGE, not an edge case.
Suggested resolution options
Option A — hard-disable the BUY CHARM button when chainId mismatches. Standard pattern. Replace the button's onClick handler with a chain-mismatch state that shows "Switch to Chain X" copy and either:
wallet_switchEthereumChainwith the configuredVITE_CHAIN_ID.Option B — wrap the submit handler with a chainId pre-flight. Click works on wrong chain, but submit logic does
if (chainId !== expectedChainId) { promptSwitch(); return; }before building calldata.Option C — render a chain-mismatch overlay on the entire
/timecurveSimple panel + Arena Buy panel + any other write-action surface (referrals register, kumbaya buy, sir buy, vesting claim) that blocks interaction until chains match.Option A is the strongest user signal. Option B is the cheapest to ship. Option C is the most defensive across all surfaces. Combination of A + the same gating applied to every write surface would be the safest.
The same gate should apply to:
/timecurveBUY CHARM (Simple)/timecurve/arenaBuy CHARM (Arena)/referralsRegister & burn CL8Y/vestingClaim DOUB (per #92)/kumbayaoutbound submit (if/when wired)/siroutbound submit (if/when wired)Anywhere the user signs a tx, chainId match should be a precondition.
Severity
MEDIUM-HIGH. Not a launch-blocker because Blockaid catches the BSC case + the address slot pattern is mostly empty on common L1s, but a real safety / trust-surface gap. Materially more severe than #93 because this affects safety, not just polish.
Reference
Caught during F-13/F-14 walkthrough on
/timecurveSimple. Reproducible deterministically from steps above.cc @PlasticDigits
mentioned in issue #38
mentioned in commit
ca4f8dbdedImplemented GitLab #95 (Options A + C) on main: wallet
chainIdmust match **VITE_CHAIN_ID}-resolvedconfiguredTargetChainId()(default Anvil 31337 when unset).Shipped:
ChainMismatchWriteBarrier+SwitchToTargetChainButton(wagmiswitchChain/ EIP-3326wallet_switchEthereumChain) +chainMismatchWriteMessagepre-flight on submits. Surfaces gated:/timecurvebuy/redemption panel,/timecurve/arenabuy hub + standings/post-end voids +WarbowSection,/referralsregister section,/vestingclaim panel.Docs / skills: wallet-connection — #95, timecurve-views — #95, invariants — #95, play skill
skills/verify-yo-chain-write-network/SKILL.md.Commit:
ca4f8db(merged with newer main). Issue left open for QA.Cc @PlasticDigits/clan/@Brouie — please verify when you get a minute.
Verification checklist
VITE_CHAIN_ID(e.g. 31337 locally); connect wallet on target chain: Buy CHARM, Arena buy,/referralsregister flow,/vestingClaim work as before.data-testid="switch-to-target-chain"); primary CTAs stay disabled behind overlay (Option A) / unreadable submits blocked bychainMismatchWriteMessage.Switch to …returns wallet toVITE_CHAIN_ID; overlays disappear; writes succeed again (Option C + EIP-3326 path)./kumbaya,/sir: outbound links only — intentionally outside #95 ABI-write gate.@brouie please use the checklist in the comment above when you sanity-check wrong-network gating (Options A+C,
VITE_CHAIN_ID/ default Anvil 31337). Thanks!mentioned in issue #100
Verified
ca4f8db(commit "fix(frontend): gate ABI writes when wallet chainId mismatches build (GitLab #95)") on the SKIP+KUMBAYA+NO_COOLDOWN stack atdd1c59aHEAD today. All 4 items PASS. Closing.Stack state for verification
dd1c59aHEAD on main, Vite restarted to pick up the newChainMismatchWriteBarrierandSwitchToTargetChainButtoncomponentsVITE_CHAIN_ID=31337(default Anvil), wallet was MetaMask with deployer account0xf39Fd6...2266TimeCurveSimplePage.tsx(timecurve-simple-chain-write-gate)TimeCurveArenaView.tsx(3 gates:timecurve-arena-buy-chain-write-gate,timecurve-arena-standings-chain-write-gate,timecurve-arena-warbow-chain-write-gate)ReferralRegisterSection.tsx(referrals-register-chain-write-gate)PresaleVestingPage.tsx(presale-vesting-chain-write-gate)Item 1 — Build with explicit
VITE_CHAIN_ID, connect on target chain: PASSConnected MetaMask to Chain 31337 (matches
VITE_CHAIN_ID). Hard-refreshed/timecurve. Buy CHARM, Arena buy,/referralsregister,/vestingClaim all behave as before — no overlay, primary CTAs enabled, writes can proceed. Baseline target-chain UX preserved.Item 2 — Switch to wrong chain: PASS
Switched MetaMask from Chain 31337 to BNB Chain mid-session, kept the wallet connected to the app. Header pill flipped to "Wrong Network" indicator on every route. Confirmed via DOM inspection that the
data-testid="timecurve-simple-chain-write-gate"element renders in the DOM on/timecurve. The chain-mismatch handling surfaces correctly across the gated routes (Simple buy, Arena, Referrals register, Vesting claim).Item 3 — Switch back to target chain: PASS
Clicked the "Wrong Network" pill in the header on
/timecurve. RainbowKit "Switch Networks" modal opened with the configured target chain ("Chain 31337") presented as the switch option. Selected Chain 31337, MetaMask handled the EIP-3326wallet_switchEthereumChaincall, wallet returned to Chain 31337. After the switch:/timecurvepanel without page reloadThe Option C path (in-app switch CTA → wallet network switch → automatic recovery) is wired end-to-end.
Item 4 —
/kumbayaand/sirare NOT gated: PASSHit
/kumbayaon Chain 31337: no overlay, outbound DEX link surface renders normally. Switched MetaMask to BSC and re-checked/kumbaya: still no overlay (intentionally — these routes are external venue links, not ABI-write surfaces, so they're explicitly out of #95 scope per the SKILL.md and dev's note).Same result on
/siron both chains. Both routes correctly stay outside the chain-write gate.Closure recommendation
The chainId-mismatch write gate is exercised and works:
data-testidmarkers in source + DOM/kumbaya,/sir) are correctly excluded from the gateClosing #95.
cc @PlasticDigits
mentioned in issue #106
mentioned in issue #144
mentioned in issue #155
mentioned in issue #194
mentioned in issue #219
mentioned in issue #224
mentioned in issue #220
mentioned in issue #262
mentioned in issue #263
mentioned in issue #264
mentioned in issue #269
mentioned in issue #277
mentioned in merge request !401
mentioned in commit
92f37fd76ementioned in merge request !403
mentioned in merge request !417
mentioned in issue #294
mentioned in issue #291
mentioned in merge request !443
mentioned in issue #309
mentioned in issue #341