Skip to content

feat: warn when the connected wallet is on a different network than the dApp - #193

Merged
gabitoesmiapodo merged 7 commits into
mainfrom
feat/146
Sep 9, 2026
Merged

feat: warn when the connected wallet is on a different network than the dApp#193
gabitoesmiapodo merged 7 commits into
mainfrom
feat/146

Conversation

@gabitoesmiapodo

@gabitoesmiapodo gabitoesmiapodo commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

Closes #146

Connecting a wallet pointed at another Canton network left every write failing with nothing on screen to explain it. The app now reads both networks, compares them, and says so.

Changes

  • Warning strip above the header while the wallet cannot reach the app's network
  • Re-checks on party change, window focus, and every 30 seconds
  • The "No deployment" card names the network instead of advising the bootstrap script
  • Reads the wallet's side from its participant's connected synchronizers
  • Reads the app's side off the synchronizer id wallet-service stamps on its disclosures

Deviations

  • Strip names only the wallet's network, not both: no reliable name exists for the app's side, so that criterion is worth editing
  • Wallet's side read from connected synchronizers, not the factory row the notes suggested, which costs three round trips and rebuilds the backend
  • Rule is membership, not equality, since a participant can be connected to several synchronizers

Acceptance criteria

  • The app reads the synchronizer id of the ledger the connected wallet uses
  • The app reads the synchronizer id stamped on the disclosures it fetches from its own wallet-service
  • A visible warning appears while the two differ
  • No warning shows when either id is missing
  • The warning names both networks
  • Tests cover match, mismatch, and a missing id

Test plan

Automated tests

  1. Run pnpm install
  2. Run pnpm -C dapp/frontend test
  3. Expect 191 passing tests

Manual verification

  1. Check out this branch, bring up the LocalNet and wallet-service, then run pnpm app:dev
  2. Connect a wallet pointed at the local participant and confirm no strip appears
  3. Switch the wallet to another network, then return to the tab
  4. Expect a warning strip above the header naming the wallet's network
  5. Reload and expect the page to read "Wrong network" rather than "No deployment"
  6. Switch back and expect the strip to clear within 30 seconds

Breaking changes

None.

Checklist

  • Self-reviewed my own diff
  • Tests added or updated
  • Docs updated (if applicable)
  • No unrelated changes bundled in

Screenshots

image

… switch

The strip only reflected what was true at connect. Nothing re-read either
synchronizer id, so switching networks in the wallet left it stale, which made
the warning useless in the case it exists for.

- useWrongNetwork re-reads on three triggers: the party changing, the page
  regaining focus, and every 30 seconds. Focus catches a switch as it happens,
  because using the wallet takes focus off the page. visibilitychange misses it,
  since an extension popup draws over the tab rather than hiding it.
- synchronizer.ts reads /v2/state/connected-synchronizers in one request. The
  wallet's side no longer comes from the factory row in config.ts: that read is
  three round trips and its answer rebuilds the backend, so polling it would
  re-run every ledger read.
- wrongNetwork tests membership instead of equality. A participant can be
  connected to several synchronizers, and reaching the app's one is what decides
  whether a write lands.
- A failed read stays silent and leaves the last answer standing.

It has to poll: CIP-0103 defines no network-change event and the SDK pushes
accounts only.
The strip claimed to name a target network to switch to, and nothing can
name one a reader could act on. CIP-0103 defines `networkId` for a wallet
only, and the label wallet-service could supply from its NETWORK variable
is typed by hand on both sides, so the two read the same for different
networks as easily as differently for one.

- The strip now reads "Wrong network: wallet is connected to
  bootnode-devnet, switch networks to proceed." It names the wallet's own
  label and no target, and drops the synchronizer ids, which were unreadable
  and told the user nothing they could act on.
- networkLabel drops the CAIP-2 namespace, since every network this app sees
  is under canton. A label with no namespace stays whole.
- wrongNetwork returns a boolean. Nothing rendered the ids any more, so the
  result payload went with them.
- architecture.md records why there is no target label and that a wallet
  quiet about its network reads as canton:local.
The connected-synchronizers read put the party in the resource string. A wallet
that checks the resource against the ledger API's own route list rejects that,
and the hook swallows the failure, so the strip never appears. The party now
travels in `query`, the way every other read passes one.

Three fixes in `useWrongNetwork`:

- Only the last check started may write, so a slow older read cannot overwrite a
  newer verdict.
- The verdict carries the party it was read for, so the previous party's answer
  is not shown against the new one's network.
- The app's synchronizer id is read once and kept. wallet-service answers for one
  network, so every later check is a single participant read rather than another
  `amulet.tap` every 30 seconds.

On the wrong network `config.ts` finds no operator and throws its
`run pnpm run bootstrap` advice, which filled the page with a symptom. The card
now names the network, and the advice stays for a ledger that really has no
deployment.

Adds `synchronizer.test.ts`: the happy path, the request shape, the empty cases.
Split `fetchAppNetwork` out of `fetchTransferContext`. That builder needs an
AmuletRules and an open mining round both, and the SV opens the first round
minutes after a LocalNet start, so the check could not answer until then. The
network id sits on the AmuletRules disclosure alone.

Also:

- Box the cached app network, so an answer carrying no id counts as read.
  Before, the ref held the bare id, so every later check re-ran the tap.
- Read the wallet's synchronizers through `call` in config.ts instead of a
  second inline cast.
- Simplify `networkLabel` to one expression and drop a redundant test row.
- Move the rule about passing a filter in `query` into dapp/frontend/CLAUDE.md,
  where the next author looks for it.
- Trim comments that restated the code or copied architecture.md.
# Conflicts:
#	dapp/frontend/architecture.md
@vercel

vercel Bot commented Sep 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
demo.canton-dappbooster Ready Ready Preview Sep 9, 2026 12:29pm UTC
docs.canton-dappbooster Ready Ready Preview Sep 9, 2026 12:29pm UTC

Request Review

@fernandomg fernandomg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🫵🏻🪨

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Warn when the connected wallet is on a different network than the dApp

2 participants