MetaMask Flask is required. Local snaps are a developer feature rejected by the standard MetaMask extension. Once the snap is published to npm and passes MetaMask's snap review process, a regular MetaMask install will suffice.
Browser profile: Run MetaMask Flask in a dedicated browser profile that does not have the standard MetaMask extension installed. Both extensions inject
window.ethereumand conflict with each other — the dApp will behave unpredictably if both are active in the same profile.
node --version # v18 or higherClone the canton-middleware repo and start the local stack:
make docker-up # start Canton ledger + PostgreSQL + middleware API
make docker-down # stop and remove containersVerify the API is up:
curl http://localhost:8081/health
# Expected: OKnpm install
cp packages/snap/.env.example packages/snap/.env
cp packages/dapp/.env.example packages/dapp/.envEdit each .env as needed. For local snap dev set VITE_SNAP_ID=local:http://localhost:4040 in packages/dapp/.env; the port must match VITE_SNAP_PORT in packages/snap/.env (default: 4040).
npm run build # build snap + dApp
npm run serve # start snap (4040) + dApp (3000) together
# Or individually:
npm run serve:snap # http://localhost:4040
npm run dev:dapp # http://localhost:3000Open the dApp at http://localhost:3000 — in the browser profile where MetaMask Flask is installed.
The snap signs the 32-byte transaction hash returned by the middleware — it never contacts the middleware directly.
Canton dApp (browser)
│
├── wallet_invokeSnap ──────────────────► MetaMask Flask
│ │
│ Canton Snap
│ (key derivation + signing)
│
└── fetch(http://localhost:8081/...) ────► Middleware API
│
Canton Ledger (gRPC)
The UI guides users through the flow. Pages in order:
| Page | Description |
|---|---|
| Landing | Connect MetaMask wallet |
| Registration Choice | Choose Custodial or Non-Custodial mode |
| Custodial Registration | One-click: middleware holds your Canton key |
| Non-Custodial Registration | Install snap → sign registration → submit |
| Registration Done | Party ID and fingerprint confirmed |
| Dashboard: Profile | Party details, key mode, snap version and install status |
| Dashboard: Balances | ERC-20 token balances for the registered Canton party |
| Dashboard: Transfer | Send tokens to another Canton party via the snap |
| Dashboard: Activity | ERC-20 transfer history (requires relayer — see Limitations) |
Before an address can register, it must be whitelisted on the middleware. From the canton-middleware repo:
go run ./scripts/utils/whitelist.go --address <evm-address>Registration will return an error until the address is whitelisted.
To test transfers, the Canton party must hold tokens. From the canton-middleware repo:
go run ./scripts/utils/fund-wallet.go --address <evm-address> --amount 1000 --token DEMOnpm test # crypto unit tests + snap integration tests
npm run test:snap # snap integration tests only (jest + @metamask/snaps-jest)MetaMask Flask required — Until the snap is published to npm and reviewed by MetaMask, only MetaMask Flask users can install it.
Local snap mode — Local development uses local:http://localhost:4040 and requires MetaMask Flask. Published snap mode uses npm:@chainsafe/canton-snap with standard MetaMask.
Each developer runs their own snap server — The local: snap ID is bound to localhost; teammates cannot share one instance.
Activity tab requires relayer/bridge — The Activity UI is built and reads ERC-20 transfer logs from the Canton EVM RPC. Populating real transaction history depends on the relayer or bridge being deployed and indexing events. This is the primary remaining integration work.
- Relayer / bridge integration — Deploy and connect the relayer or bridge so the Activity tab has real transaction history to display. The UI and log-fetching logic are complete on the dApp side.
| Canton dApp | http://localhost:3000 |
| Snap server | http://localhost:4040 (set via VITE_SNAP_PORT in packages/snap/.env) |
| Snap ID | local:http://localhost:4040 |
| Middleware API | http://localhost:8081 |
| Command | Purpose |
|---|---|
make docker-up |
Start middleware stack (Canton + DB + API) |
make docker-down |
Stop middleware stack |
npm run serve |
Start snap + dApp together |
npm run serve:snap |
Snap server only |
npm run dev:dapp |
dApp dev server only |
npm run build |
Build snap + dApp |
npm run watch:snap |
Snap hot-reload |
npm test |
All tests |
npm run lint |
Lint all packages |
npm run format |
Format all packages |
| Middleware script | Purpose |
|---|---|
go run ./scripts/utils/whitelist.go --address <addr> |
Whitelist an EVM address for registration |
go run ./scripts/utils/fund-wallet.go --address <addr> --amount N --token DEMO |
Fund a Canton party for transfer testing |
| Middleware endpoint | Purpose |
|---|---|
POST /register |
Custodial or non-custodial registration |
POST /register/prepare-topology |
Step 1 of non-custodial registration |
| Snap method | Dialog | Purpose |
|---|---|---|
canton_getPublicKey |
Yes | Key derivation + SPKI + fingerprint |
canton_signTopology |
Yes | Sign Canton topology multihash |
canton_signHash |
Yes | Sign a 32-byte Canton transaction hash with optional metadata |
canton_getFingerprint |
First use per origin + key index | Fingerprint lookup only |