gill
The ergonomic SDK on top of @solana/kit, the path the Solana Foundation recommends for most apps.
gill wraps @solana/kit with friendlier client and transaction builders. It
is the path the Solana Foundation recommends for most app developers writing
RPC and transaction code. butr owns the wallet; gill owns the RPC and the
transaction builder; the wallet's Wallet Standard features own signing.
Get the wallet and read state
The wallet and its Wallet Standard handle, walletStd, come from butr exactly
as in the @solana/kit integration;
the snippets below run once both are set. createSolanaClient resolves a
moniker to an RPC endpoint:
import { address, createSolanaClient } from "gill";
const { rpc } = createSolanaClient({ urlOrMoniker: "devnet" });
const addr = address(wallet.account.walletAddress);
const { value } = await rpc.getBalance(addr).send();
const sol = `${Number(value) / 1_000_000_000} SOL`;Build and send a transaction
The wallet signs, not gill, so the fee payer is a no-op signer over butr's
connected address. gill's program helpers stay fully typed; compile to wire
bytes and hand them to solana:signAndSendTransaction:
import { base64ToBytes, bytesToBase58 } from "@usebutr/core";
import type { SolanaSignAndSendTransactionFeature } from "@usebutr/svm";
import { findAccount, getFeature } from "@usebutr/wallet-standard-shared";
import {
compileTransaction,
createNoopSigner,
createTransaction,
getBase64EncodedWireTransaction,
} from "gill";
import { getTransferSolInstruction } from "gill/programs";
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
const feePayer = createNoopSigner(addr);
const tx = createTransaction({
feePayer,
instructions: [
getTransferSolInstruction({ amount: 0, destination: BURN_ADDRESS, source: feePayer }),
],
latestBlockhash,
version: 0,
});
const transaction = base64ToBytes(getBase64EncodedWireTransaction(compileTransaction(tx)));
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,
});
const signature = output === undefined ? null : bytesToBase58(output.signature);Message signing uses solana:signMessage exactly as in the
@solana/kit integration. For a
shorter path through butr's adapter, wallet.connector.sendTx(transaction, { chain: SVM_CHAINS.devnet })
does the same when the wallet defines it.
gill re-exports @solana/kit, so kit primitives (createNoopSigner, compileTransaction,
getBase64EncodedWireTransaction) come from the same gill entry point; you do not need a
separate @solana/kit dependency.
Source: apps/demo-with-gill/src/app.tsx in the butr
repository. Targets Solana
devnet. Run pnpm dev --filter=demo-with-gill → https://usebutr.demo-with-gill.localhost.