Transfer Status: chain-switch UX breaks stepper; withdrawSubmit stalled until refresh; MegaETH wallet chip shows ETH #131
Labels
No labels
agent:implement
agent:ready
backend
bug
cannot-reproduce
confirmed
desktop
docs
documentation
duplicate
enhancement
feature
frontend
good first issue
help wanted
high-risk
in-review
invalid
mobile
needs-triage
P0-critical
P1-high
P2-medium
P3-low
qa
QA
question
ready
report
responsive
security
security-escalate
smart-contract
solana
tablet
test-pass
ux
wallet-issue
wallet:keplr
wallet:metamask
wallet:station
wallet:walletconnect
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
code/cl8y-bridge-monorepo#131
Loading…
Add table
Add a link
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
Several related UX/regression issues on Transfer Status (
/transfer/:xchainHashId) during hash submission when the connected EVM wallet is not on the destination chain, plus incorrect MegaETH branding in the header wallet chip.Symptoms (reported)
withdrawSubmittransaction is not sent to the wallet until a full page refresh. After refresh with the wallet already on the correct chain, submission works.Affected surfaces (code map)
packages/frontend/src/hooks/useAutoWithdrawSubmit.tspackages/frontend/src/pages/TransferStatusPage.tsxpackages/frontend/src/hooks/useMultiChainLookup.tspackages/frontend/src/hooks/useWithdrawSubmit.ts(wagmiwriteContractAsync)packages/frontend/src/components/ConnectWallet.tsxpackages/frontend/src/lib/megaethMainnet.ts(MEGAETH_MAINNET_CHAIN_ID = 4326)packages/frontend/public/chains/chainlist.json(icon: "/chains/mega.png")Technical analysis
A. Stepper “reset” during hash submission (high confidence)
getStepIndex()mapslifecycle === 'deposited'to step index 0 (Deposit row in the vertical stepper).currentStepIdxoverrides that to 1 (Submit Hash) when all of these hold:transfer.lifecycle === 'deposited'source != nulldest == null!lookupLoadingSee
TransferStatusPage.tsx(currentStepIdxuseMemo).Meanwhile,
useMultiChainLookupsetsloading: trueat the start of every lookup (lookup()), including poll ticks.The Transfer Status page runs
setInterval(() => lookup(hash), POLLING_INTERVAL)for all non-terminal lifecycles—includingdeposited—see pollinguseEffectwith comment referencing stuck transfer detection (#42).Default
POLLING_INTERVALis 10s (packages/frontend/src/utils/constants.ts), so roughly every 10 seconds:lookupLoadingbecomes true for the duration of the parallel RPC sweep.currentStepIdxfalls back togetStepIndex('deposited')→ 0.There is already an inline comment in
useMultiChainLookup(“Keep prior source/dest while refreshing…”) acknowledging stepper churn for Terra→Solana, butloadingstill toggles, so the Transfer Status step logic remains vulnerable.Hypothesis: This is primarily a step-index bug coupled to
lookupLoading, not the wallet failing to switch chains.B. Missing “switch chain” affordance (high confidence)
During
autoPhase === 'switching-chain',TransferStatusPageonly shows copy such as “Please approve the chain switch in your wallet.” There is no button wired toswitchChainAsync({ chainId: destChainId })as a fallback when:even though
switchChainAsyncis already imported on the page for other flows (e.g. broken-transfer fix).useAutoWithdrawSubmit.triggerSubmitdoes callswitchChainAsyncinternally, but there is no surfaced retry/switch control in that specific banner path except indirect flows (e.g. error → Retry Submit).C. Transaction not appearing until refresh (medium confidence — likely race / stalled async flow)
In
useAutoWithdrawSubmit.triggerSubmit:submittedRef.current = trueis set before attemptingswitchChainAsync+submitOnEvm.switchChainAsyncresolves,submitOnEvmimmediately calls wagmiwriteContractAsync.Plausible failure modes:
switchChainbeforeuseAccount().chain/ wallet connector state reflects the new chain.writeContractAsyncmay still target the prior chain or fail in ways that surface as null hash + error, leaving users to refresh sotriggerSubmitruns again under stable chain state.switchChainAsync: If the pop-up is ignored or never shown, the promise may remain pending; manual switching does not necessarily fulfill that promise → flow stuck until reload re-runs orchestration from a clean hook state.packages/frontend/src/lib/wagmi.tsregistersmegaEthChain) — e.g. network-add prompts vs silent failures.This deserves instrumentation + reproduction with MetaMask/Rabby on MegaETH ↔ other EVM routes.
D. MegaETH shows “ETH” instead of logo (high confidence)
ConnectWallet.tsx:getChainLogoPath(chainId)returns logo paths for BSC, opBNB, Anvil, Ethereum mainnet only, etc.MEGAETH_MAINNET_CHAIN_ID(4326) is not handled, sochainLogoPathisundefined.gasSymbolpill — andgetGasSymboldefaults unknown chains toETH.Meanwhile
chainlist.jsonalready declares/chains/mega.pngfor MegaETH — the header widget simply does not use it.Disconnected CONNECT EVM button always renders
/chains/ethereum-icon.pngregardless of last-used chain (minor vs connected-state bug).Proposed fixes (engineering direction)
1. Decouple stepper highlight from ephemeral
lookupLoadingOptions (pick one or combine):
lifecycle === 'deposited'and we already knowsource != null && dest == null, treat the active step as Submit Hash even during lookup refresh (use sticky “post-deposit phase” derived from last successful lookup snapshot, not transient loading).transfer.lifecycleplusautoPhase/withdrawSubmitTxHashintent flags rather thanlookupLoading.Goal: No flicker back to Deposit during periodic polls.
2. Add explicit chain-switch UI on Transfer Status
When destination is EVM and
evmChain.id !== destChainId:switchChainAsyncwith dest id fromBRIDGE_CHAINS.useSwitchChain().chains/ connector capabilities detection when chain isn’t added yet (surface explorer docs link).3. Harden post-switch submission
After successful
switchChainAsync:useAccount().chainId === destChainIdor timeout with actionable error + Retry.triggerSubmitso chain alignment is idempotent and user-triggerable from the banner.4. MegaETH branding in
ConnectWalletgetChainLogoPathfor4326→/chains/mega.png(align withchainlist.json).getGasSymbolif MegaETH native symbol must not display as ETH (confirm desired ticker — product/network docs).Acceptance criteria
Transfer Status / hash submission
lifecycle === 'deposited', deposit confirmed on source (source != null), and no pending withdraw on destination yet (dest == null), the stepper does not revert to highlighting Deposit merely because a background multi-chain lookup poll started (lookupLoading === true).withdrawSubmit, the UI presents a clear primary action (button) Switch to <chain> that invokes the configured wagmi/network switch for that destination — not only passive text asking to approve in the wallet.withdrawSubmitreliably prompts for signing without requiring a manual page refresh, across at least:MegaETH branding
/chains/mega.png(or equivalent approved asset), not theETHtext fallback — assumingmega.pngis present inpackages/frontend/public/chains/.Regression / QA
currentStepIdxstability acrosslookupLoadingtoggles given deposited + source present + dest absent.Reproduction notes (for QA)
/transfer/<xchainHashId>after deposit confirms (deposited).transfer.destChain.switching-chainwindow across ≥15–20s to intersectPOLLING_INTERVAL(default 10s).Related implementation hints
TransferStatusPage.tsx—currentStepIdxoverride gated on!lookupLoading.TransferStatusPage.tsx—setInterval(... lookup ...).useMultiChainLookup.ts—setResult(... loading: true ...).useAutoWithdrawSubmit.ts—triggerSubmit, phases'switching-chain' | submitting-hash'.megaethMainnet.ts.public/chains/chainlist.json→mega.png.Labels / priority suggestion
Source-level confirmation of items A and D on
27a5e42HEAD — your analysis matches the code exactly. Plus a draft failing-test scenario for the stepper flicker bug.Item D — MegaETH logo gap (high confidence — confirmed)
packages/frontend/src/components/ConnectWallet.tsx getChainLogoPath:No branch for
4326(MegaETH). Confirmed:ls public/chains/mega.png→ file exists.chainlist.jsonhas"id": "megaeth", "icon": "/chains/mega.png".Pure data-vs-code gap. One-line fix: add
if (chainId === 4326) return '/chains/mega.png'.Item A —
currentStepIdxflicker on poll tick (high confidence — confirmed)packages/frontend/src/pages/TransferStatusPage.tsx currentStepIdxuseMemo:When the
setInterval(lookup, POLLING_INTERVAL)poll runs (default 10s perutils/constants.ts),useMultiChainLookupflipsloading: truefor the duration of the parallel RPC sweep. The override condition fails,currentStepIdxfalls back togetStepIndex('deposited')→ 0, and the stepper visibly snaps from "Submit Hash" back to "Deposit". Then resolves back to 1 when the poll completes. Exactly the "reset" sensation reported.No
TransferStatusPage.test.tsxexists today — so this regression has no automated guard.Draft failing-test scenario (for whoever lands the fix)
If you extract
currentStepIdxlogic into a pure helper (e.g.,computeTransferStepIdx(args)), this test captures the bug deterministically:The middle assertion fails on
27a5e42HEAD per the override gate — that's the regression. Once the fix decouples step highlight fromlookupLoading(per your proposed direction 1), all three assertions pass.Happy to verify the eventual fix the same way I verified #127 / #128 today: source-level walkthrough, classifier/helper unit tests if applicable, full
npx vitest runregression check.cc @PlasticDigits
mentioned in commit
67a48e46d9Landed on
main(67a48e4) — GL-131Summary: Addressed transfer-status stepper flicker during multi-chain polling, added an explicit Switch to <destination> control for wrong-chain EVM hash submission, hardened post-switch flow (timeout + chain-id alignment before
withdrawSubmit), and fixed the MegaETH header wallet icon for chain 4326.@brouie — could you verify per the checklist below? Leaving the issue open until you sign off.
Verification checklist
/transfer/:xchainHashIdwithdeposited, source confirmed (sourcepresent), no dest pending yet (destnull), leave the page open ≥20s so a background lookup poll runs. The active step should stay on “Submit Hash”, not flash back to “Deposit”.withdrawSubmitshould prompt without a full page refresh (MetaMask and one other wallet, e.g. Rabby/WC target if applicable; include MegaETH as dest or src where relevant)./chains/mega.png(MegaETH branding), gas label remains ETH per chain config — see INV-UX3 indocs/FRONTEND_BRIDGE_INVARIANTS.md.Tests / docs
packages/frontend/src/utils/transferStatusStep.test.ts(computeTransferStepIdxstability acrosslookupLoading).INV-UX3—docs/FRONTEND_BRIDGE_INVARIANTS.md; agent cross-link —skills/agent-frontend-bridge-chains.md.mentioned in issue #130
mentioned in issue #127
mentioned in issue #128
Production deploy gap — sign-off blocked
Verified prod bundle on bridge.cl8y.com today: footer reads
v0.1.345 · 27a5e42. RepomainHEAD is currently at95f8fd5, which is the merge commit containing all four fixes (#127eaa3d0a, #128b6c5b5a, #130 Terra||clause drop, #13167a48e4). Prod is therefore still on the pre-fix bundle and none of the four fixes are live yet.Confirming repro on prod (proves bundle is pre-fix):
ETHtext fallback while MetaMask is on MegaETH (chain 4326). This is the clean #131-D pre-fix behavior —getChainLogoPathhas no branch for 4326, falls through togetGasSymboldefaultETH.Cannot run mainnet sign-off on the verification checklist for #127 / #128 / #130 / #131 until prod cuts to current HEAD (or whichever release commit contains the four fixes).
Will re-run the combined live walk on bridge.cl8y.com once deploy is confirmed:
Plus the 4 #123 blocked rows pick-up that rides on #128 / #130 fixes.
cc @PlasticDigits
mentioned in issue #123
@Brouie bridge.cl8y.com frontend is now deployed at latest commit.
queued in the bundled bridge live-walk along with #128 — see https://gitlab.com/PlasticDigits/cl8y-bridge-monorepo/-/work_items/128#note_3322750554 for timing.