Building and sending transactions from a client
A transaction bundles instructions, a recent blockhash and signatures. Clients fetch a recent blockhash, add instructions (often including compute-budget instructions for priority fees), sign, send, and confirm at a chosen commitment level. Versioned transactions add address lookup tables so more accounts fit.
Reference: Solana docs: Transactions · Solana docs: Fees
Real code, line by line
6 excerpts from different projects. Each caption links the exact lines at the fork's pinned commit. The notes below each excerpt explain the lines that matter.
libs/api/preset/data-access/src/lib/api-preset-minter.service.ts L846–870 @ b87eed9 · MIT · original payerKey: feePayer.publicKey, recentBlockhash: blockhash, }).compileToV0Message(addressLookupTableAccounts) const transaction = new VersionedTransaction(transactionMessage) transaction.sign([feePayer, authority, mintKeypair]) return this.sendAndConfirmTransaction({ transaction, blockhash, lastValidBlockHeight }) } private async sendAndConfirmTransaction({ transaction, blockhash, lastValidBlockHeight, }: { transaction: VersionedTransaction blockhash: string lastValidBlockHeight: number }): Promise<string> { const signature = await this.solana.connection.sendTransaction(transaction, { skipPreflight: true }) this.logger.debug(`Signature: ${signature}`) await this.solana.connection.confirmTransaction({ blockhash, lastValidBlockHeight, signature }, 'confirmed') this.logger.debug(`Confirmed: ${signature}`) return signature }- Line 853
return this.sendAndConfirmTransaction({ transaction, blockhash, lastValidBlockHeight }): Submits the signed transaction to the cluster.
lib/src/lib/optimizeTransaction.ts L40–65 @ 7aa9ddf · MIT · original signerKey, instructions ); const transactions: VersionedTransaction[] = []; const recentBlockhash = await connection.getLatestBlockhash(); for (const tx of txs) { // Get CU budget and priority fee for each tx const [microLamportsEstimate, computeUnits] = await Promise.all([ estimatePrioritizationFee(connection, tx.instructions, 100), getSimulationUnits(connection, tx.instructions, signerKey, lookupTables), ]); // console.log('Priority fees: ', microLamportsEstimate, ' / CUs: ', computeUnits); const instructions = [ ComputeBudgetProgram.setComputeUnitLimit({ units: computeUnits || 200_000, }), ComputeBudgetProgram.setComputeUnitPrice({ microLamports: microLamportsEstimate, }), ...tx.instructions, ];- Line 46
const recentBlockhash = await connection.getLatestBlockhash();: Fetches a recent blockhash. Transactions expire if it gets too old. - Line 58
ComputeBudgetProgram.setComputeUnitLimit({: Sets the compute-unit limit or price (priority fee).
app/utils/sendVersionedTransaction.ts L8–32 @ 29a7c77 · MIT · original TransactionSignature, VersionedTransaction,} from "@solana/web3.js"; export const sendVersionedTransaction = async ( instructions: TransactionInstruction[], wallet: WalletContextState, connection: Connection) => { if (!wallet.publicKey) { throw new WalletNotConnectedError(connectWalletText); } // Get the lates block hash to use on our transaction and confirmation let latestBlockhash = await connection.getLatestBlockhash("confirmed"); // Create a new TransactionMessage with version and compile it to version 0 const messageV0 = new TransactionMessage({ payerKey: wallet.publicKey, recentBlockhash: latestBlockhash.blockhash, instructions, }).compileToV0Message(); // Create a new VersionedTransacction to support the v0 message const transaction = new VersionedTransaction(messageV0);- Line 22
let latestBlockhash = await connection.getLatestBlockhash("confirmed");: Fetches a recent blockhash. Transactions expire if it gets too old.
web/src/app/account/account-data-access.tsx L169–194 @ 0359070 · CC0-1.0 · original transaction: VersionedTransaction; latestBlockhash: { blockhash: string; lastValidBlockHeight: number };}> { // Get the latest blockhash to use in our transaction const latestBlockhash = await connection.getLatestBlockhash(); // Create instructions to send, in this case a simple transfer const instructions = [ SystemProgram.transfer({ fromPubkey: publicKey, toPubkey: destination, lamports: amount * LAMPORTS_PER_SOL, }), ]; // Create a new TransactionMessage with version and compile it to legacy const messageLegacy = new TransactionMessage({ payerKey: publicKey, recentBlockhash: latestBlockhash.blockhash, instructions, }).compileToLegacyMessage(); // Create a new VersionedTransaction which supports legacy and v0 const transaction = new VersionedTransaction(messageLegacy); return {- Line 173
const latestBlockhash = await connection.getLatestBlockhash();: Fetches a recent blockhash. Transactions expire if it gets too old.
src/app/account/account-data-access.tsx L191–216 @ 28fe02d · MIT · original transaction: VersionedTransaction; latestBlockhash: { blockhash: string; lastValidBlockHeight: number };}> { // Get the latest blockhash to use in our transaction const latestBlockhash = await connection.getLatestBlockhash(); // Create instructions to send, in this case a simple transfer const instructions = [ SystemProgram.transfer({ fromPubkey: publicKey, toPubkey: destination, lamports: amount * LAMPORTS_PER_SOL, }), ]; // Create a new TransactionMessage with version and compile it to legacy const messageLegacy = new TransactionMessage({ payerKey: publicKey, recentBlockhash: latestBlockhash.blockhash, instructions, }).compileToLegacyMessage(); // Create a new VersionedTransaction which supports legacy and v0 const transaction = new VersionedTransaction(messageLegacy); return {- Line 195
const latestBlockhash = await connection.getLatestBlockhash();: Fetches a recent blockhash. Transactions expire if it gets too old.
web/components/account/account-data-access.tsx L171–196 @ e314413 · MIT · original transaction: VersionedTransaction; latestBlockhash: { blockhash: string; lastValidBlockHeight: number };}> { // Get the latest blockhash to use in our transaction const latestBlockhash = await connection.getLatestBlockhash(); // Create instructions to send, in this case a simple transfer const instructions = [ SystemProgram.transfer({ fromPubkey: publicKey, toPubkey: destination, lamports: amount * LAMPORTS_PER_SOL, }), ]; // Create a new TransactionMessage with version and compile it to legacy const messageLegacy = new TransactionMessage({ payerKey: publicKey, recentBlockhash: latestBlockhash.blockhash, instructions, }).compileToLegacyMessage(); // Create a new VersionedTransaction which supports legacy and v0 const transaction = new VersionedTransaction(messageLegacy); return {- Line 175
const latestBlockhash = await connection.getLatestBlockhash();: Fetches a recent blockhash. Transactions expire if it gets too old.
Go further
- tokengator: explore the full project
- valhalla: explore the full project
- poe-solana: explore the full project
- mons-dot-rehab: explore the full project
- solstage-web: explore the full project
- seloongmun: explore the full project
- Next lesson: Anchor account constraints