Skip to content

fix(phantom): connect Bitcoin via Wallet Standard when injected provider absent#98

Open
0xepicode wants to merge 1 commit into
swapkit:developfrom
0xepicode:fix/phantom-btc-wallet-standard
Open

fix(phantom): connect Bitcoin via Wallet Standard when injected provider absent#98
0xepicode wants to merge 1 commit into
swapkit:developfrom
0xepicode:fix/phantom-btc-wallet-standard

Conversation

@0xepicode

Copy link
Copy Markdown

Problem

Connecting Phantom for Bitcoin throws SwapKitError: wallet_phantom_not_found. Other chains (Solana, Ethereum) work fine; Phantom is installed.

SwapKitError: wallet_phantom_not_found
    at C$1 (.../index-lI6q89jE.js:6662:27557)
    at Array.map (<anonymous>)
    ...

Root cause

Phantom has deprecated the injected window.phantom.bitcoin provider (docs) and newer builds expose Bitcoin only through the Wallet Standard registry. Solana and EVM are still injected (window.phantom.solana / window.phantom.ethereum), which is exactly why those chains keep working while BTC fails.

The Bitcoin connect path detected the provider solely via window.phantom.bitcoin?.isPhantom:

const provider = phantom?.bitcoin;
if (!provider?.isPhantom) throw new SwapKitError("wallet_phantom_not_found");

For users on newer Phantom builds window.phantom.bitcoin is absent → immediate throw. (Confirms hypothesis #1 of the investigation — the symptom asymmetry, BTC-only failure with Solana working on the same window.phantom, can only be explained by the injected BTC provider being gone.)

Fix

New getBitcoinAccess() resolves a Bitcoin signing surface in two steps:

  1. Legacy injected provider first (window.phantom.bitcoin) — zero behaviour change for users where it still works.
  2. Wallet Standard fallback — discovers Phantom via @wallet-standard/app getWallets() and uses the canonical Bitcoin Wallet Standard features:
    • bitcoin:connectconnect({ purposes: ["payment"] }){ accounts } (uses the payment address, correct for swaps)
    • bitcoin:signTransactionsignTransaction({ psbt, inputsToSign: [{ account, signingIndexes }] })[{ signedPsbt }]

The getWalletMethods Bitcoin case now delegates to getBitcoinAccess; the toolbox/signer wiring is unchanged.

Tests

New packages/wallet-extensions/test/phantom-bitcoin.test.ts (deterministic, no network):

  • ✅ falls back to Wallet Standard when injected provider absent (the original repro — fails on develop, passes here)
  • ✅ legacy injected provider still works (regression)
  • ✅ throws wallet_phantom_not_found when neither path available
  • ✅ ignores a WS wallet missing required bitcoin features
  • ✅ throws core_transaction_failed when WS signing returns no result

Full suite green (40 pass), typecheck + biome clean.

Preventing this class of bug

Other in-repo wallets still resolve BTC via deprecated injected providers and will break the same way if those wallets follow Phantom's migration (OKX, Bitget, OneKey, CTRL/XDEFI, KeepKey-bex, Vultisig). Worth a follow-up to standardise on Wallet Standard discovery for Bitcoin across extension wallets.

Note: the @near-js/* lines that may appear in bun.lock are pre-existing drift — a no-op bun install on develop already rewrites them; this PR's lock delta is limited to the new @wallet-standard/* entries.

…der absent

Phantom deprecated the injected window.phantom.bitcoin provider; newer builds
expose Bitcoin only through the Wallet Standard registry (Solana/EVM are still
injected, so those chains keep working). Connecting BTC therefore threw
wallet_phantom_not_found.

getBitcoinAccess now prefers the legacy injected provider when present (no
behaviour change for existing users) and falls back to discovering Phantom via
@wallet-standard/app getWallets() using the bitcoin:connect / bitcoin:signTransaction
features.
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.

1 participant