A browser-based test dapp for the MetaMask Connect
This playground is part of the MetaMask Connect monorepo and provides a comprehensive testing environment for:
- Multichain API: Connect to multiple chains simultaneously (Ethereum, Linea, Polygon, Solana, etc.)
- Legacy EVM Connector: Backwards-compatible connection for EVM chains
- Wagmi Integration: Test the wagmi connector for React applications
- Node.js (>=20.19.0)
- Yarn (v4.1.1+)
From the monorepo root:
# Install all dependencies
yarn install
# Build workspace packages
yarn buildcp .env.example .envThen fill out the resulting .env file:
INFURA_API_KEY=your_infura_api_keyFrom the monorepo root:
yarn workspace @metamask/browser-playground startOr from this directory:
yarn startThis launches the development server at http://localhost:3000.
Connect to multiple blockchain networks in a single session:
- Ethereum Mainnet & Testnets
- Layer 2 networks (Linea, Arbitrum, Polygon, etc.)
- Solana
Toggle between multichain and legacy EVM modes to test backwards compatibility with existing dapps.
Test the wagmi integration for React applications with persistent sessions and multichain support.
The browser playground includes an EIP-6963 test bench for checking provider discovery behavior from @metamask/connect-evm.
-
Start the browser playground:
yarn workspace @metamask/browser-playground start
-
Open
http://localhost:3000. -
Find the EIP-6963 test bench section.
-
Reload the page and watch for an automatic announcement row.
-
Click Request providers to dispatch
eip6963:requestProvider. -
To validate the MMConnect-managed provider, use a browser profile without a native MetaMask EIP-6963 provider enabled. Confirm the MetaMask row includes an
rdns, UUID,Provider request=Yes, andLegacy provider=Yes. If native MetaMask is enabled, the MMConnect-managed provider may be suppressed and the native row can showLegacy provider=No. -
Click Announce SDK provider to call
legacySDK.announceProvider()manually when the MMConnect-managed provider is not suppressed and another announcement is needed. -
Click Clear to reset the observed announcement log.
This panel observes real browser eip6963:announceProvider events. It does not simulate native wallet announcements.
The playground emits analytics events via @metamask/analytics to whatever endpoint METAMASK_ANALYTICS_ENDPOINT resolves to (defaults to the production sink). For local verification — confirming a new property landed, a classifier picked the right bucket, a new event fired at all — you can point the playground at a local echo server and use the in-page Analytics test bench section to drive each code path.
The pieces below are already wired up in this repo; this is just so you know what's involved if anything looks off:
scripts/analytics-echo-server.mjs— a tiny Node HTTP server that acceptsPOST /v2/eventsand pretty-prints every event withevent_name,failure_reason,method, andtransporthighlighted.craco.config.js—DefinePluginis patched to forwardprocess.env.METAMASK_ANALYTICS_ENDPOINTinto the browser bundle. CRA's default behaviour only exposesREACT_APP_*vars, so without this the override would silently do nothing.public/index.html— theContent-Security-Policymeta tag'sconnect-srcallowlist includeshttp://localhost:*andhttp://127.0.0.1:*so the browser is allowed to reach a local sink. Without this the browser drops the request and the Network tab shows nothing — the only hint is a CSP refusal in the console.src/components/AnalyticsTestBench.tsx— the collapsible "Analytics test bench" section in the playground UI, with one button perclassifyFailureReasonbranch.
-
Start the echo server in one terminal:
yarn analytics:echo # → analytics echo server listening on http://localhost:8787 -
Start the playground in another terminal with the endpoint override:
METAMASK_ANALYTICS_ENDPOINT="http://localhost:8787/" yarn start(You can also put
METAMASK_ANALYTICS_ENDPOINT=http://localhost:8787/in your.env— it goes through the sameDefinePluginpath.) -
Open
http://localhost:3000(or whatever port CRA picked), connect via the Multichain card. -
Expand "Analytics test bench" at the top of the page. Each button drives a request shape designed to land in a specific
failure_reasonbucket onmmconnect_wallet_action_failed. Watch the echo-server terminal — events arrive within a couple hundred ms.The bench keeps an in-page "Recent triggers" log showing the raw
name/code/msgthe wallet returned, so you can cross-reference what the wallet sent vs which bucket the classifier picked.
Two buckets aren't deterministically reachable from a button:
transport_timeout— toggle DevTools → Network → "Offline", then click any wallet-bound trigger, then wait ~30s for the SDK timeout.transport_disconnect— click a wallet-bound trigger, then disable/quit the MetaMask extension before approving.
Both buttons in the bench just print these instructions in an alert.
On a CAIP-25 multichain session, the wallet's permission layer rejects any method not in the granted scope with EIP-1193 4100 Unauthorized before the method handler runs (source). So buttons like "bogus method" and "switch chain to 0xfa" all land in wallet_unauthorized, even though they'd produce different codes (-32601 / 4902) if the wallet got to run its handlers. The bench labels reflect this — see the in-page "Heads up" note.
If events aren't arriving at the echo server, the env var probably didn't make it into the bundle. Quick check:
curl -s http://localhost:3000/static/js/bundle.js | grep -o 'localhost:8787[^"]*' | head -1
# → localhost:8787/If that prints nothing, the bundle is still pointing at the production endpoint — restart yarn start with the env var set in the same shell.
browser-playground/
├── src/
│ ├── App.tsx # Main application component
│ ├── components/
│ │ ├── AnalyticsTestBench.tsx # Collapsible bench for driving each failure_reason branch
│ │ ├── DynamicInputs.tsx # Checkbox selection UI
│ │ ├── FeaturedNetworks.tsx # Network selection component
│ │ ├── LegacyEVMCard.tsx # Legacy EVM connector card
│ │ ├── ScopeCard.tsx # Network scope with method invocation
│ │ ├── WagmiCard.tsx # Wagmi connector card
│ │ └── WalletList.tsx # Wallet selection component
│ ├── helpers/ # Platform-specific helpers
│ ├── sdk/
│ │ ├── SDKProvider.tsx # Multichain SDK context
│ │ ├── LegacyEVMSDKProvider.tsx # Legacy EVM SDK context
│ │ └── index.ts
│ └── wagmi/
│ ├── config.ts # Wagmi configuration
│ └── metamask-connector.ts # Auto-generated connector
├── scripts/
│ ├── analytics-echo-server.mjs # Local POST /v2/events sink for manual analytics testing
│ ├── copy-wagmi-connector.js # Copies wagmi connector from integrations/
│ └── README.md # Script documentation
└── public/
This playground uses @metamask/playground-ui for shared constants, helpers, and types. See the playground-ui README for details.
The src/wagmi/metamask-connector.ts file is auto-generated from integrations/wagmi/metamask-connector.ts. See scripts/README.md for details on why and how this works.
Important: Never edit src/wagmi/metamask-connector.ts directly. Edit integrations/wagmi/metamask-connector.ts instead.
See the main repository contributing guide for development setup and guidelines.