Sitelet https://github.com/ChainSafe/canton-snap/pull/55
Skip to content

feat(dapp): custodial-only readiness + dApp release-please - #55

Merged
sadiq1971 merged 7 commits into
mainfrom
feat/v1-custodial-only-flag
May 19, 2026
Merged

sadiq1971 merged 7 commits into
mainfrom
feat/v1-custodial-only-flag

Conversation

@sadiq1971

@sadiq1971 sadiq1971 commented May 19, 2026 •

Copy link
Copy Markdown
Member

Summary

Prepares the dApp for a v1.0 custodial-only ship while preserving the non-custodial flow behind a flag, drops the disabled Bridge tab, hardens chain-add handling, persists the active dashboard tab across refresh, and wires the dApp into release-please.

Changes

  • Non-custodial behind VITE_ENABLE_NON_CUSTODIAL (default off). New users skip the choice screen and go straight to custodial registration. Snap RPC probes are skipped on standard MetaMask. Non-custodial code stays in the tree so a flag-flipped build re-enables the full flow without source changes.
  • Custodial-only UX polish: CustodialRegistrationPage hides its "← Back to registration" / "Cancel" controls when there's nowhere to go back to. DashboardProfilePage hides the "CANTON SNAP" status block for custodial users.
  • Bridge tab removed: sidebar entry, BridgeIcon, "Bridge assets" quick action on Profile, Transfers/Bridge sub-tabs + orphan CSS on Activity all gone.
  • EIP-3326 switch-first chain handling in ensureChainAdded (network.ts) and now routed through by TransferPage. Avoids the -32602 nativeCurrency.symbol does not match … failure when MetaMask has the chain stored with stale metadata.
  • Dashboard tab persists across refresh via window.location.hash mirroring in App.tsx. /#balances survives a reload; browser back/forward navigates tabs.
  • release-please for dApp: packages/dapp registered with component=dapp. No publish job — the dApp is private: true, so release-please just bumps package.json, writes packages/dapp/CHANGELOG.md, tags dapp-vX.Y.Z, and creates a GitHub release on merge. The existing snap publish job is unchanged.

Bundle impact

Flag JS CSS
off (default) 272.17 KB 34.11 KB
on 283.49 KB 39.23 KB

Vite tree-shakes dead non-custodial branches when the flag is off.

Test plan

  • npm run lint clean.
  • npm -w packages/dapp run build clean (custodial-only build).
  • VITE_ENABLE_NON_CUSTODIAL=true npm -w packages/dapp run build clean (full build).
  • Standard MetaMask: connect with flag off → straight to custodial registration; no choice screen; no `wallet_getSnaps` 4100 in console.
  • Transfer flow: chain-switch / chain-add resolves cleanly against MM entries with drifted metadata.
  • Refresh on Balances → stays on Balances; back/forward navigates tabs.
  • Disabled MetaMask Flask in profile that had both extensions to eliminate the multi-extension window.ethereum collision.
  • After merge: release-please opens a dApp release PR alongside the existing snap release PR chore(main): release snap 1.0.0 #54.

Future cleanup (next milestones)

  • v1.1: flip the default flag, polish non-custodial UX.
  • Permanent drop: single PR removes every NON_CUSTODIAL_ENABLED branch + non-custodial pages.

… add dApp to release-please

v1.0 ships custodial-only. Non-custodial code stays in the bundle behind
a build-time flag so a flag-flipped build re-enables the full flow
without code changes.

Gating (UI-only — dashboard/transfer/balances already branch on
profile.keyMode, so they need no changes):
- New helper packages/dapp/src/lib/features.ts exports
  NON_CUSTODIAL_ENABLED = (import.meta.env.VITE_ENABLE_NON_CUSTODIAL === "true").
- App.tsx routes new users straight to custodial-pending instead of the
  registration-choice screen when the flag is off. The choice page and
  the two non-custodial registration pages still exist but their
  renderers are guarded by the flag so they're unreachable.
- CustodialRegistrationPage.onBack is now optional; when omitted (flag
  off) the "← Back to registration" link and the inline "Cancel" button
  are hidden, since there's nowhere to go back to.
- Bundle: 273 KB with flag off vs 283 KB with flag on — Vite tree-shakes
  the dead non-custodial branches.

