Wallet

Dispatch

packages/wallet/src/utils/dispatch.ts routes every incoming 1193 request against four strict allowlists. A method outside all four returns error 4200 (unsupported method).

Tier 1 — Wallet state (cached, no RPC, no popup)

MethodReturnsEIP
eth_chainIdhex chain IDcore
eth_accountscurrently exposed accountscore
net_versiondecimal chain ID (legacy)legacy
wallet_getPermissionsPermission[]2255
wallet_getCapabilitiesCapabilities5792
wallet_switchEthereumChainnull3326

These read wallet-managed state. wallet_switchEthereumChain mutates the state (and may pop the select-chain view on first switch) but the typical path is cached.

Tier 2 — Chain reads (forwarded to RPC)

The wallet acts as a passthrough — it forwards the request to the active chain’s HTTP RPC and returns the response unmodified. No signature, no popup, no wallet-held secrets.

MethodPurpose
eth_blockNumberLatest block.
eth_blobBaseFeeEIP-4844 base blob fee.
eth_callRead-only execution.
eth_getBalanceNative balance.
eth_getCodeDeployed bytecode.
eth_getStorageAtRaw storage.
eth_estimateGasGas estimation.
eth_gasPriceLegacy gas price.
eth_maxPriorityFeePerGasEIP-1559 tip.
eth_feeHistoryHistorical fees.
eth_getTransactionReceiptReceipt by hash.
eth_getTransactionByHashTx by hash.
eth_getTransactionByBlockHashAndIndexTx by block + index.
eth_getTransactionByBlockNumberAndIndexTx by block-number + index.
eth_getTransactionCountNonce.
eth_getBlockByNumberBlock by number.
eth_getBlockByHashBlock by hash.
eth_getBlockTransactionCountByHashTx count in block (by hash).
eth_getBlockTransactionCountByNumberTx count in block (by number).
eth_getLogsLog filter query.
eth_getProofStorage proof.
eth_protocolVersionNode protocol version.
eth_syncingSync status.
net_listeningNetwork connectivity.
net_peerCountPeer count.
web3_clientVersionNode version string.
web3_sha3Keccak via the node (rarely used by clients).

A dapp that calls only these never opens the wallet popup.

Tier 3 — Signables (popup confirmation required)

These open the popup and wait for the user. They are the only methods that touch the mnemonic. Each maps to a view:

MethodViewEIP
eth_requestAccountsconnect + select-account1102
wallet_requestPermissionsconnect (re-used)2255
eth_sendTransactionsendcore (path 1)
eth_signTransactionsend (path-2 variant)core (path 2)
personal_signpersonal-sign191
eth_signsign (gated; off by default)legacy
eth_signTypedData_v4sign-typed-data712
wallet_addEthereumChainadd-chain3085
wallet_sendCallssend-calls5792
wallet_sendSetCodeTransactionauthorize-delegation7702

The dispatcher posts an envelope to the popup; the popup looks up the view by method name; the view renders, awaits user input, signs (if needed), posts the result back.

If the user closes the popup without confirming, the dispatcher returns error 4001 (user rejected).

Tier 4 — Wallet internal (storage reads)

MethodSourceEIP
wallet_getCallsStatusextension storage5792

The wallet tracks batches it submitted via wallet_sendCalls and serves their status from a local registry. Not a chain read, not a popup — the wallet has the answer locally.

Why the four-tier split exists

Auditability. Reading the four allowlists in dispatch.ts is the same as reading the wallet’s complete public surface. There is no fifth list, no hidden method, no debug path.

Security boundary. Tier 3 is the only place secrets exist. Confining secret access to one tier makes the audit surface small.

Performance. Tier 1 returns from in-memory cache. Tier 2 doesn’t open a popup. Tier 3 opens a popup. Tier 4 reads local storage. The cost of each tier is bounded by its routing target.

Standards-first interop. Any standards-compliant wallet’s tier 3 is the part that varies (UI, policy). Tiers 1, 2, 4 are essentially uniform across wallets. The split makes that explicit.

Outside all four

A request like evm_mine (the Hardhat test cheat code) is outside every tier. The dispatcher returns 4200 (unsupported method). The dapp must catch the error and fall back appropriately.

See also