butr
Integrations

viem

Wrap butr's EIP-1193 provider with viem's createWalletClient.

butr owns wallet discovery and connection state. viem owns chain abstraction and RPC. The bridge is one line: getSigner() on an EVM adapter returns the raw EIP-1193 provider — exactly what viem's custom() transport wants.

Wire the wallet client

import { createWalletClient, custom, type EIP1193Provider } from "viem";
import { sepolia } from "viem/chains";

useEffect(() => {
  let cancelled = false;
  void (async () => {
    const provider = (await wallet.connector.getSigner()) as EIP1193Provider;
    if (cancelled) {
      return;
    }
    setWalletClient(
      createWalletClient({
        account,
        chain: sepolia,
        transport: custom(provider),
      }),
    );
  })();
  return () => {
    cancelled = true;
  };
}, [account, wallet.connector]);

account is wallet.account.walletAddress cast to viem's Address.

Read, sign, send

Reads go through viem's own public client (its RPC, not the wallet's, so you don't double-spend the wallet on eth_getBalance):

const publicClient = createPublicClient({ chain: sepolia, transport: http() });
const wei = await publicClient.getBalance({ address: account });
const eth = `${formatEther(wei)} ETH`;

Signing and sending go through the wallet client (the real wallet):

const sig = await walletClient.signMessage({ account, message: "Hello from butr + viem" });

const hash = await walletClient.sendTransaction({
  account,
  chain: sepolia,
  to: BURN_ADDRESS,
  value: parseEther("0"),
});

The split is the whole point: butr handles discovery + connection; viem handles chain reads, signing, and tx submission against the provider butr exposes.

Source: apps/demo-with-viem/src/app.tsx in the butr repository. Targets Sepolia. Run pnpm dev --filter=demo-with-viemhttp://localhost:5175.

On this page