Transaction Handling

Advanced transaction management and custom flows.

Overview

The SDK handles transaction building, signing, and confirmation. This guide covers advanced patterns for custom transaction flows.

Instruction Builders

Instead of sending transactions directly, you can build instructions for more control:

typescript
// Get instruction without sending
const instruction = await sdk.instructions.placeWagerV2({
  bet: betAddress,
  amount: 25,
  side: Outcome.For,
});

// Build custom transaction
const transaction = new Transaction().add(instruction);

Available Instruction Builders

All transaction methods have corresponding instruction builders:

typescript
// User
const createUserIx = await sdk.instructions.createUser({...});

// Bets
const initBetIx = await sdk.instructions.initializeBetV2Ix({...});
const wagerIx = await sdk.instructions.placeWagerV2({...});
const initiateVoteIx = await sdk.instructions.initiateVoteV2({...});
const voteIx = await sdk.instructions.placeVoteV2({...});
const settleIx = await sdk.instructions.settleBetBatchV2({...});

// Games
const createGameIx = await sdk.instructions.createGameIx({...});
const gameWagerIx = await sdk.instructions.placeGameWagerIx({...});

Custom Transaction Building

Build transactions with multiple instructions:

typescript
import { Transaction, ComputeBudgetProgram } from "@solana/web3.js";

async function buildCustomTransaction() {
  // Add compute budget for complex transactions
  const computeIx = ComputeBudgetProgram.setComputeUnitLimit({
    units: 400_000,
  });

  const priorityFeeIx = ComputeBudgetProgram.setComputeUnitPrice({
    microLamports: 1000,
  });

  // Get SDK instructions
  const wagerIx = await sdk.instructions.placeWagerV2({
    bet: betAddress,
    amount: 50,
    side: Outcome.For,
  });

  // Build transaction
  const tx = new Transaction()
    .add(computeIx)
    .add(priorityFeeIx)
    .add(wagerIx);

  // Get recent blockhash
  const { blockhash } = await connection.getLatestBlockhash();
  tx.recentBlockhash = blockhash;
  tx.feePayer = wallet.publicKey;

  return tx;
}

Pre-instructions

Add instructions before the main operation:

typescript
await sdk.placeWagerV2({
  bet: betAddress,
  amount: 25,
  side: Outcome.For,
  preinstructions: [
    // Create ATA if needed
    createAssociatedTokenAccountInstruction(...),
  ],
  signers: [wallet],
});

Fee Payer Override

Specify a different fee payer:

typescript
await sdk.placeWagerV2({
  bet: betAddress,
  amount: 25,
  side: Outcome.For,
  feePayerOverride: sponsorWallet.publicKey,
  signers: [wallet, sponsorWallet],
});

Transaction Confirmation

Control confirmation behavior:

typescript
await sdk.placeWagerV2({
  bet: betAddress,
  amount: 25,
  side: Outcome.For,
  confirmOptions: {
    commitment: "finalized",     // Wait for finalization
    maxRetries: 10,              // More retries
    skipPreflight: false,        // Enable simulation
    preflightCommitment: "processed",
  },
  signers: [wallet],
});

Retrying Failed Transactions

Implement retry logic for network issues:

typescript
async function sendWithRetry(
  fn: () => Promise<string>,
  maxRetries = 3
): Promise<string> {
  let lastError: Error | undefined;

  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error as Error;

      // Check if retryable
      if (error.message.includes("blockhash not found")) {
        console.log(`Attempt ${attempt + 1} failed, retrying...`);
        await new Promise((r) => setTimeout(r, 1000 * (attempt + 1)));
        continue;
      }

      throw error;
    }
  }

  throw lastError;
}

// Usage
const txHash = await sendWithRetry(() =>
  sdk.placeWagerV2({
    bet: betAddress,
    amount: 25,
    side: Outcome.For,
    signers: [wallet],
  })
);

Transaction Simulation

Simulate before sending:

typescript
import { Transaction } from "@solana/web3.js";

async function simulateTransaction(tx: Transaction) {
  const simulation = await connection.simulateTransaction(tx);

  if (simulation.value.err) {
    console.error("Simulation failed:", simulation.value.err);
    console.log("Logs:", simulation.value.logs);
    throw new Error("Transaction would fail");
  }

  console.log("Compute units used:", simulation.value.unitsConsumed);
  return simulation;
}

Versioned Transactions

The SDK supports versioned transactions for address lookup tables:

typescript
import { VersionedTransaction, TransactionMessage } from "@solana/web3.js";

// Build with address lookup tables
const message = new TransactionMessage({
  payerKey: wallet.publicKey,
  recentBlockhash: blockhash,
  instructions: [wagerIx],
}).compileToV0Message([lookupTable]);

const versionedTx = new VersionedTransaction(message);

Transaction Batching

Send multiple operations efficiently:

typescript
async function batchWagers(
  bets: { address: PublicKey; amount: number; side: Outcome }[]
) {
  const results = await Promise.allSettled(
    bets.map((bet) =>
      sdk.placeWagerV2({
        bet: bet.address,
        amount: bet.amount,
        side: bet.side,
        signers: [wallet],
      })
    )
  );

  const successful = results.filter((r) => r.status === "fulfilled");
  const failed = results.filter((r) => r.status === "rejected");

  console.log(`${successful.length} succeeded, ${failed.length} failed`);
  return results;
}

Monitoring Transactions

Track transaction status:

typescript
async function waitForConfirmation(
  signature: string,
  commitment: Commitment = "confirmed"
) {
  const start = Date.now();

  const result = await connection.confirmTransaction(signature, commitment);

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

  console.log(`Confirmed in ${Date.now() - start}ms`);
  return result;
}