Skip to main content

Add transaction confirmation UI

Upgrade your transfer tool from a black box to a live tracker—watch your transaction move from processed → confirmed → finalized in real time, handle failures properly, and get a verifiable on-chain receipt.
Add transaction confirmation UI background
Challenge

Add transaction confirmation UI

The Scenario

Yesterday you built a programmatic transfer tool. It takes a recipient address and an amount from the command line, sends SOL on devnet, and prints a transaction signature. It even waits for the transaction to be confirmed before moving on. But that confirmation happens in a single opaque step: you call sendAndConfirm, it hangs for a moment, and then you get a signature. You have no visibility into the stages in between, and if anything goes wrong the error lands as an unhandled rejection. In Web2 terms, you built a POST request that returns a request ID but never streams progress back to the user. Today you fix that. You will add real-time confirmation tracking to your CLI tool so that every transfer shows its status as it moves from “processed” to “confirmed” to “finalized,” and then hands you an explorer link to prove it.

The Challenge

What You’ll Need

  • Your transfer tool from Day 17
  • A terminal with Node.js installed
  • Your devnet-funded keypair from earlier days

Steps

Step 1: Understand what confirmation actually means

When you send a transaction on Solana, it does not go from “pending” to “done” in one jump. It moves through three commitment levels:

  1. Processed: A validator included your transaction in a recent block. Think of this like your POST request reaching the server. The server acknowledged it, but nothing is guaranteed yet.
  2. Confirmed: A supermajority of validators (66%+) voted on the block containing your transaction. This is like getting a 200 OK from a load-balanced API where most backend nodes agree the write succeeded. In Solana’s entire history, no confirmed transaction has ever been reversed.
  3. Finalized: At least 31 additional confirmed blocks have been built on top of yours. This is the equivalent of a database commit that has been replicated, flushed to disk, and backed up. It is irreversible.

Your tool currently waits for “confirmed” behind a single function call and walks away. You are going to break that into observable stages and report each one.

Step 2: Swap the all-in-one factory for a manual send + poll

Yesterday’s tool used sendAndConfirmTransactionFactory, which bundles sending the transaction and waiting for confirmation into one call. That is convenient, but it gives you no hook to report progress between stages. Today you split it apart: send the transaction yourself, then poll the network for its status as it climbs the commitment ladder.

Update your imports at the top of the file to add one new helper from @solana/kit:

import {
	address,
	createKeyPairSignerFromBytes,
	createSolanaRpc,
	createSolanaRpcSubscriptions,
	pipe,
	createTransactionMessage,
	setTransactionMessageFeePayerSigner,
	setTransactionMessageLifetimeUsingBlockhash,
	appendTransactionMessageInstruction,
	signTransactionMessageWithSigners,
	getSignatureFromTransaction,
	getBase64EncodedWireTransaction,
	lamports,
	devnet,
} from "@solana/kit";
import { getTransferSolInstruction } from "@solana-program/system";

getBase64EncodedWireTransaction is what lets you hand a signed transaction directly to rpc.sendTransaction() instead of going through the factory.

Step 3: Add a confirmation progress display

Before you wire up the new send logic, set up a simple status reporter. Add this helper function to your script:

function statusUpdate(message) {
	process.stdout.clearLine(0);
	process.stdout.cursorTo(0);
	process.stdout.write(message);
}

This overwrites the current terminal line instead of printing a new one each time, giving you a clean progress indicator.

Step 4: Send with staged confirmation tracking

Now replace your transaction-sending logic with a version that reports on each commitment level. Solana Kit does not ship with a single “wait for this commitment level” call, so you write a tiny polling helper that walks getSignatureStatuses up the ladder. Then transferWithConfirmation builds the transaction, sends it, and calls the helper twice — once for confirmed, once for finalized — with a status update between each stage.

See the code in this gist.

Now wire it into main(). In your Day 17 file, sections 4 (Build the transaction), 5 (Sign the transaction), and 6 (Send and confirm) all live inline inside main(). Delete those three sections — transferWithConfirmation now does all of that work — and replace them with a single call to the new function. Steps 5 and 6 below show exactly what goes in their place.

Step 5: Print the explorer link

In place of the old sections 4–6 inside main(), call transferWithConfirmation and print the explorer link from the signature it returns:

const signature = await transferWithConfirmation(rpc, sender, recipientAddress, solAmount);

console.log("Transaction successful!");
console.log(`Signature: ${signature}`);
console.log(`View on Solana Explorer:`);
console.log(`https://explorer.solana.com/tx/${signature}?cluster=devnet`);

You can also delete the “Show updated balance” section at the bottom of main() if you want to keep the output focused, or leave it in — your choice.

Step 6: Add error handling for failed confirmations

Transactions can fail. A network hiccup, an expired blockhash, or insufficient funds could all cause problems, and your waitForCommitment helper will throw if the network reports an on-chain error. Wrap the call you just added in Step 5 in a try/catch:

try {
	const signature = await transferWithConfirmation(rpc, sender, recipientAddress, solAmount);
	console.log("Transaction successful!");
	console.log(`Signature: ${signature}`);
	console.log(`View on Solana Explorer:`);
	console.log(`https://explorer.solana.com/tx/${signature}?cluster=devnet`);
} catch (error) {
	console.error("\nTransaction failed:");
	console.error(error.message);
	process.exit(1);
}

Run It

node transfer.mjs [RECIPIENT_ADDRESS] 0.01

Watch your terminal. You should see the status line update in place as the transaction progresses through each stage. On devnet, the jump from “processed” to “confirmed” typically takes about 400 milliseconds, and reaching “finalized” takes roughly 6 to 12 seconds. Once it completes, open the explorer link in your browser and verify that the transaction, the amount, and the recipient all match.

What Just Happened

You turned an opaque confirmation step into something that actually tells you what is going on. Yesterday’s tool waited for “confirmed” behind a single function call and handed you a signature once it was done. Now it tracks the transaction through Solana’s three commitment levels, surfaces on-chain errors as proper exceptions, and gives you a direct link to verify the result on-chain.

This matters because confirmation is not a binary state on Solana. The network processes around 4,000 transactions per second across a global set of validators, and consensus happens in stages. “Processed” means one validator saw it. “Confirmed” means the network’s supermajority agrees. “Finalized” means it would take a catastrophic network event to reverse it. By tracking each stage, your tool now gives users the same kind of feedback they expect from any well-built application: real-time progress, clear completion, and a receipt.

The pattern you used here, polling for confirmation at increasing commitment levels, is the same pattern production Solana applications rely on. Wallets, DEXes, and NFT marketplaces all track confirmation status to decide when to update their UI. You now understand exactly how that works.

Resources

Submission

Run your updated transfer tool and take a screenshot showing the terminal output with the confirmation status progression and the explorer link. Submit the screenshot here.

Submit your project