Pool, selection, and active
Three independent pieces of state — every connected wallet, the chosen wallet per platform, and the one wallet in focus now.
butr keeps three distinct things. Confusing them is the most common source of bugs, so they are kept separate on purpose.
Pool
Every connected wallet, keyed by connectorId.
// Map<string, ConnectedWallet>
const pool = usePool();
const wallets = useConnectedWallets(); // pool projected as an arrayA user can have many wallets in the pool at once (MetaMask + Phantom + WalletConnect). Adding or removing one never implicitly changes selection or active state.
Selection — per platform
A Map<ChainPlatform, connectorId>: which wallet is chosen for evm and which
for svm. The two are independent — selecting an EVM wallet does nothing to
SVM.
const selection = useSelection(); // Map<"evm" | "svm", string>
const evmWallet = useSelectedWallet("evm");
const isEvmConnected = useIsPlatformConnected("evm");
const setSelection = useSetSelection();
setSelection("evm", "metamask");Active — the one in focus
A single global activeConnectorId: the wallet the user is interacting with
right now. This is what most single-wallet UIs read.
const active = useActiveWallet(); // ConnectedWallet | undefined
const setActive = useSetActiveConnector();
setActive("phantom");How they relate
They are independent buckets. Setting selection does not change active; setting active does not change selection. A wallet can be selected for EVM and not be the active wallet at all.
- Single-wallet apps: read
useActiveWallet(), ignore selection. - Multi-chain apps that act on both platforms at once: read
useSelectedWallet("evm")anduseSelectedWallet("svm"). - A "make active" button in a multi-wallet list: call
useSetActiveConnector()(this is exactly what thedemo-vitereference does).
Stable accessors useGetWallet() and useGetSelectedWallet() return functions instead of
subscribing — use them inside callbacks and effect bodies to avoid re-renders.
Source: packages/react/src/hooks.ts, packages/core/src/store.