npm.io
7.0.0 • Published 3 weeks ago

@solana/plugin-interfaces

Licence
MIT
Version
7.0.0
Deps
7
Size
49 kB
Vulns
0
Weekly
0
Stars
693

npm npm-downloads
code-style-prettier

@solana/plugin-interfaces

This package defines common TypeScript interfaces for features that Kit plugins can provide or require. It can be used standalone, but it is also exported as part of Kit @solana/kit.

Overview

When building Solana applications, different environments require different capabilities. A browser wallet might support signing but not RPC calls. A testing environment might support airdrops. A full client might support everything.

These interfaces serve two purposes:

  • Plugins can provide capabilities: A plugin can implement these interfaces to add features to a client (e.g., a plugin that adds airdrop support implements ClientWithAirdrop).
  • Plugins can require capabilities: A plugin can declare which capabilities it needs from the client to function (e.g., a token plugin might require ClientWithRpc to fetch account data).

This enables a composable plugin architecture where plugins can build on top of each other's capabilities.

Installation

npm install @solana/plugin-interfaces

Interfaces

ClientWithPayer

Represents a client that provides a default transaction payer.

import { extendClient } from '@solana/plugin-core';
import { ClientWithPayer } from '@solana/plugin-interfaces';

function memoPlugin() {
    return <T extends ClientWithPayer>(client: T) =>
        extendClient(client, {
            sendMemo: (message: string) => {
                // Use client.payer as the fee payer for the memo transaction
                const feePayer = client.payer;
                // ...
            },
        });
}
ClientWithIdentity

Represents a client that provides a default identity signer — the wallet that owns things in the application, such as the authority over accounts, tokens, or other on-chain assets owned by the current user. Unlike ClientWithPayer, which describes the signer responsible for paying transaction fees and storage costs, the identity describes the signer whose assets the application is acting upon. In many apps, the payer and identity refer to the same signer, but they can differ — for example, when a service pays fees on behalf of a user.

import { extendClient } from '@solana/plugin-core';
import { ClientWithIdentity } from '@solana/plugin-interfaces';

function nftPlugin() {
    return <T extends ClientWithIdentity>(client: T) =>
        extendClient(client, {
            transferNft: (mint: Address, recipient: Address) => {
                // Use client.identity as the current owner of the NFT
                const owner = client.identity;
                // ...
            },
        });
}
ClientWithSubscribeToPayer / ClientWithSubscribeToIdentity

Some plugins set client.payer or client.identity reactively — the connected wallet may change, an account may be swapped, or a signer may be cleared on disconnect. Plugins that participate in this pattern advertise it by installing a sibling subscribeTo<Capability> function on the client:

Type Sibling function Advertises that…
ClientWithSubscribeToPayer subscribeToPayer client.payer may change over time
ClientWithSubscribeToIdentity subscribeToIdentity client.identity may change over time

Reactive consumers (framework hooks, stores, effects) can then observe changes without having to know which plugin installed the capability — they duck-type on the subscribe function:

import { ClientWithPayer, ClientWithSubscribeToPayer } from '@solana/plugin-interfaces';

function observePayer() {
    return <T extends ClientWithPayer & ClientWithSubscribeToPayer>(client: T) => {
        client.subscribeToPayer(() => {
            console.log('payer is now', client.payer);
        });
        return client;
    };
}

Plugins that leave the signer fixed for the lifetime of the client do not need to install these hooks — there is nothing to subscribe to. The convention is meant for plugins that reassign client.payer / client.identity as the user connects, switches accounts, or disconnects.

ClientWithAirdrop

Represents a client that can request SOL airdrops (typically on devnet/testnet). The airdrop succeeds when the promise resolves. Some implementations (e.g., LiteSVM) update balances directly without a transaction, so no signature is returned in those cases.

import { extendClient } from '@solana/plugin-core';
import { ClientWithAirdrop, ClientWithPayer } from '@solana/plugin-interfaces';

function faucetPlugin() {
    return <T extends ClientWithAirdrop & ClientWithPayer>(client: T) =>
        extendClient(client, {
            fundMyself: async (amount: Lamports) => {
                await client.airdrop(client.payer.address, amount);
            },
        });
}
ClientWithGetMinimumBalance

