follow-up(!15): EVM account= continuity — matching path, WC mismatch, deeplink proof #16

Closed
opened 2026-08-31 04:12:24 +00:00 by PlasticDigits · 9 comments
PlasticDigits commented 2026-08-31 04:12:24 +00:00 (Migrated from gitlab.com)

Summary

Integrator report after @plasticdigits/cl8y-clickwrap 0.1.1: voting Accept now puts account=0x… on /sign/evm. Terra Classic already bound that query (assertAccountContinuity). The report assumed EVM still signed whatever wallet connected.

That bind is already on main and in the current terms.cl8y.com bundle (GitLab #15 / !33): getClaimedAccount() → assertEvmAccountContinuity on injected and WalletConnect, mismatch copy This page is for a different wallet, Sign as 0x… as a text node. Do not reimplement the helper.

What is still missing is proof of the path voting actually uses (matching account= + same wallet → accept) and WC / deeplink / hostile-query coverage. Mismatch e2e exists; the happy path does not. A WalletConnect session for a different address is only covered on the injected mock.

Current codebase

Piece Behavior today
Query web/src/query.ts getClaimedAccount() reads account, trim, empty → null. Never a redirect target.
EVM page web/src/pages/evm.ts shows Sign as ${claimedAccount} via el() text nodes; passes claimedAccount into signEvmMessage.
Bind web/src/evm/account.ts canonicalizeEvmAddress = lowercase 0x + 40 hex (lockstep API normalize_account EVM). Mismatch or invalid claimed → EVM_ACCOUNT_MISMATCH. No claim → pass through connected address.
Injected sign web/src/evm/sign.ts assert before prepare / personal_sign.
WalletConnect web/src/evm/walletConnect.ts assert on session address (and e2e hook) before personal_sign.
Deeplink web/src/evm/deeplink.ts encodes current portal origin+path+search (so account= stays). Never encode query redirect_uri as the Open-in-app target.
API api/src/verify/evm.rs + api/src/account.rs: recovered signer must match submitted account_id. Query account= is portal UX; the API does not see it.
SDK TermsGate Accept + buildSignUrl({ account }) in @plasticdigits/cl8y-clickwrap 0.1.1 (GitLab npm).
Terra (reference) web/src/terra/chain.ts assertAccountContinuity; e2e mismatch in web/e2e/terra-sign.spec.ts.

Tests that exist

Tests that do not exist

  • E2E: account= equals connected wallet → Accepted + signed_latest for that address (the voting Accept path).
  • E2E / unit: WalletConnect mock + different claimed account → no submit.
  • E2E: Open in MetaMask / Binance / Copy link hrefs include account= when it is on the page.
  • Unit: WC connectAndSignEvmWalletConnect mismatch ( web/src/evm/walletConnect.test.ts only gates project id / e2e hook).
  • Hostile account= (javascript:, terra1…, too short) → fail closed, not used as href.

Skills already mention invariant 13 in skills/portal-sign-disclosure/SKILL.md. Keep them in lockstep.

Why this follow-up is needed

Voting (and other integrators) now send account= on Accept, not only on Open-in-MetaMask hints. If the matching path regresses, users get a false Accepted for a different 0x… than the dApp polls, then return still unsigned — or, worse, a future change skips the assert and records the wrong account’s terms while the UI still says Sign as <claimed>.

Mismatch-only tests do not prove the 0.1.1 integrator contract. WC is a second signer path that can skip the injected assert if someone wires prepare without assertEvmAccountContinuity.

Constraints / guardrails

  1. Do not reimplement assertEvmAccountContinuity / a second checksum scheme. Extend tests and any thin wiring holes only.
  2. Portal bind is UX. API EIP-191 recover (verify/evm.rs) remains source of truth. Do not trust query account in POST /signatures/wallet.
  3. account is not a URL. Never pass it to location, <a href>, WalletConnect pairing, or Open-in-app targets except as an existing query key on the portal sign URL.
  4. Text nodes only for Sign as … (el() / textContent). No innerHTML of query values.
  5. Checksum: compare lowercase 0x+40 hex only. EIP-55 vs lower must succeed; mixed/invalid claimed must fail closed (same message is OK).
  6. No claim → current behavior: sign whatever valid address the wallet returns (then API binds that account_id).
  7. Do not wallet_switchEthereumChain to Ethereum mainnet as part of this work (personal_sign is chain-agnostic).
  8. Do not weaken redirect allowlisting or encode redirect_uri into MetaMask/Binance deeplinks.
  9. Out of scope: Solana /sign/solana still ignores account= (postponed with #2 / #4). Telegram uses Telegram ids, not EVM 0x. Do not expand this issue to those pages.
  10. Playwright: Chromium, workers = 5, mock wallets only. No real MetaMask.

Relevant files

  1. Add Playwright matching case: install mock wallet, goto /sign/evm?property=cl8y.com&account=<mockChecksumOrLower>, consent, accept → heading Accepted, GET .../status?...&account=<lower> signed_latest: true. Assert Sign as is visible. Use the same address the mock will connect (EIP-55 on the query is a plus).
  2. Add Playwright WC mismatch: WC mock + account= other 0x → different wallet, no Accepted, claimed address remains unsigned.
  3. Extend missing-provider (or a dedicated) e2e: page URL includes account=0x…; Open in MetaMask href contains that account; Open in Binance url= contains it; Copy link is the portal URL (not redirect_uri, not javascript:).
  4. Unit: WC hook/prepare path throws EVM_ACCOUNT_MISMATCH and does not call personal_sign. Invalid claimed (javascript:alert(1), terra1…, 0x123) throws; Sign as still text-only if rendered.
  5. Keep skills/testing-coverage/SKILL.md row for evm-sign.spec.ts updated (matching + WC mismatch + deeplink account=).
  6. Optional ops checkbox: spot-check production /sign/evm?property=…&account=0x… still shows Sign as and refuses a different injected account (bundle already contains the mismatch string).

Acceptance criteria

  • Matching account= + same injected mock → accept records that account, not a sibling provider.
  • Matching checksum vs lowercase claimed vs connected still succeeds.
  • Mismatch injected (already present) stays green.
  • Mismatch WalletConnect does not submit and does not show Accepted.
  • Open in MetaMask / Binance / Copy link preserve account= from the current portal search; they never use account as an href target.
  • Hostile account query is not executed / not used as navigation; bind still fail-closed.
  • No claim (account absent) still signs the connected wallet (existing tests).
  • API verify unchanged; no new trust of query account on POST.
  • Skills/test map mention the new cases.
  • cd web && npx vitest run src/evm src/query.test.ts and npm run test:e2e -- evm-sign (Playwright workers=5) green.

Test plan (functional paths)

  1. Match (lower): account= mock lowercase → consent → Connect & sign → Accepted → status true for that account.
  2. Match (EIP-55): query checksum, wallet returns lower (or vice versa) → same.
  3. Mismatch injected: existing e2e (0xaaa… vs mock) → different wallet, claimed unsigned, mock account also not newly signed for the claimed key.
  4. Mismatch WC: WC mock account ≠ query account → fail closed; CTA re-enabled (signShell finally busy reset).
  5. Already signed + match: status already true for claimed → success without forcing a second personal_sign (same as unsigned-already-signed today).
  6. No account: existing happy paths (injected, EIP-6963, BinanceChain, late inject, WC) unchanged.
  7. Deeplink: with account + redirect_uri on the page, MetaMask/Binance/Copy keep account and do not navigate to redirect_uri.
  8. Multi-provider pick: two EIP-6963; claimed = MetaMask mock; picking Binance mock with a different address → mismatch; picking MetaMask → success.

Test plan (attack / abuse)

  1. account=javascript:alert(1) / data: / https://evil: must not become <a href> or location. Bind fail-closed. Sign as is a text node if shown.
  2. account= as open redirect: Open-in-app and success redirect still use portal URL / allowlisted redirect_uri only (web/src/redirect.ts). account must not be fed to safeRedirectUri.
  3. Query tamper: user strips account= → can sign a different wallet and see Accepted; integrator status poll for the original address stays unsigned (fail-closed for the dApp). Do not “fix” this by authenticating the query param.
  4. Query swap: user replaces account= with another 0x they control → they may accept for that address; dApp still polls the connected store address. Document; no server-side “claimed query” ACL.
  5. Hidden second EIP-6963: must not personal_sign a non-selected provider when claimed matches the hidden one.
  6. WC session account ≠ claimed: no personal_sign / no POST.
  7. accountsChanged mid-sign: if the wallet signs a different key than account_id in the message, API recover must reject. Do not add a portal-only bypass.
  8. XSS in Sign as: payload in account appears as text, not DOM.
  9. Cross-network claim: account=terra1… on /sign/evm → invalid EVM address → mismatch, no submit.
  10. Do not treat UI consent checkbox as proof of the claimed account.

Verification criteria

cd web && npx vitest run src/evm src/query.test.ts
cd web && npm run test:e2e -- evm-sign   # playwright.config.ts workers=5
  • New e2e names are obvious in the report (match, WC mismatch, deeplink account=).
  • Production spot-check (optional ops): https://terms.cl8y.com/sign/evm?property=<registered>&account=0x… shows Sign as 0x…; connecting a different injected account does not Accepted.
  • Integrator: after 0.1.1 Accept from voting, portal URL includes account= and Sign as matches the connected voting address.

Out of scope

  • Re-doing #15 mobile discovery / Open in MetaMask / in-page WC.
  • Solana / Telegram sign-page account= bind (#2 / #4).
  • Changing EIP-191 verify or message bytes (#6).
  • Real device MetaMask in CI.
## Summary Integrator report after `@plasticdigits/cl8y-clickwrap` **0.1.1**: voting **Accept** now puts `account=0x…` on `/sign/evm`. Terra Classic already bound that query (`assertAccountContinuity`). The report assumed EVM still signed whatever wallet connected. **That bind is already on `main` and in the current `terms.cl8y.com` bundle** (GitLab [#15](https://gitlab.com/plasticdigits/cl8y-ecosystem-legal/-/issues/15) / !33): `getClaimedAccount()` → `assertEvmAccountContinuity` on injected **and** WalletConnect, mismatch copy *This page is for a different wallet*, `Sign as 0x…` as a text node. **Do not reimplement the helper.** What is still missing is **proof of the path voting actually uses** (matching `account=` + same wallet → accept) and **WC / deeplink / hostile-query coverage**. Mismatch e2e exists; the happy path does not. A WalletConnect session for a different address is only covered on the injected mock. ## Current codebase | Piece | Behavior today | |--------|----------------| | Query | [`web/src/query.ts`](web/src/query.ts) `getClaimedAccount()` reads `account`, trim, empty → null. **Never a redirect target.** | | EVM page | [`web/src/pages/evm.ts`](web/src/pages/evm.ts) shows `Sign as ${claimedAccount}` via `el()` text nodes; passes `claimedAccount` into `signEvmMessage`. | | Bind | [`web/src/evm/account.ts`](web/src/evm/account.ts) `canonicalizeEvmAddress` = lowercase `0x` + 40 hex (lockstep API `normalize_account` EVM). Mismatch or invalid claimed → `EVM_ACCOUNT_MISMATCH`. No claim → pass through connected address. | | Injected sign | [`web/src/evm/sign.ts`](web/src/evm/sign.ts) assert **before** `prepare` / `personal_sign`. | | WalletConnect | [`web/src/evm/walletConnect.ts`](web/src/evm/walletConnect.ts) assert on session address (and e2e hook) **before** `personal_sign`. | | Deeplink | [`web/src/evm/deeplink.ts`](web/src/evm/deeplink.ts) encodes **current** portal `origin+path+search` (so `account=` stays). Never encode query `redirect_uri` as the Open-in-app target. | | API | [`api/src/verify/evm.rs`](api/src/verify/evm.rs) + [`api/src/account.rs`](api/src/account.rs): recovered signer must match submitted `account_id`. Query `account=` is **portal UX**; the API does not see it. | | SDK | `TermsGate` Accept + `buildSignUrl({ account })` in `@plasticdigits/cl8y-clickwrap` **0.1.1** (GitLab npm). | | Terra (reference) | [`web/src/terra/chain.ts`](web/src/terra/chain.ts) `assertAccountContinuity`; e2e mismatch in [`web/e2e/terra-sign.spec.ts`](web/e2e/terra-sign.spec.ts). | **Tests that exist** - Unit: [`web/src/evm/account.test.ts`](web/src/evm/account.test.ts), injected mismatch in [`web/src/evm/sign.test.ts`](web/src/evm/sign.test.ts), deeplink preserves `account=` in [`web/src/evm/deeplink.test.ts`](web/src/evm/deeplink.test.ts). - E2E: [`web/e2e/evm-sign.spec.ts`](web/e2e/evm-sign.spec.ts) **mismatch only** (`account=0xaaa…` vs mock `testEvmAccount`). Missing-provider Open in MetaMask case **does not** put `account=` on the page, so href preservation is unproven in Playwright. **Tests that do not exist** - E2E: `account=` **equals** connected wallet → Accepted + `signed_latest` for **that** address (the voting Accept path). - E2E / unit: WalletConnect mock + **different** claimed account → no submit. - E2E: Open in MetaMask / Binance / Copy link hrefs include `account=` when it is on the page. - Unit: WC `connectAndSignEvmWalletConnect` mismatch ( [`web/src/evm/walletConnect.test.ts`](web/src/evm/walletConnect.test.ts) only gates project id / e2e hook). - Hostile `account=` (`javascript:`, `terra1…`, too short) → fail closed, not used as `href`. Skills already mention invariant 13 in [`skills/portal-sign-disclosure/SKILL.md`](skills/portal-sign-disclosure/SKILL.md). Keep them in lockstep. ## Why this follow-up is needed Voting (and other integrators) now send `account=` on **Accept**, not only on Open-in-MetaMask hints. If the matching path regresses, users get a false *Accepted* for a **different** `0x…` than the dApp polls, then return still unsigned — or, worse, a future change skips the assert and records the wrong account’s terms while the UI still says *Sign as &lt;claimed&gt;*. Mismatch-only tests do not prove the 0.1.1 integrator contract. WC is a second signer path that can skip the injected assert if someone wires `prepare` without `assertEvmAccountContinuity`. ## Constraints / guardrails 1. **Do not reimplement** `assertEvmAccountContinuity` / a second checksum scheme. Extend tests and any thin wiring holes only. 2. **Portal bind is UX.** API EIP-191 recover (`verify/evm.rs`) remains source of truth. Do not trust query `account` in `POST /signatures/wallet`. 3. **`account` is not a URL.** Never pass it to `location`, `<a href>`, WalletConnect pairing, or Open-in-app targets except as an existing query **key** on the portal sign URL. 4. **Text nodes only** for `Sign as …` (`el()` / `textContent`). No `innerHTML` of query values. 5. **Checksum:** compare lowercase `0x`+40 hex only. EIP-55 vs lower must succeed; mixed/invalid claimed must fail closed (same message is OK). 6. **No claim → current behavior:** sign whatever valid address the wallet returns (then API binds that `account_id`). 7. **Do not** `wallet_switchEthereumChain` to Ethereum mainnet as part of this work (`personal_sign` is chain-agnostic). 8. **Do not** weaken redirect allowlisting or encode `redirect_uri` into MetaMask/Binance deeplinks. 9. **Out of scope:** Solana `/sign/solana` still ignores `account=` (postponed with #2 / #4). Telegram uses Telegram ids, not EVM `0x`. Do not expand this issue to those pages. 10. Playwright: Chromium, **workers = 5**, mock wallets only. No real MetaMask. ## Relevant files - [`web/src/pages/evm.ts`](web/src/pages/evm.ts) - [`web/src/evm/account.ts`](web/src/evm/account.ts) + [`account.test.ts`](web/src/evm/account.test.ts) - [`web/src/evm/sign.ts`](web/src/evm/sign.ts) + [`sign.test.ts`](web/src/evm/sign.test.ts) - [`web/src/evm/walletConnect.ts`](web/src/evm/walletConnect.ts) + [`walletConnect.test.ts`](web/src/evm/walletConnect.test.ts) - [`web/src/evm/deeplink.ts`](web/src/evm/deeplink.ts) + [`deeplinkUi.ts`](web/src/evm/deeplinkUi.ts) - [`web/src/query.ts`](web/src/query.ts) - [`web/e2e/evm-sign.spec.ts`](web/e2e/evm-sign.spec.ts) + [`web/e2e/helpers/evm-wallet.ts`](web/e2e/helpers/evm-wallet.ts) - [`api/src/verify/evm.rs`](api/src/verify/evm.rs), [`api/src/account.rs`](api/src/account.rs) - [`packages/cl8y-clickwrap/src/react/TermsGate.tsx`](packages/cl8y-clickwrap/src/react/TermsGate.tsx), [`packages/cl8y-clickwrap/src/urls.ts`](packages/cl8y-clickwrap/src/urls.ts) - [`skills/portal-sign-disclosure/SKILL.md`](skills/portal-sign-disclosure/SKILL.md), [`skills/testing-coverage/SKILL.md`](skills/testing-coverage/SKILL.md) ## Recommended direction 1. Add Playwright **matching** case: install mock wallet, `goto /sign/evm?property=cl8y.com&account=<mockChecksumOrLower>`, consent, accept → heading Accepted, `GET .../status?...&account=<lower>` `signed_latest: true`. Assert *Sign as* is visible. Use the **same** address the mock will connect (EIP-55 on the query is a plus). 2. Add Playwright **WC mismatch**: WC mock + `account=` other `0x` → *different wallet*, no Accepted, claimed address remains unsigned. 3. Extend missing-provider (or a dedicated) e2e: page URL includes `account=0x…`; Open in MetaMask href contains that account; Open in Binance `url=` contains it; Copy link is the portal URL (not `redirect_uri`, not `javascript:`). 4. Unit: WC hook/prepare path throws `EVM_ACCOUNT_MISMATCH` and does not call `personal_sign`. Invalid claimed (`javascript:alert(1)`, `terra1…`, `0x123`) throws; `Sign as` still text-only if rendered. 5. Keep [`skills/testing-coverage/SKILL.md`](skills/testing-coverage/SKILL.md) row for `evm-sign.spec.ts` updated (matching + WC mismatch + deeplink `account=`). 6. Optional ops checkbox: spot-check production `/sign/evm?property=…&account=0x…` still shows *Sign as* and refuses a different injected account (bundle already contains the mismatch string). ## Acceptance criteria - [ ] Matching `account=` + same injected mock → accept records **that** account, not a sibling provider. - [ ] Matching checksum vs lowercase claimed vs connected still succeeds. - [ ] Mismatch injected (already present) stays green. - [ ] Mismatch WalletConnect does not submit and does not show Accepted. - [ ] Open in MetaMask / Binance / Copy link preserve `account=` from the current portal search; they never use `account` as an href target. - [ ] Hostile `account` query is not executed / not used as navigation; bind still fail-closed. - [ ] No claim (`account` absent) still signs the connected wallet (existing tests). - [ ] API verify unchanged; no new trust of query `account` on POST. - [ ] Skills/test map mention the new cases. - [ ] `cd web && npx vitest run src/evm src/query.test.ts` and `npm run test:e2e -- evm-sign` (Playwright workers=5) green. ## Test plan (functional paths) 1. **Match (lower):** `account=` mock lowercase → consent → Connect & sign → Accepted → status true for that account. 2. **Match (EIP-55):** query checksum, wallet returns lower (or vice versa) → same. 3. **Mismatch injected:** existing e2e (`0xaaa…` vs mock) → *different wallet*, claimed unsigned, mock account also not newly signed for the claimed key. 4. **Mismatch WC:** WC mock account ≠ query `account` → fail closed; CTA re-enabled (`signShell` `finally` busy reset). 5. **Already signed + match:** status already true for claimed → success without forcing a second `personal_sign` (same as unsigned-already-signed today). 6. **No `account`:** existing happy paths (injected, EIP-6963, BinanceChain, late inject, WC) unchanged. 7. **Deeplink:** with `account` + `redirect_uri` on the page, MetaMask/Binance/Copy keep `account` and do not navigate to `redirect_uri`. 8. **Multi-provider pick:** two EIP-6963; claimed = MetaMask mock; picking Binance mock with a different address → mismatch; picking MetaMask → success. ## Test plan (attack / abuse) 1. **`account=javascript:alert(1)` / `data:` / `https://evil`:** must not become `<a href>` or `location`. Bind fail-closed. *Sign as* is a text node if shown. 2. **`account=` as open redirect:** Open-in-app and success redirect still use portal URL / allowlisted `redirect_uri` only (`web/src/redirect.ts`). `account` must not be fed to `safeRedirectUri`. 3. **Query tamper:** user strips `account=` → can sign a different wallet and see Accepted; integrator status poll for the **original** address stays unsigned (fail-closed for the dApp). Do not “fix” this by authenticating the query param. 4. **Query swap:** user replaces `account=` with another `0x` they control → they may accept for that address; dApp still polls the connected store address. Document; no server-side “claimed query” ACL. 5. **Hidden second EIP-6963:** must not `personal_sign` a non-selected provider when claimed matches the hidden one. 6. **WC session account ≠ claimed:** no `personal_sign` / no POST. 7. **`accountsChanged` mid-sign:** if the wallet signs a different key than `account_id` in the message, API recover must reject. Do not add a portal-only bypass. 8. **XSS in `Sign as`:** payload in `account` appears as text, not DOM. 9. **Cross-network claim:** `account=terra1…` on `/sign/evm` → invalid EVM address → mismatch, no submit. 10. **Do not** treat UI consent checkbox as proof of the claimed account. ## Verification criteria ```bash cd web && npx vitest run src/evm src/query.test.ts cd web && npm run test:e2e -- evm-sign # playwright.config.ts workers=5 ``` - New e2e names are obvious in the report (match, WC mismatch, deeplink `account=`). - Production spot-check (optional ops): `https://terms.cl8y.com/sign/evm?property=<registered>&account=0x…` shows *Sign as 0x…*; connecting a different injected account does not Accepted. - Integrator: after 0.1.1 Accept from voting, portal URL includes `account=` and *Sign as* matches the connected voting address. ## Out of scope - Re-doing [#15](https://gitlab.com/plasticdigits/cl8y-ecosystem-legal/-/issues/15) mobile discovery / Open in MetaMask / in-page WC. - Solana / Telegram sign-page `account=` bind (#2 / #4). - Changing EIP-191 verify or message bytes (#6). - Real device MetaMask in CI.
PlasticDigits commented 2026-08-31 04:12:25 +00:00 (Migrated from gitlab.com)

marked as related to #15

marked as related to #15
PlasticDigits commented 2026-08-31 04:31:51 +00:00 (Migrated from gitlab.com)

mentioned in issue #17

mentioned in issue #17
PlasticDigits commented 2026-08-31 04:31:51 +00:00 (Migrated from gitlab.com)

marked as related to #17

marked as related to #17
PlasticDigits commented 2026-08-31 04:41:31 +00:00 (Migrated from gitlab.com)

mentioned in merge request !33

mentioned in merge request !33
PlasticDigits commented 2026-08-31 04:56:55 +00:00 (Migrated from gitlab.com)

mentioned in commit 45f4ea5b61

mentioned in commit 45f4ea5b610bfae7a3c54c354199eebd7725957e
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-08-31 04:56:56 +00:00
PlasticDigits commented 2026-08-31 04:58:07 +00:00 (Migrated from gitlab.com)

Merge review (!33)

Merged !33 into main at 45f4ea5 (glab mr merge 15 --yes --remove-source-branch, no automerge, did not wait for CI). No merge conflicts (has_conflicts: false, 0 diverged commits vs main).

Acceptance criteria vs local verify

All #16 AC items are covered by the MR (unit + Playwright). Production code was not a reimplementation of assertEvmAccountContinuity — comments/docs plus PLAYWRIGHT_WEB_PORT for e2e; bind stays the existing helper. API EIP-191 verify was unchanged.

Local (worktree test/16-evm-account-continuity):

cd web && npx vitest run src/evm src/query.test.ts
# 10 files / 53 tests passed

PLAYWRIGHT_WEB_PORT=5174 npm run test:e2e -- evm-sign
# 17 passed (5 workers) — matching lower + EIP-55, sibling EIP-6963, WC mismatch,
# deeplink account=, hostile javascript:/terra1, already-signed skip personal_sign,
# plus existing injected / EIP-6963 / BinanceChain / late-inject / no-claim paths

127.0.0.1:5173 was already owned by an unrelated CL8Y-web vite preview, so e2e used port 5174 (supported by the MR config).

Problems (not merge blockers)

  • GitLab CI did not run: pipeline #69 / jobs failed with ci_quota_exceeded (same quota issue noted on #10 / #13). Not a code defect. Did not wait for CI per merge instructions.
  • Optional ops still open: production spot-check of https://terms.cl8y.com/sign/evm?property=<registered>&account=0x… (Sign as + refuse a different injected account). The bind was already on main before this test MR; a deploy is not required for the new tests to be true in prod, but the checkbox was never done.
  • Out of scope (already tracked): Solana /sign/solana still ignores account= — #17.

Post-merge leftovers (manual QA / CI quota note) are tracked in a follow-up issue.

## Merge review (!33) Merged [!33](https://gitlab.com/PlasticDigits/cl8y-ecosystem-legal/-/merge_requests/15) into `main` at `45f4ea5` (`glab mr merge 15 --yes --remove-source-branch`, no automerge, did not wait for CI). **No merge conflicts** (`has_conflicts: false`, 0 diverged commits vs `main`). ### Acceptance criteria vs local verify All #16 AC items are covered by the MR (unit + Playwright). Production code was **not** a reimplementation of `assertEvmAccountContinuity` — comments/docs plus `PLAYWRIGHT_WEB_PORT` for e2e; bind stays the existing helper. API EIP-191 verify was unchanged. Local (worktree `test/16-evm-account-continuity`): ```text cd web && npx vitest run src/evm src/query.test.ts # 10 files / 53 tests passed PLAYWRIGHT_WEB_PORT=5174 npm run test:e2e -- evm-sign # 17 passed (5 workers) — matching lower + EIP-55, sibling EIP-6963, WC mismatch, # deeplink account=, hostile javascript:/terra1, already-signed skip personal_sign, # plus existing injected / EIP-6963 / BinanceChain / late-inject / no-claim paths ``` `127.0.0.1:5173` was already owned by an unrelated `CL8Y-web` vite preview, so e2e used port **5174** (supported by the MR config). ### Problems (not merge blockers) - **GitLab CI did not run:** pipeline `#69` / jobs failed with `ci_quota_exceeded` (same quota issue noted on #10 / #13). Not a code defect. Did not wait for CI per merge instructions. - **Optional ops still open:** production spot-check of `https://terms.cl8y.com/sign/evm?property=<registered>&account=0x…` (*Sign as* + refuse a different injected account). The bind was already on `main` before this test MR; a deploy is not required for the new tests to be true in prod, but the checkbox was never done. - **Out of scope (already tracked):** Solana `/sign/solana` still ignores `account=` — [#17](https://gitlab.com/plasticdigits/cl8y-ecosystem-legal/-/issues/17). Post-merge leftovers (manual QA / CI quota note) are tracked in a follow-up issue.
PlasticDigits commented 2026-08-31 04:58:23 +00:00 (Migrated from gitlab.com)

mentioned in issue #18

mentioned in issue #18
PlasticDigits commented 2026-08-31 04:58:23 +00:00 (Migrated from gitlab.com)

marked as related to #18

marked as related to #18
PlasticDigits commented 2026-08-31 04:58:35 +00:00 (Migrated from gitlab.com)

Post-merge leftovers are #18 (production / integrator smoke). Solana bind remains #17.

Post-merge leftovers are [#18](https://gitlab.com/plasticdigits/cl8y-ecosystem-legal/-/issues/18) (production / integrator smoke). Solana bind remains [#17](https://gitlab.com/plasticdigits/cl8y-ecosystem-legal/-/issues/17).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
code/cl8y-ecosystem-legal#16
No description provided.