butr
Integrations

@solana/web3.js

Connection for reads, Transaction/SystemProgram builders, Wallet Standard features for signing.

Legacy. @solana/web3.js v1 is in maintenance mode. For new apps, prefer framework-kit (the recommended modern stack), gill (ergonomic, for most apps), or @solana/kit (low-level). This page is kept for apps still on v1.

butr discovers Solana Wallet Standard wallets and manages the connection. @solana/web3.js provides Connection for chain reads and the Transaction / SystemProgram builders; signing and sending go through the wallet's own Wallet Standard features.

Get the wallet

import { useSelectedWallet, useSigner } from "@usebutr/react";

const wallet = useSelectedWallet("svm");
const signer = useSigner(wallet);
// `null` for a WalletConnect or Ledger wallet, which resolve another kind.
const walletStd = signer.data?.kind === "wallet-standard" ? signer.data.wallet : null;

The snippets below run once wallet and walletStd are set.

Read chain state

import { Connection, PublicKey } from "@solana/web3.js";

const connection = new Connection("https://api.devnet.solana.com", "confirmed");
const publicKey = new PublicKey(wallet.account.walletAddress);
const lamports = await connection.getBalance(publicKey);

Sign a message

getFeature reads the feature off the wallet, typed by the shape @usebutr/svm exports; findAccount finds the Wallet Standard account for butr's address:

import type { SolanaSignMessageFeature } from "@usebutr/svm";
import { findAccount, getFeature } from "@usebutr/wallet-standard-shared";

const feature = getFeature<SolanaSignMessageFeature>(
  walletStd,
  "solana:signMessage",
  "signMessage",
);
const account = findAccount(walletStd.accounts, wallet.account.walletAddress);
if (feature === undefined || account === undefined) {
  throw new Error("This wallet cannot sign messages for the active account");
}
const [output] = await feature.signMessage({ account, message });
// output.signature: Uint8Array; verify against output.signedMessage

Build and send a transaction

Build with @solana/web3.js, then submit through solana:signAndSendTransaction. The chain input routes this one call to devnet without moving the wallet:

import { SystemProgram, Transaction } from "@solana/web3.js";
import type { SolanaSignAndSendTransactionFeature } from "@usebutr/svm";

const { blockhash } = await connection.getLatestBlockhash();
const tx = new Transaction().add(
  SystemProgram.transfer({ fromPubkey: publicKey, lamports: 0, toPubkey: BURN_ADDRESS }),
);
tx.recentBlockhash = blockhash;
tx.feePayer = publicKey;

const feature = getFeature<SolanaSignAndSendTransactionFeature>(
  walletStd,
  "solana:signAndSendTransaction",
  "signAndSendTransaction",
);
const account = findAccount(walletStd.accounts, wallet.account.walletAddress);
if (feature === undefined || account === undefined) {
  throw new Error("This wallet cannot send transactions for the active account");
}
const [output] = await feature.signAndSendTransaction({
  account,
  chain: "solana:devnet",
  transaction: new Uint8Array(tx.serialize({ requireAllSignatures: false })),
});

Wallets advertise Wallet Standard features unevenly, which is why getFeature can return undefined. butr's adapter mirrors that: wallet.connector.sendTx and .signMessage exist only when the wallet has the feature, so gate buttons on them.

Source: apps/demo-with-solana-web3js/src/app.tsx in the butr repository. Targets Solana devnet. Run pnpm dev --filter=demo-with-solana-web3js → https://usebutr.demo-with-solana-web3js.localhost.