Represents a client that can compute the minimum balance required for an account to be exempt from deletion. Different implementations may compute this differently — for example, by calling the getMinimumBalanceForRentExemption RPC method, or by using a locally cached value.

By default, the 128-byte account header is added on top of the provided space. Pass { withoutHeader: true } to skip adding the header bytes.

import { extendClient } from '@solana/plugin-core';
import { ClientWithGetMinimumBalance } from '@solana/plugin-interfaces';

function accountCreationPlugin() {
    return <T extends ClientWithGetMinimumBalance>(client: T) =>
        extendClient(client, {
            getAccountCreationCost: async (dataSize: number) => {
                const minimumBalance = await client.getMinimumBalance(dataSize);
                console.log(`Minimum balance for ${dataSize} bytes: ${minimumBalance} lamports`);
                return minimumBalance;
            },
        });
}
ClientWithRpc<TRpcMethods>

Represents a client with access to a Solana RPC endpoint.

import { extendClient } from '@solana/plugin-core';
import { ClientWithRpc } from '@solana/plugin-interfaces';
import { GetBalanceApi } from '@solana/rpc-api';

function balancePlugin() {
    return <T extends ClientWithRpc<GetBalanceApi>>(client: T) =>
        extendClient(client, {
            getBalance: async (address: Address): Promise<Lamports> => {
                const { value } = await client.rpc.getBalance(address).send();
                return value;
            },
        });
}
ClientWithRpcSubscriptions<TRpcSubscriptionsMethods>

Represents a client that provides access to Solana RPC subscriptions for real-time notifications such as account changes, slot updates, and transaction confirmations.

import { extendClient } from '@solana/plugin-core';
import { ClientWithRpcSubscriptions } from '@solana/plugin-interfaces';
import { AccountNotificationsApi } from '@solana/rpc-subscriptions-api';

function accountWatcherPlugin() {
    return <T extends ClientWithRpcSubscriptions<AccountNotificationsApi>>(client: T) =>
        extendClient(client, {
            onAccountChange: async (address: Address, callback: (lamports: Lamports) => void) => {
                const subscription = await client.rpcSubscriptions.accountNotifications(address).subscribe();
                for await (const notification of subscription) {
                    callback(notification.value.lamports);
                }
            },
        });
}
ClientWithTransactionPlanning

Represents a client that can convert instructions or instruction plans into transaction plans.

import { flattenTransactionPlan } from '@solana/instruction-plans';
import { extendClient } from '@solana/plugin-core';
import { ClientWithTransactionPlanning } from '@solana/plugin-interfaces';

function transactionCounterPlugin() {
    return <T extends ClientWithTransactionPlanning>(client: T) =>
        extendClient(client, {
            countTransactions: async (instructions: IInstruction[]) => {
                const plan = await client.planTransactions(instructions);
                return flattenTransactionPlan(plan).length;
            },
        });
}
ClientWithTransactionSending

Represents a client that can send transactions to the Solana network. It supports flexible input formats including instructions, instruction plans, transaction messages, or transaction plans.

import { extendClient } from '@solana/plugin-core';
import { ClientWithPayer, ClientWithTransactionSending } from '@solana/plugin-interfaces';

function transferPlugin() {
    return <T extends ClientWithPayer & ClientWithTransactionSending>(client: T) =>
        extendClient(client, {
            transfer: async (recipient: Address, amount: Lamports) => {
                const instruction = getTransferSolInstruction({
                    source: client.payer,
                    destination: recipient,
                    amount,
                });
                const result = await client.sendTransaction(instruction);
                return result.context.signature;
            },
        });
}

Combining Interfaces

Use TypeScript intersection types to require multiple capabilities from the client:

import { extendClient } from '@solana/plugin-core';
import { ClientWithPayer, ClientWithRpc, ClientWithTransactionSending } from '@solana/plugin-interfaces';
import { GetAccountInfoApi } from '@solana/rpc-api';

function tokenTransferPlugin() {
    return <T extends ClientWithPayer & ClientWithRpc<GetAccountInfoApi> & ClientWithTransactionSending>(client: T) =>
        extendClient(client, {
            transferToken: async (mint: Address, recipient: Address, amount: bigint) => {
                // Use client.rpc to fetch token accounts
                // Use client.payer as the token owner
                // Use client.sendTransaction to execute the transfer
            },
        });
}

Keywords