Overview

A Solana transaction is a list of instructions that are executed in order. This allows developers to batch multiple instructions into a single transaction, reducing the number of transactions required to complete a complex multi-step process.

Prerequisites

It is assumed you have already completed the Quickstart guide.

Create and send transaction with multiple instructions

In this example, we will:
  • Create a Solana account
  • Construct multiple instructions to be executed in the transaction
  • Sign the transaction with the Solana account
  • Send the transaction to the Solana network
main.ts
import {
Connection,
PublicKey,
SystemProgram,
Transaction,
} from "@solana/web3.js";
import { CdpClient } from "@coinbase/cdp-sdk";
import dotenv from "dotenv";

dotenv.config();

const cdp = new CdpClient();

const connection = new Connection("https://api.devnet.solana.com");

async function createAccount() {
const account = await cdp.solana.createAccount();
console.log(`Created account: ${account.address}`);
return account;
}

async function requestFaucet(address: string) {
await cdp.solana.requestFaucet({
address,
token: "sol",
});
}

async function waitForBalance(address: string) {
let balance = 0;
let attempts = 0;
const maxAttempts = 30;

while (balance === 0 && attempts < maxAttempts) {
balance = await connection.getBalance(new PublicKey(address));
if (balance === 0) {
  console.log("Waiting for funds...");
  await new Promise(resolve => setTimeout(resolve, 1000));
  attempts++;
} else {
  console.log("Account funded with", balance / 1e9, "SOL");
}
}

if (balance === 0) {
throw new Error("Account not funded after multiple attempts");
}
}

async function sendTransaction(address: string) {
// Amount of lamports to send (default: 1000 = 0.000001 SOL)
const lamportsToSend = 1000;
const fromAddress = new PublicKey(address)
const destinations = [
"ANVUJaJoVaJZELtV2AvRp7V5qPV1B84o29zAwDhPj1c2",
"EeVPcnRE1mhcY85wAh3uPJG1uFiTNya9dCJjNUPABXzo",
"4PkiqJkUvxr9P8C1UsMqGN8NJsUcep9GahDRLfmeu8UK",
]

const { blockhash } = await connection.getLatestBlockhash();

// Create instructions for each destination
const instructions = destinations.map((toAddress) => {
return SystemProgram.transfer({
  fromPubkey: fromAddress,
  toPubkey: new PublicKey(toAddress),
  lamports: lamportsToSend,
})
})

// Create a single transaction with all instructions
const transaction = new Transaction();
transaction.add(...instructions);

transaction.recentBlockhash = blockhash;
transaction.feePayer = fromAddress;

const serializedTx = Buffer.from(
transaction.serialize({ requireAllSignatures: false })
).toString("base64");

const { signature: txSignature } = await cdp.solana.signTransaction({
address,
transaction: serializedTx,
});
const decodedSignedTx = Buffer.from(txSignature, "base64");

console.log("Sending transaction...");
const txSendSignature = await connection.sendRawTransaction(decodedSignedTx);

const latestBlockhash = await connection.getLatestBlockhash();

console.log("Waiting for transaction to be confirmed...");
const confirmation = await connection.confirmTransaction({
signature: txSendSignature,
blockhash: latestBlockhash.blockhash,
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
});

if (confirmation.value.err) {
throw new Error(`Transaction failed: ${confirmation.value.err.toString()}`);
}

console.log(`Sent SOL: https://explorer.solana.com/tx/${txSendSignature}?cluster=devnet`);
}

async function main() {
const account = await createAccount();
await requestFaucet(account.address);
await waitForBalance(account.address);
await sendTransaction(account.address);
}

main().catch(console.error)
After running the above snippet, you should see output similar to the following:
Created account: Af8cVHK2DZXcT4WhK6VDZ3h2zFxbEfgamsRkrB7dUcfF
Waiting for funds...
Account funded with 0.00125 SOL (1250000 lamports)
Sending transaction...
Waiting for transaction to be confirmed...
Sent SOL: https://explorer.solana.com/tx/56oRrY2nHSbncysmrW6vtBaUoyvWnRrMqN1joGNzaY3TNmPSTM653skDjbj2jDEdMA4QqFo9c4GY4hTnRhScgJk5?cluster=devnet