
How Wallet Injectors Work
When a DApp calls window.ethereum.request() or window.bitcoin.signPsbt(), it has no idea which wallet is on the other end MetaMask, Unisat, Xverse, or your own. That is the point. A wallet injector is the thin bridge that makes every wallet look the same to every DApp. EVM and Bitcoin wallets both use this pattern, but their interfaces, signing primitives, and discovery mechanisms differ in important ways that every wallet developer needs to understand.
What Is a Wallet Injector?
A wallet injector places a provider object onto the browser's global window before the DApp's own JavaScript runs. For EVM wallets this is window.ethereum; for Bitcoin wallets it is window.bitcoin. The DApp calls methods on this object to request accounts, sign transactions, and broadcast without knowing anything about the wallet behind it. The provider is injected either by a browser extension content script or by an SDK script tag loaded inside a mobile in-app browser or WebView.
EVM Wallet Injection
Extension / SDK
↓
window.ethereum set
↓
EIP-1193 provider
↓
DApp calls request()
↓
Wallet signs & returns
Bitcoin Wallet Injection
Extension / In-App Browser
↓
window.bitcoin set
↓
Bitcoin Provider (BIP-style)
↓
DApp calls request()
↓
PSBT signed & returnedBoth chains inject a provider into window but the interface and signing model differ.
Diagram: EVM injection (left) sets window.ethereum via EIP-1193; Bitcoin injection (right) sets window.bitcoin with a PSBT-based interface.
EVM Injection: EIP-1193
EVM wallet injection is standardised under EIP-1193. The provider exposes a single request() method that accepts any JSON-RPC method eth_requestAccounts, eth_sendTransaction, eth_signTypedData and returns a Promise. It also exposes on() for subscribing to events such as accountsChanged and chainChanged. Internally, the provider proxies each call through a postMessage() bridge to the wallet's background service worker, which holds the keys and performs ECDSA signing over secp256k1. The signed transaction or result is passed back as a resolved Promise.
DApp
↓
window.ethereum
↓
Background Worker
↓
Key Store (ECDSA)
↓
EVM Node (RPC)
eth_requestAccounts / eth_sendTransaction / eth_signTypedData -->
<-- accounts / tx hash / signatureDiagram: EVM request flow DApp calls window.ethereum, provider routes to background worker, key store signs, EVM node broadcasts.
Bitcoin Injection: window.bitcoin and PSBTs
Bitcoin wallets inject a provider under window.bitcoin, following a convention popularised by Unisat and adopted by Xverse, OKX, and others though no universal standard equivalent to EIP-1193 exists yet. The interface exposes methods such as getAccounts(), signPsbt(), sendBitcoin(), and signMessage(). The critical difference from EVM is the signing primitive: Bitcoin uses PSBTs (Partially Signed Bitcoin Transactions). Because each UTXO input requires its own signature, the DApp constructs or receives a PSBT, passes it to the wallet, and the wallet signs each input independently using ECDSA for Legacy and SegWit, or Schnorr signatures for Taproot inputs before returning the completed PSBT.
DApp / Website
↓
window.bitcoin
↓
Background Worker
↓
Key Store (Schnorr/ECDSA)
↓
Bitcoin Node/API
getAccounts / signPsbt / sendBitcoin / signMessage -->
<-- address list / signed PSBT / txid / signatureDiagram: Bitcoin request flow DApp passes PSBT to window.bitcoin, wallet signs each input (ECDSA/Schnorr), returns signed PSBT or broadcasts.
Key insight: The PSBT signing model is fundamentally different from EVM. A Bitcoin wallet must sign each UTXO input separately, verify the sighash for that input, and support multiple address types (Legacy, SegWit, Taproot) within a single transaction.
Multi-Wallet Discovery: EIP-6963 vs Bitcoin
When multiple wallets are installed, each tries to set its window provider object. EVM solved this with EIP-6963: each wallet dispatches an eip6963:announceProvider event with a unique rdns identifier, and DApps listen for all announcements to let the user choose. Bitcoin has no equivalent standard the last wallet to inject overwrites window.bitcoin silently. This fragmentation is an active problem in the Bitcoin DApp ecosystem, with wallets like Unisat and Xverse working towards a shared discovery convention.
EVM (EIP-6963):
MetaMask Rabby Coinbase
DApp: listens for eip6963:announceProvider
Bitcoin (window.bitcoin):
Unisat Xverse OKX BTC
DApp: reads window.bitcoin (last writer wins)EVM has standardised multi-wallet discovery; Bitcoin still relies on window.bitcoin convention.
Diagram: EIP-6963 gives EVM structured multi-wallet discovery; Bitcoin still suffers last-writer-wins window.bitcoin conflicts.
EVM vs Bitcoin Injector: Full Comparison
| Aspect | EVM Wallet (EIP-1193) | Bitcoin Wallet |
|---|---|---|
| Window object | window.ethereum | window.bitcoin |
| Interface standard | EIP-1193 (request / on / isConnected) | No universal standard (Unisat-style common) |
| Discovery standard | EIP-6963 (multi-wallet events) | No standard (last writer wins) |
| Core signing method | eth_sendTransaction, eth_sign | signPsbt, signMessage |
| Signing primitive | ECDSA secp256k1 (single sig) | PSBT (per-input ECDSA/Schnorr) |
| Transaction format | RLP-encoded tx object | PSBT (Partially Signed Bitcoin Tx) |
| Multi-wallet conflict | Resolved via EIP-6963 | Last injected wins (fragmented) |
| Mobile injection | WebView bridge / SDK | In-app browser / SDK bridge |
Security Considerations
For both EVM and Bitcoin injectors, the provider object is a trust boundary. Private keys must never cross into the page context they must remain in the isolated background service worker (extension) or native signing layer (SDK). A malicious page cannot read keys from a well-implemented extension, but a malicious extension can inject a fake window.ethereum or window.bitcoin that intercepts and alters transactions. For Bitcoin wallets specifically, always validate the PSBT inputs and outputs before signing the DApp could construct a PSBT that signs away more funds than the user expects.
Key insight: Always display a clear signing prompt showing exactly what the user is approving for EVM this means decoded transaction data; for Bitcoin this means each PSBT input and output with amounts and addresses. Never auto-sign without explicit user confirmation.
EVM and Bitcoin wallets both use window injection to bridge DApps to signing but the interfaces, signing primitives (ECDSA tx vs PSBT), and discovery mechanisms are distinct. Build to both standards and your wallet works across the full Web3 ecosystem.
