Platforms and chains
Every adapter is evm, svm, sui, bitcoin, or polkadot. Platforms are tracked independently. Chains use CAIP-2 identifiers.
ChainPlatform
const CHAIN_PLATFORMS = ["evm", "svm", "sui", "bitcoin", "polkadot"] as const;
type ChainPlatform = (typeof CHAIN_PLATFORMS)[number];Every adapter carries a chainPlatform. butr tracks each platform
independently: a user can connect MetaMask (evm), Phantom Solana (svm),
Slush Sui (sui), and Phantom Bitcoin (bitcoin) simultaneously, and each
platform has its own selected wallet. A single multi-chain wallet (Phantom
EVM + Phantom SVM + Phantom BTC) appears as separate adapters with different
chainPlatform values; useDiscoveredWalletsByPlatform() groups them.
ChainBase: the CAIP-2 shape
butr only needs four fields per chain. It follows CAIP-2 and never inspects beyond these.
type ChainBase = {
id: string; // "eip155:1", "solana:mainnet"
name: string; // "Ethereum", "Solana Mainnet"
namespace: string; // "eip155", "solana"
reference: string; // "1", "mainnet"
};name is always the chain's name, never the wallet's. Adapters name a chain
with resolveChain(id, knownChains), which falls back to the CAIP-2 id as the
name for a chain outside the registry. You can extend ChainBase with
app-specific fields (logos, explorers, RPC URLs) via structural typing; butr
passes the whole object through untouched.
Chain registries
Every registry is plain data in @usebutr/core, so any transport names a chain
the same way, and an app that imports none of them bundles none of them. You
don't have to use them: any ChainBase-shaped object works.
| Export | Contents |
|---|---|
EVM_CHAINS, EVM_CHAINS_LIST | Ethereum, Sepolia, Arbitrum One, Optimism, Base, Polygon, BNB Smart Chain. |
SVM_CHAINS, SVM_CHAINS_LIST | Solana mainnet / testnet / devnet. |
SUI_CHAINS, SUI_CHAINS_LIST | Sui mainnet / testnet / devnet / localnet. |
BITCOIN_CHAINS, BITCOIN_CHAINS_LIST | Bitcoin mainnet / testnet / signet. |
POLKADOT_CHAINS, POLKADOT_CHAINS_LIST | Polkadot, Kusama, Westend, Paseo. |
CHAINS_BY_PLATFORM | Every *_CHAINS_LIST, keyed by platform. |
CHAINS_BY_PLATFORM[wallet.connector.chainPlatform] is the idiomatic way to
get the right chain list for a connected wallet; see
multi-chain switching. A single-platform app imports
only its own list, such as EVM_CHAINS_LIST.
Platform differences
| EVM | SVM | Sui | Bitcoin | Polkadot | |
|---|---|---|---|---|---|
| Discovery | EIP-6963 (+ injected fallback) | Wallet Standard | Wallet Standard | Wallet Standard (+ injected fallback) | injectedWeb3 (+ Wallet Standard) |
| Accounts exposed | one or many (MetaMask multi-account) | all at once | all at once | one or many (per address format) | all at once (SS58-encoded) |
requestAccounts | wallet_requestPermissions picker | absent: no picker | absent: no picker | absent: no picker | absent: the wallet lists every account |
| Chain switching | real, via wallet_switchEthereumChain | re-points butr's view; chain routes a call | re-points butr's view; chain routes a call | Wallet Standard routes per call; injected wallets switch their network or reject | none on injectedWeb3; an account can pin its network via genesisHash |
| Signature format | EIP-191 personal_sign, input echoed | Solana format, may re-encode signedMessage | sui:signPersonalMessage | Bitcoin Signed Message; not interchangeable | signRaw over raw bytes |
sendTx input | EvmTransactionRequest | serialized transaction bytes | Transaction, its JSON, or BCS bytes | BitcoinTransfer ({ amount, recipient }, satoshis) | no sendTx: build extrinsics with polkadot-api |
| Sign without send | — | signTransaction → signed bytes | signTransaction → { bytes, signature } | signTransaction on PSBT bytes → signed PSBT | through getSigner() |
Source: packages/core/src/types/chain.ts, packages/core/src/types/platform.ts,
packages/core/src/chains.ts.