release-please:
- Add packages/dapp to release-please-config.json with component=dapp.
- Add packages/dapp: 0.1.0 to .release-please-manifest.json.
- No publish step: only the snap is on npm; the dApp gets a tag +
  GitHub release + CHANGELOG entry on each user-facing dApp commit.
  Regular CI on the release PR validates the dApp builds.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a build-time feature flag, NON_CUSTODIAL_ENABLED, to control the availability of the non-custodial registration flow, defaulting to a custodial-only experience for v1.0. Changes include conditional navigation logic in App.tsx, UI adjustments in CustodialRegistrationPage.tsx to hide back-links when the flag is disabled, and environment configuration updates. Feedback suggests refactoring the custodial flow entry logic into a reusable useCallback to eliminate code duplication and improve maintainability.

Comment thread packages/dapp/src/App.tsx Outdated
sadiq1971 added 4 commits May 20, 2026 00:48
useSnap()'s mount effect called wallet_getSnaps unconditionally to
detect a previously-installed snap. Standard MetaMask doesn't support
that method and rejects with RPC 4100. The error was caught (returns
null), so behavior was correct, but MetaMask logs the error before
returning the rejection, and the failed RPC also showed up as
'StreamMiddleware - Unknown response id' noise.

useRegistration() mounts useSnap() unconditionally, so even the
custodial flow triggered the probe. Now the effect is gated on
NON_CUSTODIAL_ENABLED — custodial-only builds never call any snap RPC,
console stays clean, and standard MetaMask is fully supported.
…sn't reject stale chains

ensureChainAdded() unconditionally called wallet_addEthereumChain when MM
wasn't already on the target chainId. If MM had previously stored that
chainId with a different nativeCurrency.symbol (e.g. an older dApp build
that used a different name), MM returns -32602:
'nativeCurrency.symbol does not match currency symbol for a network the
user already has added with the same chainId'.

EIP-3326 covers this: switch first, fall back to add only on 4902
(chain not added). Switch tolerates symbol/name drift because it
doesn't try to mutate the existing entry. Add still runs cleanly for a
genuinely-unknown chain.
handleMetaMaskSign called addEthChain directly, bypassing the
switch-first pattern in ensureChainAdded. If MM had the chain stored
with even minor metadata drift (e.g. a previous Canton Devnet entry
using a different nativeCurrency.symbol), every transfer attempt would
hit -32602 'nativeCurrency.symbol does not match'.

ensureChainAdded already does the right thing: switch first, fall back
to add only on 4902. Drop the inline addEthChain + ethChainId calls
and use the shared helper.
Bridge: drop the Bridge sidebar entry (was disabled), the BridgeIcon
component, the 'Bridge assets' quick-action button on the profile
page, and the Transfers/Bridge sub-tab toggle on the activity page
along with its associated CSS. Activity now just shows transfers.

Tab persistence: dashboardTab now syncs with window.location.hash, so
refreshing on /#balances stays on Balances instead of snapping back to
Profile. Hash updates use history.replaceState so back/forward across
tabs works without polluting history with intermediate entries. The
hash is only written while on the dashboard — landing/registration
don't touch it.
@sadiq1971 sadiq1971 changed the title feat(dapp): gate non-custodial behind flag; add dApp to release-please feat(dapp): custodial only readiness May 19, 2026
… profile

- Gemini review: handleCustodial is now a useCallback, and goRegister
  calls it instead of duplicating the setMode/setPage pair.
- Self-review: the Profile page's 'CANTON SNAP — Not installed' block
  is now hidden for custodial users entirely. The block was always
  meaningless for them and looked like a warning in the v1.0 custodial-
  only build where useSnap's probe is also skipped.
@sadiq1971

Copy link
Copy Markdown
Member Author

Resolved the duplication in commit a842684 — handleCustodial is now a useCallback and goRegister calls it instead of inlining the setMode+setPage pair. Thanks for the catch.

@sadiq1971 sadiq1971 changed the title feat(dapp): custodial only readiness feat(dapp): v1.0 custodial-only readiness + dApp release-please May 19, 2026
@sadiq1971 sadiq1971 changed the title feat(dapp): v1.0 custodial-only readiness + dApp release-please feat(dapp): custodial-only readiness + dApp release-please May 19, 2026
@sadiq1971 sadiq1971 self-assigned this May 19, 2026
@salindne
salindne self-requested a review May 19, 2026 21:01
@sadiq1971
sadiq1971 merged commit b7ab0f1 into main May 19, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants