Skip to main content

Overview

Transactions are the primary way to interact with the Solana blockchain. Hermis provides comprehensive tools for creating, signing, and sending transactions with proper error handling and confirmation tracking.

Transaction Lifecycle

Create → Sign → Send → Confirm → Finalize
1

Create

Build a transaction with instructions
2

Sign

Sign with wallet’s private key
3

Send

Broadcast to Solana network
4

Confirm

Wait for block confirmation
5

Finalize

Transaction is finalized on chain

Creating Transactions

Basic Transfer

  • React + web3.js
  • React + Kit
  • Vanilla TS + web3.js
  • Vanilla TS + Kit
import { Transaction, SystemProgram, LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js';
import { useWallet, useConnection } from '@hermis/solana-headless-react';

function SendSOL() {
  const { publicKey, signAndSendTransaction } = useWallet();
  const { connection } = useConnection();

  const send = async (recipient: string, amount: number) => {
    const transaction = new Transaction();

    transaction.add(
      SystemProgram.transfer({
        fromPubkey: publicKey!,
        toPubkey: new PublicKey(recipient),
        lamports: amount * LAMPORTS_PER_SOL
      })
    );

    // Set recent blockhash
    const { blockhash } = await connection.getLatestBlockhash();
    transaction.recentBlockhash = blockhash;
    transaction.feePayer = publicKey!;

    // Sign and send
    const signature = await signAndSendTransaction(transaction, connection);
    return signature;
  };

  return <button onClick={() => send('recipient...', 0.1)}>Send 0.1 SOL</button>;
}

Signing Transactions

Sign Only

  • React + web3.js
  • React + Kit
  • Vanilla TS + web3.js
  • Vanilla TS + Kit
import { useWallet, useConnection } from '@hermis/solana-headless-react';

const { signTransaction } = useWallet();
const { connection } = useConnection();

// Sign without sending
const signedTx = await signTransaction(transaction);

// Can be sent later or by another service
const signature = await connection.sendRawTransaction(signedTx.serialize());

Sign Multiple

  • React + web3.js
  • React + Kit
  • Vanilla TS + web3.js
  • Vanilla TS + Kit
import { useWallet, useConnection } from '@hermis/solana-headless-react';

const { signAllTransactions } = useWallet();
const { connection } = useConnection();

const transactions = [tx1, tx2, tx3];
const signedTxs = await signAllTransactions(transactions);

// Send all transactions
for (const tx of signedTxs) {
  await connection.sendRawTransaction(tx.serialize());
}

Sending Transactions

With Confirmation

  • React + web3.js
  • React + Kit
  • Vanilla TS + web3.js
  • Vanilla TS + Kit
import { useWallet, useConnection } from '@hermis/solana-headless-react';

const { signAndSendTransaction } = useWallet();
const { connection } = useConnection();

const signature = await signAndSendTransaction(transaction, connection);

// Wait for confirmation
await connection.confirmTransaction(signature, 'confirmed');

console.log('Transaction confirmed:', signature);

With Options

  • React + web3.js
  • React + Kit
  • Vanilla TS + web3.js
  • Vanilla TS + Kit
import { useWallet, useConnection } from '@hermis/solana-headless-react';

const { signAndSendTransaction } = useWallet();
const { connection } = useConnection();

const signature = await signAndSendTransaction(
  transaction,
  connection,
  {
    skipPreflight: false,
    preflightCommitment: 'confirmed',
    maxRetries: 3,
  }
);

Transaction Confirmation

Commitment Levels

Processed

Fastest - Transaction processed but not confirmed

Confirmed

Recommended - Confirmed by supermajority

Finalized

Safest - Finalized by the network

Tracking Status

This hook works universally with transaction signatures from both web3.js and Kit architectures.
import { useSolanaTransaction } from '@hermis/solana-headless-react';

function TransactionStatus({ signature }: { signature: string }) {
  const { status, loading } = useSolanaTransaction(signature);

  if (loading) return <div>Checking status...</div>;

  return (
    <div>
      <p>Status: {status?.status}</p>
      <p>Confirmations: {status?.confirmations}</p>
      {status?.error && <p>Error: {status.error}</p>}
    </div>
  );
}

SPL Token Transactions

Transfer Tokens

  • React + web3.js
  • React + Kit
  • Vanilla TS + web3.js
  • Vanilla TS + Kit
import { useWallet, useConnection } from '@hermis/solana-headless-react';
import {
  getAssociatedTokenAddress,
  createTransferInstruction,
  createAssociatedTokenAccountInstruction
} from '@solana/spl-token';
import { PublicKey, Transaction } from '@solana/web3.js';

const { publicKey, signAndSendTransaction } = useWallet();
const { connection } = useConnection();

const transferTokens = async (
  mintAddress: PublicKey,
  recipient: PublicKey,
  amount: number
) => {
  // Get token accounts (ATAs)
  const fromTokenAccount = await getAssociatedTokenAddress(
    mintAddress,
    publicKey!
  );

  const toTokenAccount = await getAssociatedTokenAddress(
    mintAddress,
    recipient
  );

  // Check if recipient's ATA exists
  const toAccountInfo = await connection.getAccountInfo(toTokenAccount);

  // Create transaction
  const transaction = new Transaction();

  // Create recipient's ATA if it doesn't exist
  if (!toAccountInfo) {
    transaction.add(
      createAssociatedTokenAccountInstruction(
        publicKey!,        // payer
        toTokenAccount,    // ata
        recipient,         // owner
        mintAddress        // mint
      )
    );
  }

  // Add transfer instruction
  transaction.add(
    createTransferInstruction(
      fromTokenAccount,
      toTokenAccount,
      publicKey!,
      amount
    )
  );

  const { blockhash } = await connection.getLatestBlockhash();
  transaction.recentBlockhash = blockhash;
  transaction.feePayer = publicKey!;

  return await signAndSendTransaction(transaction, connection);
};

Error Handling

Hermis uses HermisError for type-safe transaction error handling:
import {
  isHermisError,
  HERMIS_ERROR__TRANSACTION__SEND_FAILED,
  HERMIS_ERROR__NETWORK__INSUFFICIENT_BALANCE
} from '@hermis/errors';

try {
  await signAndSendTransaction(transaction, connection);
} catch (error) {
  if (isHermisError(error, HERMIS_ERROR__NETWORK__INSUFFICIENT_BALANCE)) {
    console.error(`Insufficient balance: required ${error.context.required}, available ${error.context.available}`);
  } else if (isHermisError(error, HERMIS_ERROR__TRANSACTION__SEND_FAILED)) {
    console.error(`Transaction failed: ${error.message}`);
  } else if (isHermisError(error)) {
    console.error(`Error [${error.code}]: ${error.message}`);
  }
}

Common Error Patterns

Handle insufficient funds with typed context:
import { isHermisError, HERMIS_ERROR__NETWORK__INSUFFICIENT_BALANCE } from '@hermis/errors';

if (isHermisError(error, HERMIS_ERROR__NETWORK__INSUFFICIENT_BALANCE)) {
  // Access typed context: address, required, available
  const needed = error.context.required - error.context.available;
  showToast(`Need ${needed} more lamports`);
}
Handle transaction send failures:
import { isHermisError, HERMIS_ERROR__TRANSACTION__SEND_FAILED } from '@hermis/errors';

if (isHermisError(error, HERMIS_ERROR__TRANSACTION__SEND_FAILED)) {
  // HermisError provides formatted message
  showToast(error.message);

  // Optionally retry or get fresh blockhash
  const { blockhash } = await connection.getLatestBlockhash('finalized');
  transaction.recentBlockhash = blockhash;
}
Handle transaction signing failures:
import { isHermisError, HERMIS_ERROR__TRANSACTION__SIGNATURE_FAILED } from '@hermis/errors';

if (isHermisError(error, HERMIS_ERROR__TRANSACTION__SIGNATURE_FAILED)) {
  showToast('Failed to sign transaction');
}
Handle any HermisError with automatic formatting:
import { isHermisError } from '@hermis/errors';

if (isHermisError(error)) {
  // All HermisErrors provide formatted messages
  console.error(`[${error.code}] ${error.message}`);
  showToast(error.message);
}

Retry Logic

  • React + web3.js
  • React + Kit
  • Vanilla TS + web3.js
  • Vanilla TS + Kit
import { useWallet, useConnection } from '@hermis/solana-headless-react';
import { Transaction } from '@solana/web3.js';

const { signAndSendTransaction } = useWallet();
const { connection } = useConnection();

const sendWithRetry = async (
  transaction: Transaction,
  maxRetries = 3
) => {
  for (let i = 0; i < maxRetries; i++) {
    try {
      // Refresh blockhash on retry
      if (i > 0) {
        const { blockhash } = await connection.getLatestBlockhash();
        transaction.recentBlockhash = blockhash;
      }

      const signature = await signAndSendTransaction(transaction, connection);
      await connection.confirmTransaction(signature);
      return signature;
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
    }
  }
};

Transaction Fees

Estimating Fees

  • React + web3.js
  • React + Kit
  • Vanilla TS + web3.js
  • Vanilla TS + Kit
import { useWallet, useConnection } from '@hermis/solana-headless-react';
import { Transaction, LAMPORTS_PER_SOL } from '@solana/web3.js';

const { publicKey } = useWallet();
const { connection } = useConnection();

const estimateFee = async (transaction: Transaction) => {
  const { blockhash } = await connection.getLatestBlockhash();
  transaction.recentBlockhash = blockhash;
  transaction.feePayer = publicKey!;

  const fee = await connection.getFeeForMessage(
    transaction.compileMessage()
  );

  return fee.value / LAMPORTS_PER_SOL; // Convert to SOL
};

Priority Fees

  • React + web3.js
  • React + Kit
  • Vanilla TS + web3.js
  • Vanilla TS + Kit
import { useWallet, useConnection } from '@hermis/solana-headless-react';
import { Transaction, ComputeBudgetProgram } from '@solana/web3.js';

const { publicKey, signAndSendTransaction } = useWallet();
const { connection } = useConnection();

// Add priority fee to transaction
transaction.add(
  ComputeBudgetProgram.setComputeUnitPrice({
    microLamports: 1000, // Priority fee
  })
);

const signature = await signAndSendTransaction(transaction, connection);

Best Practices

Always set recent blockhash
Set fee payer explicitly
Wait for confirmation
Handle errors gracefully
Simulate before sending
Check account balances first
Use appropriate commitment levels
Implement retry logic

What’s Next?