Standards compliance
How butr follows the Wallet Standard, EIP-6963, and EIP-1193: what is covered, and the deliberate edges.
butr discovers wallets through the published standards only. There is no
window.solana poking, no isMetaMask branching, no hardcoded wallet list.
Solana goes through the Wallet
Standard; EVM goes through
EIP-6963 over
EIP-1193. Every chain identifier is
CAIP-2 (solana:mainnet,
eip155:1).
Discovery is spec-only
- Wallet Standard (SVM, Sui, Bitcoin, Polkadot):
@usebutr/svm,@usebutr/sui,@usebutr/bitcoin, and@usebutr/polkadotlazily import@wallet-standard/appand usegetWallets():get()for wallets already present,on('register')for ones that load later, andon('unregister')to drop a wallet whose extension is disabled or removed mid-session. - EVM:
@usebutr/evmadds theeip6963:announceProviderlistener before dispatchingeip6963:requestProvider, so wallets that announced early aren't missed. Providers are de-duplicated byinfo.rdns. - Legacy fallback: a raw
window.ethereumis adopted only after a 150 ms settle window in which no EIP-6963 wallet answered. It suppresses itself the moment a compliant wallet is present, so it never double-lists.
What's covered
| Capability | Wallet Standard (SVM) | EIP-6963 / EIP-1193 (EVM) |
|---|---|---|
| Discovery | getWallets() get + register + unregister | requestProvider / announceProvider, dedupe by rdns |
| Connect | standard:connect | eth_requestAccounts |
| Silent reconnect | standard:connect({ silent: true }) on hydration | eth_accounts probe on hydration |
| Disconnect | standard:disconnect | wallet_revokePermissions (best-effort) |
| Account / chain events | standard:events change (accounts and chains) | accountsChanged, chainChanged, connect, disconnect |
| Sign message | solana:signMessage | personal_sign |
| Send transaction | solana:signAndSendTransaction | eth_sendTransaction |
| Sign-only | solana:signTransaction (consumer broadcasts) | — |
| Sign in | solana:signIn (SIWS) | — |
| Switch chain | per-call chain + local state | wallet_switchEthereumChain |
Eager hydration asks the wallet to restore already-authorized accounts
without prompting: Wallet Standard's silent input, the eth_accounts
read on EIP-1193. A wallet that can't reconnect silently fails the restore
cleanly instead of popping a connect dialog on page load. Hardware (Ledger)
and WalletConnect adapters, which are inherently interactive, reject a silent
reconnect rather than show UI.
Connector errors are normalized once, centrally: EIP-1193 codes 4001
(user rejected), -32002 (request pending), and 4100 / 4900 / 4901
(unauthorized / disconnected) map to a ConnectionError so consumers branch
on kind instead of regexing messages. See errors.
Feature presence drives capabilities: a Wallet
Standard adapter defines signMessage, signTransaction, signIn, and
sendTx only when the wallet advertises the underlying feature, so an adapter
never claims more than the wallet supports.
Deliberate edges
These are intentional boundaries, not gaps:
- No RPC. Wallet Standard adapters have no
getBalanceorgetTransactionReceipt: butr hands you a signer and stays out of the RPC business. Wrap your own client (@solana/kit,@mysten/sui, …). EVM adapters read both through the wallet's own EIP-1193 provider. solana:signTransactionis sign-only. butr returns the signed bytes; you broadcast them, because butr ships no RPC.signTransactionexists only when the wallet advertises the feature.- No wallet-side account switch. Neither spec has a "use address X" RPC,
so the user picks the active account in the wallet's own UI and butr mirrors
it via the change event.
setAccount(id, account)changes which exposed account butr treats as active; pass it as{ account }to sign with it. - Features are read once. Discovery builds an adapter from the features the wallet advertises at registration. A wallet that swaps its feature set at runtime keeps the adapter's original methods until the page reloads.
- EIP-1193
message/eth_subscribeis a non-goal. Subscription push streams are a consumer concern; butr surfaces the raw provider viagetSigner()for that.
Source: packages/svm/src (Wallet Standard), packages/evm/src (EIP-6963 / EIP-1193),
packages/core/src/store/wallet-manager.ts (silent reconnect),
packages/core/src/types/errors.ts (error mapping). Behaviour is exercised by the package test
suites and the runnable apps/demo-with-* demos.