butr
Guides

EVM-only setup

Ship @usebutr/react + @usebutr/evm with no @usebutr/svm or @usebutr/wallets in the bundle.

Pick this path when your app only touches EVM chains: a viem, wagmi, or ethers app with no Solana surface. You pass discoverEvmAdapters from @usebutr/evm as the source instead of autoDiscovery() from @usebutr/wallets, and the Solana packages never enter your bundle. Nothing else about the provider or hooks changes.

Install

@usebutr/react gives you the provider and hooks, @usebutr/evm does EIP-6963 discovery, and @usebutr/core has the config type and chain registries. No @usebutr/wallets, no @usebutr/svm.

npm install @usebutr/react @usebutr/evm @usebutr/core zustand

Provider

discoverEvmAdapters is a wallet source as-is, so it goes straight into sources. Define the config at module scope; the provider reads it once.

src/wallet-provider.tsx
import type { WalletManagerConfig } from "@usebutr/core";
import { discoverEvmAdapters } from "@usebutr/evm";
import { WalletManagerProvider } from "@usebutr/react";
import type { ReactNode } from "react";

// EVM-only: no @usebutr/svm or @usebutr/wallets in the bundle.
const config: WalletManagerConfig = {
  sources: [discoverEvmAdapters],
  storageKeyPrefix: "butr-demo",
};

const WalletProvider = ({ children }: { children: ReactNode }) => (
  <WalletManagerProvider config={config}>{children}</WalletManagerProvider>
);

export { WalletProvider };

demo-with-viem, demo-with-wagmi, demo-next, and demo-tanstack-start all use this pattern. To also catch wallets that only inject window.ethereum, add discoverInjectedAdapter as a second source; see caveats.

On Next.js the same module needs a "use client" directive at the top; see Next.js (App Router).

The bundle guarantee

The isolation is not a flag; it is your import graph. @usebutr/evm depends only on @usebutr/core; it carries no Wallet Standard or Solana code. autoDiscovery() lives in @usebutr/wallets, which depends on every platform package, so importing it would pull Solana in. Here you import discoverEvmAdapters from @usebutr/evm directly, so @usebutr/svm and @usebutr/wallets are never referenced and the bundler tree-shakes them out ("sideEffects": false is set on every package). See Platforms.

Chains

Every chain registry lives in @usebutr/core as plain data, so importing only the EVM list bundles only the EVM list:

import { EVM_CHAINS, EVM_CHAINS_LIST } from "@usebutr/core";

EVM_CHAINS is keyed (ethereum, sepolia, arbitrum, optimism, base, polygon, bsc) and EVM_CHAINS_LIST is the same as an array. butr names a wallet's chain from this list, and a chain outside it by its CAIP-2 id. Use it for your chain-switcher UI and for sendTx's chain option; switchChain accepts any ChainBase.

Capabilities and caveats

@usebutr/evm discovers wallets via EIP-6963, the standard where wallets announce themselves through window events instead of fighting over window.ethereum. Some regional or legacy wallets only expose a raw EIP-1193 window.ethereum and never announce; discoverInjectedAdapter (also from @usebutr/evm) is the last-resort fallback for those. Passed as a separate source, it adopts window.ethereum after a short settle window whether or not EIP-6963 found anything; autoDiscovery(["evm"]) pairs the two so the fallback stays quiet once a standard wallet announced.

Every EVM adapter defines requestAccounts, switchChain, signMessage, sendTx, getBalance, getTransactionReceipt, and subscribe, because EIP-1193 has a call for each; the wallet can still reject one it doesn't implement. switchChain and sendTx's chain option switch the wallet for real via wallet_switchEthereumChain. getSigner() resolves { kind: "eip1193", provider } for viem, ethers, or wagmi.

Next steps

Source: apps/demo-with-viem/src/wallet-provider.tsx, apps/demo-with-wagmi/src/wallet-provider.tsx, and apps/demo-next/src/wallet-provider.tsx in the butr repository.