fix(frontend): WalletConnect stays on Connecting... (Galaxy Station, iPhone, desktop Chrome) #12
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
Live QA: tapping WalletConnect never finishes pairing. Reproduced with Galaxy Station, iPhone, and Chrome on PC. The Connect Wallet modal stays on Connecting... with Cancel; no QR, no Open Galaxy Station / Copy pairing link sheet, and no error until (or unless) the 90s timeout.
This is production-blocking for any voter who is not already in an injected extension / in-app browser. Terra Galaxy Station is WalletConnect-only in the Connect list (
WalletType.WALLETCONNECT). BSC WalletConnect is the same row label and can hang the same way.Related: #6 (wallet port, closed), #7 (live WC QA leftover). Do not treat this as “ops only” — the DEX WalletConnect mobile patch and production
VITE_WC_PROJECT_IDfail-closed were not carried into this repo.Skill:
skills/AGENTS_WALLET_CONNECTORS.md· DEX playbookAGENTS_FRONTEND_WALLETCONNECT_MOBILE.md(WC-M1–WC-M12).Current codebase
Voting copied DEX Terra + Bridge EVM connect UI, but several pieces that actually make WalletConnect complete a session are missing or weaker than DEX.
frontend/src/components/wallet/connectWalletOptions.tsWalletType.WALLETCONNECT. On mobile Chrome without an injected extension, Keplr / Station / Cosmostation also become WC rows.frontend/src/components/wallet/WalletModal.tsxfrontend/src/services/terraclassic/walletConnectPairingHook.tsglobalThis.__CL8Y_WC_PAIRING_MODAL__. On a validwc:URI and mobile client, closes Connect and opensWalletConnectPairingModal(Open / Copy / Cancel).frontend/src/main.tsxuseEffectafter first paint, not synchronously beforecreateRoot(DEX does the latter).@goblinhunt/cosmesQRCodeModalpatch-package(frontend-dapp/patches/@goblinhunt+cosmes+0.0.71-ghunt.22.patch) is what calls__CL8Y_WC_PAIRING_MODAL__.open. This repo has nofrontend/patches/, nopostinstallpatch-package. Vanilla mobileQRCodeModaldoeswindow.location.href = …from the asyncdisplay_uricallback — iOS/Android ignore that (not a user gesture). Desktop path is a canvas QR atz-index: 10000.frontend/src/services/terraclassic/wallet.tsGalaxyStationController(WC_PROJECT_ID)is WalletConnect v2. Comment says production vite requiresVITE_WC_PROJECT_ID(DEX #378 / M-10).frontend/vite.config.tsdoes not. Empty string is passed through.frontend/src/lib/wagmi.tswalletConnect({ showQrModal: true })is omitted entirely whenWC_PROJECT_IDis empty. Cancel on the Connect modal calls TerracancelConnectiononly — it does not abort wagmi WC.frontend/src/utils/walletConnectSession.tsWC-M9). User reports never get past Connecting, so they never reach a readable timeout/error in practice (or abandon before 90s).deploy/docker/frontend.DockerfileARG VITE_WC_PROJECT_ID=— notest -n. Coolify can ship a production image with WalletConnect rows that cannot pair.deploy/docker/frontend.security-headers.confconnect-src(O3): WalletConnecthttps/wsswildcards + a few hosts. Noframe-src(falls back todefault-src 'self'). Missing DEX-listedhttps://verify.walletconnect.com. No Station/LuncDash WC v1 bridgehttps://walletconnect.luncdash.com.img-srcis'self' data: blob:only.VITE_WC_PROJECT_ID.Galaxy Station (cosmes):
WalletConnectV2with Androidhttps://station.hexxagon.io/wcV2#Intent;…scheme=galaxystationand iOShttps://station.hexxagon.io/wcV2. Those hrefs are only offered after the pairing hook runs. Without the patch, iPhone never sees Open Galaxy Station.Observed UI (live Connect modal): Terra rows including Galaxy Station with WalletConnect badges, BSC WalletConnect + Coinbase, status Connecting..., Cancel. Pairing portal (
data-testid="walletconnect-pairing-modal") is not shown.Why this is needed
Holders on mobile (Galaxy Station, Keplr, Station, Cosmostation, LuncDash) and desktop users without an extension cannot register or vote. Identity v1 is one address; there is no fallback that silently uses another chain. BSC WalletConnect is core electorate (#4), not optional.
DEX already solved this (issues #519 / #554 / #566, WC-M1–WC-M12). Voting #6 required porting that stack, not a greenfield WC kit. The pairing helpers landed; the cosmes intercept that drives them, the boot-order, and the production project-id / CSP did not. That is why Galaxy + iPhone + Chrome PC all look like a hung Connecting... state.
Constraints / guardrails
@walletconnect/modalas a new Terra pairing UI (WC-M2 / wallet skill). Cosmes owns Terra QR; the dApp owns the mobile Open/Copy sheet.VITE_*BSC RPC in the browser (O4).wc:URI viaCopyButton; scheme allowlist; in-app browser remains a valid alternate path (WC-M7); pairing is foreground UI (WC-M8); bounded connect + cancel ignores a late session (WC-M9); Android Galaxyhttps…#Intent→intent://(WC-M11).connect-src https:or blanketframe-src *. Add only the hosts WalletConnect / Reown / Coinbase / Terra LCD-RPC actually need. Keepframe-ancestors 'none'.isAllowedWalletConnectDeepLink) must keep rejectingjavascript:,data:, and arbitraryhttps://. Do not open untrusted URLs from the WC payload.VITE_WC_PROJECT_IDis missing (match DEXvite.config.ts). Dockerfiletest -n "$VITE_WC_PROJECT_ID". Do not commit the secret.https://vote.cl8y.commust be allowlisted / verified on the same project id baked into the image. An unverified domain can hang or phishing-warn; that is ops, but it is required to close this issue.Relevant files
Voting (implement here)
frontend/src/main.tsxfrontend/src/services/terraclassic/walletConnectPairingHook.tsfrontend/src/services/terraclassic/wallet.tsfrontend/src/utils/walletConnectPairing.tsfrontend/src/utils/walletConnectSession.tsfrontend/src/components/wallet/WalletModal.tsxfrontend/src/components/wallet/WalletConnectPairingModal.tsxfrontend/src/components/wallet/connectWalletOptions.tsfrontend/src/hooks/useWallet.tsfrontend/src/stores/evmWallet.tsfrontend/src/lib/wagmi.tsfrontend/src/utils/prodEnvGuards.ts·frontend/vite.config.tsfrontend/package.json(postinstall / patch-package)deploy/docker/frontend.Dockerfiledeploy/docker/frontend.security-headers.confdeploy/coolify.env.example·frontend/.env.examplefrontend/src/utils/__tests__/walletConnectPairing.test.ts·frontend/src/utils/__tests__/prodEnvGuards.test.tsdocs/FRONTEND.md·docs/OPS.md§3 ·skills/AGENTS_WALLET_CONNECTORS.mdCopy from (do not implement voting in those repos)
cl8y-dex-terraclassic/frontend-dapp/patches/@goblinhunt+cosmes+0.0.71-ghunt.22.patch(QRCodeModal hook; keep other patch hunks only if voting still needs them)cl8y-dex-terraclassic/frontend-dapp/src/main.tsx(installWalletConnectPairingHook()beforecreateRoot)cl8y-dex-terraclassic/frontend-dapp/vite.config.ts(VITE_WC_PROJECT_IDproduction guard)cl8y-dex-terraclassic/frontend-dapp/src/services/terraclassic/__tests__/cosmesPatch127.test.tscl8y-dex-terraclassic/frontend-dapp/src/services/terraclassic/__tests__/walletConnectPairingHook.test.tscl8y-bridge-monorepo/packages/frontend/src/lib/wagmi.ts(EVM WCshowQrModal, metadata URL = voting origin)Recommended direction
vote.cl8y.com. DevTools: CSPconnect-src/frame-srcviolations; emptyimport.meta.env.VITE_WC_PROJECT_ID; whether cosmesQRCodeModal.openever runs; whether__CL8Y_WC_PAIRING_MODAL__exists at tap time.patch-package+postinstallon the same@goblinhunt/cosmesversion. Add a test thatnode_modules/.../QRCodeModal.jscontains__CL8Y_WC_PAIRING_MODAL__and does not auto-location.hrefon mobile.createRoot(module scope), so auto-reconnect and the first tap cannot raceuseEffect.VITE_WC_PROJECT_IDinprodEnvGuards/ vite production / Dockerfile. Keep WC rows from appearing in production unless the id is baked in. Coolify must pass the build-arg; WalletConnect Cloud must listhttps://vote.cl8y.com.frame-srcfor the Bridge-style EVM QR modal (verify.walletconnect.com/ org as required by the installed wagmi WC version). Do not openconnect-src https:. Confirm desktop Terra QR still works if it is canvas/blob:(already inimg-src).showQrModalis kept, it must actually paint above the Connect list (or hide the list while connecting). Cancel must abort wagmi WalletConnect, not only Terracontroller.connect().wc:) + Copy pairing link. Android must useintent://, nothttps://…#Intent(already unit-tested; unreachable until the sheet opens).false(not mobile), cosmes Scan via Galaxy Station QR must be visible (WC-M2). Connect list must not sit on top of it (WC-M8). Same for Keplr/Cosmostation WC if those rows are used on a narrow window classified as mobile.Acceptance criteria
terra1…in the header.intent://…scheme=galaxystation, not a Hexxagon website.eip155:56. Wrong chain remains a readable error. Cancel clears EVMconnecting.isConnecting/ EVM connecting; a late session does not attach (WC-M9).npm run build/ frontend image fails withoutVITE_WC_PROJECT_ID. Hatch / mnemonic /VITE_*BSC RPC still refused (O3/O4).https:. Pairing hrefs still rejectjavascript:/data:/ random https.Test plan (all paths)
Unit / CI
cd frontend && npm test— existingwalletConnectPairing.test.ts+ new hook / modal / patch /prodEnvGuardsWC id tests.cd frontend && npm run test:e2e— 5 Playwright workers; Simulated Wallet / hatch only. Do not treat this as live WC QA.npm ci, assert cosmesQRCodeModal.jscontains__CL8Y_WC_PAIRING_MODAL__(CI must runpostinstall).VITE_WC_PROJECT_IDfails; with a dummy id + no hatch/RPC succeeds (id format not a secret leak in logs).Terra WalletConnect — mobile (real devices)
intent://; Keplr / Station / Cosmostation / LuncDash Open+Copy each complete once.Terra WalletConnect — desktop
EVM
0x…on chain 56.Cancel / timeout / Legal
Deploy
VITE_WC_PROJECT_ID. Confirmvote.cl8y.comis on that WalletConnect Cloud project.wss://relay.walletconnect.com(or current relay) on a Galaxy connect attempt.Test plan (attack, hack, abuse)
urior android/ios templates that arejavascript:,data:,https://evil.example/…must not becomehref. Allowlist tests stay green. Hexxagon / Station hosts stay exact prefixes, notstation.hexxagon.io.evil.#Intent/ extrascheme=must not navigate to an arbitrary package. Prefer failing closed toCopy pairing link.wc:URI only, not HTML. Do not write attacker URLs to the clipboard.connect-src https:orframe-src *is a regression. EVM iframe (if any) is allowlisted hosts only.frame-ancestors 'none'remains.vote.cl8y.com(phishing / “deceptive request” warnings).controller.connect()/ wagmi session ignored.frame-ancestors. Pairing sheet is same-origin dApp chrome.url/iconsmust behttps://vote.cl8y.com(or the current origin), not an attacker site.redirect_urirules unchanged.Verification criteria
https://vote.cl8y.comwithout waiting on Connecting... and without editing the URL.VITE_WC_PROJECT_IDis set and the Cloud project includesvote.cl8y.com.git grep/ CI: cosmes patch applied;prodEnvGuardsrejects missing WC id; CSP file has noconnect-src https:.Bundle
marked as related to #6
marked as related to #7
mentioned in merge request !21
mentioned in commit
138cdbe637Merged in !21 onto
main(after the GitLab auto-branch was empty at5b86f64; the real commits were onfix/12-walletconnect-pairingand were fast-forwarded onto the MR source).In-tree AC: cosmes
QRCodeModalpatch +postinstall, pairing hook beforecreateRoot, productionVITE_WC_PROJECT_IDfail-closed, CSPframe-src/ WCimg-src/ LuncDash bridge on both the Dockerfile headers anddeploy/coolify-frontend.nginx.conf, EVM Cancel aborts wagmi WC (WC-M9). Unit coverage for hook, patch, prod guard, Galaxyintent://.Do not close. Remaining:
VITE_WC_PROJECT_IDand WalletConnect Cloud originhttps://vote.cl8y.comframe-srcintent://…scheme=galaxystation, BSC WC on chain 56Related post-merge deploy checklist will be filed separately.
mentioned in issue #15
marked as related to #15
mentioned in issue #7
mentioned in issue #16
Verify pass (2026-09-01) — in-tree only; this issue stays open
Pushed
d7f4d51tomain.In-tree (pass)
QRCodeModalpatch +postinstall; pairing hook beforecreateRoot; productionVITE_WC_PROJECT_IDfail-closed.GET /voteCSP hasframe-srcWalletConnect hosts,connect-srcincludeshttps://api.web3modal.org, noconnect-src https:,X-Frame-Options: DENY.frontend/patches/(Secp256k1PubKeytype name was a false generic-api-key).Live (not done here)
intent://…scheme=galaxystation, BSC WalletConnect on chain 56.VITE_WC_PROJECT_IDand Cloud originhttps://vote.cl8y.com(ops).Checklist
intent://unit coveragehttps:;/voteis 200terra1…in headerintent://…scheme=galaxystationVITE_WC_PROJECT_IDset; Cloud project listsvote.cl8y.comStays open: Connecting... on real Galaxy/iPhone/desktop Chrome was not re-run. Do not tick issue 7’s Cosmos WC box from Playwright.
Closing on in-tree pairing + live CSP
Cosmes patch, hook before
createRoot, productionVITE_WC_PROJECT_IDfail-closed, EVM Cancel, and livevote.cl8y.comCSP (frame-srcWalletConnect,api.web3modal.org, no blankethttps:) are verified.Human Galaxy Station / iPhone / desktop Chrome / BSC WalletConnect sessions were not re-run. That leftover stays on issue 7 live wallet QA. Playwright hatch is not that QA.