Solana Kit Development Guide
A comprehensive guide for building Solana applications with @solana/kit - the modern, tree-shakeable, zero-dependency JavaScript SDK from Anza.
Overview
Solana Kit (formerly web3.js 2.0) is a complete rewrite of the Solana JavaScript SDK with:
Tree-shakeable: Only ship code you use (-78% bundle size) Zero dependencies: No third-party packages Functional design: Composable, no classes 10x faster crypto: Native Ed25519 support TypeScript-first: Full type safety Quick Start Installation npm install @solana/kit
For specific program interactions:
npm install @solana-program/system @solana-program/token
Minimal Example import { createSolanaRpc, createSolanaRpcSubscriptions, generateKeyPairSigner, lamports, pipe, createTransactionMessage, setTransactionMessageFeePayer, setTransactionMessageLifetimeUsingBlockhash, appendTransactionMessageInstruction, signTransactionMessageWithSigners, sendAndConfirmTransactionFactory, getSignatureFromTransaction, } from "@solana/kit"; import { getTransferSolInstruction } from "@solana-program/system";
const LAMPORTS_PER_SOL = BigInt(1_000_000_000);
async function transferSol() { // 1. Connect to RPC const rpc = createSolanaRpc("https://api.devnet.solana.com"); const rpcSubscriptions = createSolanaRpcSubscriptions("wss://api.devnet.solana.com");
// 2. Create signers const sender = await generateKeyPairSigner(); const recipient = await generateKeyPairSigner();
// 3. Get blockhash const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
// 4. Build transaction with pipe const transactionMessage = pipe( createTransactionMessage({ version: 0 }), (tx) => setTransactionMessageFeePayer(sender.address, tx), (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), (tx) => appendTransactionMessageInstruction( getTransferSolInstruction({ amount: lamports(LAMPORTS_PER_SOL / BigInt(10)), // 0.1 SOL destination: recipient.address, source: sender, }), tx ) );
// 5. Sign const signedTx = await signTransactionMessageWithSigners(transactionMessage);
// 6. Send and confirm const sendAndConfirm = sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions }); await sendAndConfirm(signedTx, { commitment: "confirmed" });
console.log("Signature:", getSignatureFromTransaction(signedTx)); }
Core Concepts 1. RPC Connections
Kit separates HTTP and WebSocket connections:
import { createSolanaRpc, createSolanaRpcSubscriptions } from "@solana/kit";
// HTTP for requests const rpc = createSolanaRpc("https://api.devnet.solana.com");
// WebSocket for subscriptions const rpcSubscriptions = createSolanaRpcSubscriptions("wss://api.devnet.solana.com");
// Make RPC calls const slot = await rpc.getSlot().send(); const balance = await rpc.getBalance(address).send(); const { value: blockhash } = await rpc.getLatestBlockhash().send();
- Signers
Kit uses signer interfaces instead of keypairs directly:
import { generateKeyPairSigner, createKeyPairSignerFromBytes, address, } from "@solana/kit";
// Generate new signer const signer = await generateKeyPairSigner(); console.log("Address:", signer.address);
// From existing secret key (Uint8Array) const existing = await createKeyPairSignerFromBytes(secretKeyBytes);
// Create address from string const addr = address("11111111111111111111111111111111");
- Transaction Building with Pipe
Kit uses functional composition via pipe:
import { pipe, createTransactionMessage, setTransactionMessageFeePayer, setTransactionMessageLifetimeUsingBlockhash, appendTransactionMessageInstruction, appendTransactionMessageInstructions, prependTransactionMessageInstructions, } from "@solana/kit";
const tx = pipe( createTransactionMessage({ version: 0 }), // Create v0 message (tx) => setTransactionMessageFeePayer(payer.address, tx), // Set fee payer (tx) => setTransactionMessageLifetimeUsingBlockhash(blockhash, tx), // Set lifetime (tx) => appendTransactionMessageInstruction(instruction1, tx), // Add instruction (tx) => appendTransactionMessageInstructions([instruction2, instruction3], tx), // Add multiple );
- Signing Transactions import { signTransactionMessageWithSigners, partiallySignTransactionMessageWithSigners, getSignatureFromTransaction, } from "@solana/kit";
// Sign with all signers in the transaction const signedTx = await signTransactionMessageWithSigners(transactionMessage);
// Partial signing (for multisig) const partiallySignedTx = await partiallySignTransactionMessageWithSigners( transactionMessage );
// Get signature before sending const signature = getSignatureFromTransaction(signedTx);
- Sending Transactions import { sendAndConfirmTransactionFactory, sendTransactionWithoutConfirmingFactory, getBase64EncodedWireTransaction, } from "@solana/kit";
// Send with confirmation (recommended) const sendAndConfirm = sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions }); await sendAndConfirm(signedTx, { commitment: "confirmed" });
// Send without waiting for confirmation const send = sendTransactionWithoutConfirmingFactory({ rpc }); await send(signedTx, { commitment: "confirmed" });
// Manual encoding (low-level) const encoded = getBase64EncodedWireTransaction(signedTx); await rpc.sendTransaction(encoded, { encoding: "base64" }).send();
- Fetching Accounts import { fetchEncodedAccount, fetchEncodedAccounts, assertAccountExists, } from "@solana/kit";
// Fetch single account const account = await fetchEncodedAccount(rpc, address); if (account.exists) { console.log("Lamports:", account.lamports); console.log("Owner:", account.programAddress); console.log("Data:", account.data); }
// Fetch multiple accounts const accounts = await fetchEncodedAccounts(rpc, [addr1, addr2, addr3]);
// Assert account exists (throws if not) assertAccountExists(account);
Package Reference Core Package Import Description @solana/kit Main package - includes everything below Individual Packages Package Purpose @solana/rpc RPC client creation @solana/rpc-subscriptions WebSocket subscriptions @solana/signers Signing interfaces @solana/addresses Address utilities @solana/keys Key generation @solana/transactions Transaction compilation @solana/transaction-messages Message building @solana/accounts Account fetching @solana/codecs Data encoding/decoding @solana/errors Error handling Program Packages Package Program @solana-program/system System Program @solana-program/token SPL Token @solana-program/token-2022 Token Extensions @solana-program/memo Memo Program @solana-program/compute-budget Compute Budget @solana-program/address-lookup-table Lookup Tables Common Patterns Pattern 1: Helper Function for Send & Confirm import { signTransactionMessageWithSigners, sendAndConfirmTransactionFactory, getSignatureFromTransaction, CompilableTransactionMessage, TransactionMessageWithBlockhashLifetime, Commitment, } from "@solana/kit";
function createTransactionSender(rpc: Rpc, rpcSubscriptions: RpcSubscriptions) { const sendAndConfirm = sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions });
return async ( txMessage: CompilableTransactionMessage & TransactionMessageWithBlockhashLifetime, commitment: Commitment = "confirmed" ) => { const signedTx = await signTransactionMessageWithSigners(txMessage); await sendAndConfirm(signedTx, { commitment, skipPreflight: false }); return getSignatureFromTransaction(signedTx); }; }
// Usage const sendTx = createTransactionSender(rpc, rpcSubscriptions); const signature = await sendTx(transactionMessage);
Pattern 2: Reusable Transaction Builder import { pipe, createTransactionMessage, setTransactionMessageFeePayer, setTransactionMessageLifetimeUsingBlockhash, appendTransactionMessageInstructions, IInstruction, } from "@solana/kit";
async function buildTransaction( rpc: Rpc, feePayer: Address, instructions: IInstruction[] ) { const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
return pipe( createTransactionMessage({ version: 0 }), (tx) => setTransactionMessageFeePayer(feePayer, tx), (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), (tx) => appendTransactionMessageInstructions(instructions, tx) ); }
Pattern 3: Add Compute Budget import { getSetComputeUnitLimitInstruction, getSetComputeUnitPriceInstruction, } from "@solana-program/compute-budget";
const computeInstructions = [ getSetComputeUnitLimitInstruction({ units: 200_000 }), getSetComputeUnitPriceInstruction({ microLamports: 1000n }), ];
const tx = pipe( createTransactionMessage({ version: 0 }), (tx) => setTransactionMessageFeePayer(payer.address, tx), (tx) => setTransactionMessageLifetimeUsingBlockhash(blockhash, tx), (tx) => prependTransactionMessageInstructions(computeInstructions, tx), // Prepend! (tx) => appendTransactionMessageInstruction(mainInstruction, tx), );
Pattern 4: Versioned Transactions with Lookup Tables import { setTransactionMessageAddressLookupTable, } from "@solana/kit";
// Fetch lookup table const lookupTableAccount = await fetchAddressLookupTable(rpc, lookupTableAddress);
const tx = pipe( createTransactionMessage({ version: 0 }), (tx) => setTransactionMessageFeePayer(payer.address, tx), (tx) => setTransactionMessageLifetimeUsingBlockhash(blockhash, tx), (tx) => setTransactionMessageAddressLookupTable(tx, lookupTableAccount), (tx) => appendTransactionMessageInstructions(instructions, tx), );
Type Safety
Kit provides comprehensive TypeScript types:
import type { Address, Signature, Lamports, TransactionMessage, Rpc, RpcSubscriptions, KeyPairSigner, } from "@solana/kit";
// Addresses are branded strings const addr: Address = address("11111111111111111111111111111111");
// Lamports are branded bigints const amount: Lamports = lamports(1_000_000_000n);
// Type-safe RPC responses const response = await rpc.getBalance(addr).send(); // response.value is typed as Lamports
Performance Tips
Import only what you need - Kit is tree-shakeable
// Good - only imports what's used import { createSolanaRpc, generateKeyPairSigner } from "@solana/kit";
// Also good - use subpackages for smaller bundles import { createSolanaRpc } from "@solana/rpc"; import { generateKeyPairSigner } from "@solana/signers";
Reuse RPC connections - Don't create per request
// Create once const rpc = createSolanaRpc(endpoint);
// Reuse everywhere await rpc.getBalance(addr1).send(); await rpc.getBalance(addr2).send();
Batch requests when possible
// Fetch multiple accounts in one request const accounts = await fetchEncodedAccounts(rpc, [addr1, addr2, addr3]);
Use skipPreflight carefully - Faster but no simulation
await sendAndConfirm(tx, { commitment: "confirmed", skipPreflight: true });
Error Handling import { isSolanaError, SOLANA_ERROR__TRANSACTION_ERROR__INSUFFICIENT_FUNDS } from "@solana/errors";
try { await sendAndConfirm(signedTx, { commitment: "confirmed" }); } catch (error) { if (isSolanaError(error, SOLANA_ERROR__TRANSACTION_ERROR__INSUFFICIENT_FUNDS)) { console.error("Not enough SOL for transaction"); } else if (isSolanaError(error)) { console.error("Solana error:", error.context); } else { throw error; } }
Migration from web3.js 1.x
See the separate migration skill or use @solana/compat for interoperability:
import { fromLegacyPublicKey, fromLegacyKeypair, fromVersionedTransaction, fromLegacyTransactionInstruction, } from "@solana/compat";
// Convert legacy PublicKey to Kit Address const address = fromLegacyPublicKey(legacyPublicKey);
// Convert legacy Keypair to Kit CryptoKeyPair (async) const keyPair = await fromLegacyKeypair(legacyKeypair);
// Convert legacy VersionedTransaction to Kit Transaction const kitTransaction = fromVersionedTransaction(legacyVersionedTx);
// Convert legacy TransactionInstruction to Kit Instruction const kitInstruction = fromLegacyTransactionInstruction(legacyInstruction);
Note: The compat package converts FROM legacy TO Kit types. For reverse conversion, you may need to manually construct legacy objects.
Performance Benchmarks
Kit delivers significant performance improvements over web3.js 1.x:
Metric web3.js 1.x @solana/kit Improvement Keypair Generation ~50ms ~5ms 10x faster Transaction Signing ~20ms ~2ms 10x faster Bundle Size 311KB 226KB 26% smaller Confirmation Latency ~400ms ~200ms ~200ms faster
Benchmarks from Triton One's Ping Thing service and Solana Explorer testing
Why It's Faster Native Ed25519: Uses browser/runtime native crypto APIs Zero Dependencies: No third-party library overhead Tree-Shakeable: Only imports code you use No Classes: Functional design enables better optimization Resources Official Documentation GitHub Repository Examples Triton Blog - Kit Introduction Skill Structure solana-kit/ ├── SKILL.md # This file ├── resources/ │ ├── packages-reference.md # Complete package documentation │ └── api-quick-reference.md # Quick lookup table ├── examples/ │ ├── transfer-sol/ # Basic SOL transfer │ ├── create-token/ # SPL token creation │ ├── fetch-accounts/ # Account fetching & decoding │ └── subscriptions/ # Real-time subscriptions ├── templates/ │ └── project-template.ts # Copy-paste starter └── docs/ ├── advanced-patterns.md # Complex patterns └── troubleshooting.md # Common issues