Transferring SOL

Send SOL from one account to another with error handling

This recipe walks through sending SOL from your client's payer to another address. It uses a local validator so you can run it end-to-end without touching real funds, and shows how to surface a useful error when a transfer fails.

Set up a client

Install Kit and the plugins you need.

npm install @solana/kit @solana/kit-plugin-rpc @solana/kit-plugin-signer @solana-program/system

Compose a client with a generated signer, a local RPC connection, an airdrop to fund the signer, and the System program plugin. Make sure a local validator is running (solana-test-validator) before this code executes.

import { ,  } from '@solana/kit';
import {  } from '@solana/kit-plugin-rpc';
import { ,  } from '@solana/kit-plugin-signer';
import {  } from '@solana-program/system';
 
const  = await ()
    .(())
    .(())
    .(((1_000_000_000n)))
    .(());

Send the transfer

Use client.system.instructions.transferSol to build the transfer instruction, then call .sendTransaction() to plan, sign, send, and confirm it in one step.

import { ,  } from '@solana/kit';
const  = ('GdnSyH3YtwcxFvQrVVJMm1JhTS4QVX7MFsX56uJLUfiZ');
 
const  = await ..
    .({
        : .,
        : ,
        : (10_000_000n), // 0.01 SOL
    })
    .();
 
.(`✅ ${..}`);

The shortcut is equivalent to passing the instruction to client.sendTransaction([...]). Reach for the array form when you want to combine several instructions into a single atomic transaction.

Handle errors

When a transfer fails, client.sendTransaction(...) throws a SolanaError with the SOLANA_ERROR__FAILED_TO_SEND_TRANSACTION code. Wrapping the call lets you surface a useful message and inspect the underlying program error.

import {
    ,
    ,
    ,
    ,
    ,
} from '@solana/kit';
import { ,  } from '@solana-program/system';
try {
    await ..
        .({
            : .,
            : ,
            : (10_000_000n),
        })
        .();
} catch () {
    if (!(, )) throw ;
 
    const  = .. as ;
    const  = .. ?? .;
    const  = . as Error;
 
    if ((, )) {
        .(`System program error: ${(..)}`);
    } else {
        .('Transfer failed:', .);
    }
}

error.cause carries the underlying error, and error.context.transactionPlanResult carries the planned transaction message. Pairing them with isSystemError and getSystemErrorMessage from @solana-program/system turns an opaque program error code into a human-readable message — invaluable when something like an underfunded source account causes the transfer to fail.

Next steps

On this page