Skip to content

Phantom EVM wallet unavailable in popup flows due to long URL params #2467

@broody

Description

@broody

Problem

Phantom's browser extension does not inject its EVM provider (window.phantom.ethereum) when a page is opened via window.open() with very long URL query parameters. This affects both popup auth (connect) and session registration flows.

Affected flows:

  • Popup auth (popupConnect) — connect/signup flow via openPopupAuth({ action: "connect", ... })
  • Session registration (createSessionViaPopup) — session creation via openPopupAuth({ action: "create-session", ... })

Both flows forward URL params from the iframe to the popup in openPopupAuth() (packages/keychain/src/utils/connection/popup.ts), including policies which contains large double-encoded JSON.

Root Cause

Phantom's content script skips EVM provider injection when the URL exceeds a certain length. The policies param is the culprit — it contains URL-encoded JSON that can be very large.

Key finding: Passing just preset as a URL param works fine — Phantom injects normally. It's specifically the large policies encoding that causes the issue.

Evidence:

  • window.open("localhost:3001/popup-auth?channel_id=xxx&action=connect")window.phantom.ethereum exists
  • window.open("localhost:3001/popup-auth?channel_id=xxx&action=connect&policies=<large encoded JSON>")window.phantom.ethereum is undefined
  • MetaMask, Rabby, and Temple all inject correctly regardless of URL length

Proposed Fix

Move large params (policies, signers) off the URL and deliver them via BroadcastChannel (already used for auth-complete/auth-error signaling):

  1. openPopupAuth() opens popup with only channel_id + action in URL
  2. Popup sends { type: "popup-ready" } on mount
  3. Opener responds with { type: "popup-params", policies, preset, rpcUrl, signers, origin }
  4. Popup injects received params into URL via setSearchParams (or state) so the connection hook picks them up

This keeps the initial URL short so Phantom injects, and params arrive shortly after via the channel.

Files

  • packages/keychain/src/utils/connection/popup.tsopenPopupAuth(), builds popup URL
  • packages/keychain/src/components/PopupAuth.tsx — popup page, reads params from URL
  • packages/keychain/src/components/ConnectRoute.tsxcreateSessionViaPopup(), calls openPopupAuth()
  • packages/keychain/src/utils/connection/index.tspopupConnect(), calls openPopupAuth()
  • packages/keychain/src/hooks/connection.ts — connection hook that consumes URL params

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions