A library for Solana Kit that helps you build composable Solana clients with a modular plugin system.
- ✨ All-in-one bundle plugins for production, local development, and local testing.
- ✨ Modular plugin system to compose custom clients by combining granular plugins.
- ✨ Default transaction planning and execution logic built-in, just call
client.sendTransaction(myInstructions). - ✨ Various useful plugins for RPC connectivity, payer management, SOL airdrops, LiteSVM support and more.
Use the solanaRpc bundle plugin to set up a full Solana RPC client with transaction planning and execution.
pnpm install @solana/kit @solana/kit-plugin-rpc @solana/kit-plugin-payerimport { createClient } from '@solana/kit';
import { solanaRpc } from '@solana/kit-plugin-rpc';
import { payer } from '@solana/kit-plugin-payer';
const client = createClient()
.use(payer(myProductionSigner))
.use(solanaRpc({ rpcUrl: 'https://api.mainnet-beta.solana.com' }));
await client.sendTransaction([myInstruction]);For mainnet type safety (preventing accidental use of devnet-only features like airdrops), use solanaMainnetRpc instead.
See all features and configuration options.
Use solanaLocalRpc which defaults to http://127.0.0.1:8899 and includes airdrop support.
pnpm install @solana/kit @solana/kit-plugin-rpc @solana/kit-plugin-payerimport { createClient } from '@solana/kit';
import { solanaLocalRpc } from '@solana/kit-plugin-rpc';
import { payerFromFile } from '@solana/kit-plugin-payer';
const client = createClient().use(payerFromFile('~/.config/solana/id.json')).use(solanaLocalRpc());
await client.sendTransaction([myInstruction]);For devnet, use solanaDevnetRpc which defaults to https://api.devnet.solana.com and also includes airdrop support.
See all features and configuration options.
Use the litesvm bundle plugin for fast local blockchain simulation without a network connection.
pnpm install @solana/kit @solana/kit-plugin-litesvm @solana/kit-plugin-payerimport { createClient, lamports } from '@solana/kit';
import { litesvm } from '@solana/kit-plugin-litesvm';
import { generatedPayer } from '@solana/kit-plugin-payer';
const client = await createClient().use(generatedPayer()).use(litesvm());
// Fund the payer
await client.airdrop(client.payer.address, lamports(10_000_000_000n));
// Set up test environment
client.svm.setAccount(myTestAccount);
client.svm.addProgramFromFile(myProgramAddress, 'program.so');
// Execute transactions locally
await client.sendTransaction([myInstruction]);See all features and configuration options.
The bundle plugins above are built from smaller, granular plugins. You can compose these granular plugins directly to build a custom setup tailored to your needs.
import { createClient } from '@solana/kit';
import {
solanaRpcConnection,
solanaRpcSubscriptionsConnection,
rpcAirdrop,
rpcTransactionPlanner,
rpcTransactionPlanExecutor,
} from '@solana/kit-plugin-rpc';
import { payerFromFile } from '@solana/kit-plugin-payer';
import { planAndSendTransactions } from '@solana/kit-plugin-instruction-plan';
const client = await createClient()
.use(payerFromFile('path/to/keypair.json')) // Adds `client.payer` using a local keypair file.
.use(solanaRpcConnection('https://api.devnet.solana.com')) // Adds `client.rpc`.
.use(solanaRpcSubscriptionsConnection('wss://api.devnet.solana.com')) // Adds `client.rpcSubscriptions`.
.use(rpcAirdrop()) // Adds `client.airdrop` to request SOL from faucets.
.use(rpcTransactionPlanner()) // Adds `client.transactionPlanner`.
.use(rpcTransactionPlanExecutor()) // Adds `client.transactionPlanExecutor`.
.use(planAndSendTransactions()); // Adds `client.planTransaction(s)` and `client.sendTransaction(s)`.Note that since core plugin interfaces are defined in @solana/kit itself, you're not limited to the plugins in this repo! You can use community plugins or even create your own.
This repo provides the following individual plugin packages. You can learn more about each package by following the links to their READMEs below.
| Package | Version | Description | Plugins |
|---|---|---|---|
@solana/kit-plugin-rpc |
Connect to Solana clusters | solanaRpc, solanaMainnetRpc, solanaDevnetRpc, solanaLocalRpc, rpcConnection, rpcSubscriptionsConnection, solanaRpcConnection, solanaRpcSubscriptionsConnection, rpcAirdrop, rpcGetMinimumBalance, rpcTransactionPlanner, rpcTransactionPlanExecutor |
|
@solana/kit-plugin-payer |
Manage transaction fee payers | payer, payerFromFile, generatedPayer, generatedPayerWithSol |
|
@solana/kit-plugin-litesvm |
LiteSVM support | litesvm, litesvmConnection, litesvmAirdrop, litesvmGetMinimumBalance, litesvmTransactionPlanner, litesvmTransactionPlanExecutor |
|
@solana/kit-plugin-instruction-plan |
Transaction planning and execution | transactionPlanner, transactionPlanExecutor, planAndSendTransactions |
| Package | Description | Plugins | Maintainers |
|---|---|---|---|
airdrop-token (unpublished) |
Create token mints, ATAs, and mint tokens for testing | airdropToken, testTokenPlugin |
@amilz |
local-validator (unpublished) |
Solana test validator lifecycle management (start, stop, restart) | localValidatorPlugin |
@amilz |
transaction-builder (unpublished) |
Transaction builder for constructing, signing, and sending transactions | transactionBuilderPlugin |
@amilz |
Do you know any? Please open a PR to add them here!
A plugin is defined in @solana/kit as a function that takes a client object and returns a new one (or a promise that resolves to a new one).
export type ClientPlugin<TInput extends object, TOutput extends Promise<object> | object> = (input: TInput) => TOutput;This means you can create plugins that extend clients like so:
function apple() {
return <T extends object>(client: T) => ({ ...client, fruit: 'apple' as const });
}While this works, it is recommended to use the extendClient utility from @solana/kit instead. Unlike a plain object spread, extendClient preserves property descriptors (such as getter functions and symbol-keyed properties) from the original client.
import { extendClient } from '@solana/kit';
function apple() {
return <T extends object>(client: T) => extendClient(client, { fruit: 'apple' as const });
}You may also add requirements to your plugins to ensure that other plugins have been installed beforehand. For instance, in the example below, the appleTart plugin requires that the apple plugin has already been installed on the client.
function appleTart() {
return <T extends { fruit: 'apple' }>(client: T) => extendClient(client, { dessert: 'appleTart' as const });
}
createClient().use(apple()).use(appleTart()); // Ok
createClient().use(appleTart()); // TypeScript errorPlugins can also be asynchronous if required and return promises that resolve to clients.
function magicFruit() {
return async <T extends object>(client: T) => {
const fruit = await fetchSomeMagicFruit();
return extendClient(client, { fruit });
};
}The use function on the client takes care of awaiting asynchronous plugins automatically, so you can use them seamlessly alongside synchronous ones and simply await the final client when required.
const client = await createClient().use(magicFruit()).use(apple());