Connecting Wallets¶
A Web3 application doesn't hold users' private keys. It asks their wallet software to sign things on their behalf, through a standardized connection protocol. This chapter covers how that connection actually works, and the specific standard (EIP-1193) that makes different wallets interchangeable from a dapp's perspective.
EIP-1193: the interface every browser wallet implements¶
Browser-based wallets (MetaMask, and most others) inject a window.ethereum object into every page, implementing a standard interface (EIP-1193) for requesting account access, sending JSON-RPC requests, and subscribing to events (account changes, network changes). Because this interface is standardized, an application written against it works with any conforming wallet, without needing wallet-specific integration code, the same interoperability principle behind Contract ABI's standardized encoding, applied here to the wallet-connection layer instead.
The connection flow¶
import { createWalletClient, custom } from "viem";
import { mainnet } from "viem/chains";
async function connectWallet() {
if (typeof window === "undefined" || !window.ethereum) {
throw new Error("No browser wallet detected");
}
const walletClient = createWalletClient({
chain: mainnet,
transport: custom(window.ethereum),
});
// Requests the user's explicit permission via a wallet popup —
// nothing is accessible before the user approves this request.
const [address] = await walletClient.requestAddresses();
console.log("connected address:", address);
return { walletClient, address };
}
This code is illustrative browser-environment code (it depends on window.ethereum, which only exists in a browser with an installed wallet extension) and cannot be run in this book's usual Node.js verification environment. The pattern itself, though, is exactly what libraries like viem and wagmi (see wagmi) wrap into more ergonomic hooks and utilities.
What "connecting" actually grants, and doesn't¶
Connecting a wallet to a dapp grants that dapp visibility into the connected account's address (and, implicitly, its public on-chain activity and balances, since those are publicly readable regardless, see Reading Blockchain State) and the ability to request signatures and transactions. It does not grant the dapp any ability to sign or send anything without the wallet separately prompting the user to explicitly approve each individual action. Connection and authorization-per-action are two distinct steps, and a well-behaved wallet always requires the second even after the first has already happened.
Why this distinction matters for security¶
This is precisely the gap phishing attacks exploit: a malicious site can request a wallet connection (a low-stakes, often reflexively approved action) and then present a disguised transaction or signature request (one that looks routine but actually authorizes something harmful (an unlimited token approval, see Approval Attacks, or a malicious signature, see Malicious Signatures)) relying on users not carefully reading what they're actually approving at that second, separate step.
Common misconceptions¶
A wallet connection is not persistent or automatically re-granted across browser sessions in every implementation, many wallets and dapps re-request or re-confirm connection state, and a user can typically revoke a site's connection permission at any time through their wallet's own settings, independent of anything the dapp itself does.
Connecting a wallet does not, by itself, reveal a user's private key to the dapp in any well-implemented wallet. The entire EIP-1193 model is specifically designed so the dapp only ever receives public information (the address) and signed outputs (signatures, transaction hashes), never the key material used to produce them.
Further reading¶
← Previous: RPC Providers · Back to Building Web3 Applications · Next: WalletConnect →