Skip to main content

Overview

Entries are the smallest building blocks of the Solana blockchain. Think of them as containers that hold groups of transactions.
Start here first: Complete the Quickstart Guide to set up your stream manager.

What are Entries?

  • The Basics
  • What's Inside
  • When to Use This
Entries are transaction containersHere’s what they do:
  • Bundle transactions: They group transactions together
  • Set the order: Transactions run in a specific order
  • Link together: Each entry connects to the next one
  • Track time: They record when things happen
    const subscribeRequest: SubscribeRequest = {
      entry: {
        entrySubscribe: {}
      },
      commitment: CommitmentLevel.CONFIRMED
    };

Real Examples

Example 1: Basic Entry Monitor

Watch entries as they come in:
import { StreamManager } from './yellowstone';
import { CommitmentLevel, SubscribeRequest } from "@triton-one/yellowstone-grpc";

async function monitorEntries() {
  const streamManager = new StreamManager(
    "https://grpc.solanatracker.io",
    process.env.GRPC_API_TOKEN || "your-api-key",
    handleEntry
  );

  const subscribeRequest: SubscribeRequest = {
    entry: {
      entrySubscribe: {}
    },
    commitment: CommitmentLevel.CONFIRMED
  };

  console.log('Starting entry monitoring...');
  console.log('Note: This is a high-volume stream\n');
  
  await streamManager.connect(subscribeRequest);
}

const entryStats = {
  totalEntries: 0,
  totalTransactions: 0,
  emptyEntries: 0,
  lastReportTime: Date.now()
};

function handleEntry(data: any): void {
  if (data.entry) {
    const entry = data.entry;
    entryStats.totalEntries++;
    
    const txCount = entry.transactions?.length || 0;
    entryStats.totalTransactions += txCount;
    
    if (txCount === 0) {
      entryStats.emptyEntries++;
    }
    
    console.log(`\n[Entry #${entryStats.totalEntries}]`);
    console.log(`  Slot: ${entry.slot}`);
    console.log(`  Index: ${entry.index || 'N/A'}`);
    console.log(`  Hash: ${entry.hash?.slice(0, 16) || 'N/A'}...`);
    console.log(`  Num Hashes: ${entry.numHashes || 'N/A'}`);
    console.log(`  Transactions: ${txCount}`);
    
    if (txCount === 0) {
      console.log(`  [Empty Entry]`);
    } else {
      // Count transaction types
      let successCount = 0;
      let failedCount = 0;
      let voteCount = 0;
      
      entry.transactions.forEach((tx: any) => {
        if (tx.isVote) {
          voteCount++;
        }
        if (tx.meta) {
          if (tx.meta.err) {
            failedCount++;
          } else {
            successCount++;
          }
        }
      });
      
      console.log(`  Transaction Breakdown:`);
      console.log(`    Votes: ${voteCount}`);
      console.log(`    Successful: ${successCount}`);
      console.log(`    Failed: ${failedCount}`);
      
      // Show first few signatures
      if (entry.transactions.length > 0) {
        console.log(`  First Transactions:`);
        entry.transactions.slice(0, 3).forEach((tx: any, idx: number) => {
          const sig = tx.signature?.slice(0, 16) || 'unknown';
          const type = tx.isVote ? '[VOTE]' : '[TX]';
          console.log(`    ${idx + 1}. ${sig}... ${type}`);
        });
      }
    }
    
    // Report stats every 100 entries
    if (entryStats.totalEntries % 100 === 0) {
      printEntrySummary();
    }
  }
}

function printEntrySummary(): void {
  const now = Date.now();
  const elapsed = (now - entryStats.lastReportTime) / 1000;
  const entriesPerSec = 100 / elapsed;
  const avgTxPerEntry = entryStats.totalTransactions / entryStats.totalEntries;
  const emptyRate = (entryStats.emptyEntries / entryStats.totalEntries * 100).toFixed(1);
  
  console.log(`\n=== Entry Summary ===`);
  console.log(`  Total Entries: ${entryStats.totalEntries}`);
  console.log(`  Total Transactions: ${entryStats.totalTransactions}`);
  console.log(`  Empty Entries: ${entryStats.emptyEntries} (${emptyRate}%)`);
  console.log(`  Entries/sec: ${entriesPerSec.toFixed(1)}`);
  console.log(`  Avg Tx/Entry: ${avgTxPerEntry.toFixed(2)}`);
  
  entryStats.lastReportTime = now;
}

monitorEntries().catch(console.error);

Example 2: Filter Entries

Only process entries you care about:
async function monitorFilteredEntries() {
  const streamManager = new StreamManager(
    "https://grpc.solanatracker.io",
    process.env.GRPC_API_TOKEN || "your-api-key",
    handleFilteredEntry
  );

  const subscribeRequest: SubscribeRequest = {
    entry: {
      entrySubscribe: {}
    },
    commitment: CommitmentLevel.CONFIRMED
  };

  console.log('Monitoring filtered entries (non-vote transactions only)...\n');
  await streamManager.connect(subscribeRequest);
}

const filteredStats = {
  totalEntriesProcessed: 0,
  totalEntriesSkipped: 0,
  nonVoteTransactions: 0,
  voteTransactions: 0
};

function handleFilteredEntry(data: any): void {
  if (data.entry) {
    const entry = data.entry;
    
    // Skip empty entries
    if (!entry.transactions || entry.transactions.length === 0) {
      filteredStats.totalEntriesSkipped++;
      return;
    }
    
    // Count transaction types
    let nonVoteCount = 0;
    let voteCount = 0;
    
    entry.transactions.forEach((tx: any) => {
      if (tx.isVote) {
        voteCount++;
      } else {
        nonVoteCount++;
      }
    });
    
    // Skip entries with only vote transactions
    if (nonVoteCount === 0) {
      filteredStats.totalEntriesSkipped++;
      filteredStats.voteTransactions += voteCount;
      return;
    }
    
    // Process this entry
    filteredStats.totalEntriesProcessed++;
    filteredStats.nonVoteTransactions += nonVoteCount;
    filteredStats.voteTransactions += voteCount;
    
    console.log(`\n[Filtered Entry #${filteredStats.totalEntriesProcessed}]`);
    console.log(`  Slot: ${entry.slot}`);
    console.log(`  Total Transactions: ${entry.transactions.length}`);
    console.log(`  Non-Vote Txs: ${nonVoteCount}`);
    console.log(`  Vote Txs: ${voteCount}`);
    
    // Show non-vote transaction signatures
    const nonVoteTxs = entry.transactions.filter((tx: any) => !tx.isVote);
    console.log(`  Non-Vote Signatures:`);
    nonVoteTxs.slice(0, 3).forEach((tx: any, idx: number) => {
      const sig = tx.signature?.slice(0, 16) || 'unknown';
      const status = tx.meta?.err ? '❌' : '✅';
      console.log(`    ${idx + 1}. ${sig}... ${status}`);
    });
    
    // Report stats every 100 processed entries
    if (filteredStats.totalEntriesProcessed % 100 === 0) {
      printFilterStats();
    }
  }
}

function printFilterStats(): void {
  const totalEntries = filteredStats.totalEntriesProcessed + filteredStats.totalEntriesSkipped;
  const processRate = (filteredStats.totalEntriesProcessed / totalEntries * 100).toFixed(1);
  const skipRate = (filteredStats.totalEntriesSkipped / totalEntries * 100).toFixed(1);
  
  console.log(`\n=== Filtering Statistics ===`);
  console.log(`  Total Entries Seen: ${totalEntries}`);
  console.log(`  Processed: ${filteredStats.totalEntriesProcessed} (${processRate}%)`);
  console.log(`  Skipped: ${filteredStats.totalEntriesSkipped} (${skipRate}%)`);
  console.log(`  Non-Vote Txs Found: ${filteredStats.nonVoteTransactions}`);
  console.log(`  Vote Txs Found: ${filteredStats.voteTransactions}`);
}

monitorFilteredEntries().catch(console.error);

Entry Data Structure

  {
    slot: number;              // Which time slot
    index: number;             // Position in that slot
    hash: string;              // Unique ID
    numHashes: number;         // Proof-of-history count
    transactions: Array<{      // List of transactions
      signature: string;
      isVote: boolean;
      meta: {
        err: any;
        fee: number;
      };
    }>;
    tick: boolean;             // Is this a tick entry?
  }
Entries vs Transactions:
  • Entries = groups of transactions
  • Shows you the order things happen
  • Includes timing proof data
Entries vs Blocks:
  • Blocks = many entries combined
  • Entries = smaller pieces inside blocks
  • Blocks show finality
Entries vs Slots:
  • Slots = 400ms time windows
  • One slot = multiple entries
  • Entries = what happened in that time

Performance Considerations

Lots of Data

This stream is busy
  • Many messages per second
  • Never stops during network activity
  • Each entry has multiple transactions
  • You need fast processing

Stay Fast

Keep your code efficient
  • Don’t wait for each entry (use async)
  • Process multiple entries at once
  • Only look at what you need
  • Sample the data if analyzing a lot

Common Use Cases

  • Performance Research
  • Validator Research
  • Network Debugging
Study how fast things workTrack how the network groups transactions. Find patterns that show problems or ways to make things faster.

Filtering Tips

Entry monitoring sends you ALL entries. There’s no built-in filtering. Here’s how to deal with that:
Ways to handle the data:
  • Filter in your code: Only process what you care about
  • Sample it: Look at every 10th or 100th entry for stats
  • Time windows: Only look at specific time periods
  • Slot filtering: Only process certain slots
  • Transaction types: Focus on specific transaction types (see Example 3)

Best Practices

Use entry monitoring for:
  • Deep blockchain research
  • Studying validators
  • Debugging network issues
  • Academic projects
  • Understanding Proof of History
Don’t use it for:
  • Normal apps
  • User interfaces
  • Business features
  • Trading apps
Tips for processing:
  • Build fast code
  • Don’t wait for each entry (use async)
  • Sample if you’re analyzing a lot
  • Watch your memory usage
  • Handle when data comes too fast
Do good analysis:
  • Pick specific things to measure
  • Use sampling for large datasets
  • Calculate rolling averages
  • Look at patterns over time
  • Compare with other data sources

Troubleshooting

Problem: The entry stream is overwhelmingFix it:
  • Filter in your code (see Example 3)
  • Sample the data
  • Use async processing
  • Check your system resources
  • Consider transaction monitoring instead
Problem: Entries don’t have enough contextFix it:
  • Also monitor transactions
  • Check account updates
  • Use block monitoring too
  • Keep your own state

Common Questions

Entries are created constantly as validators process transactions. The Solana network is very active, so you’ll see many entries per second. Use filtering to manage the volume.
Empty entries are part of Solana’s Proof of History mechanism. They help maintain timing even when there are no transactions to process. They’re normal and expected.
Usually yes. Vote transactions are validator consensus votes and typically aren’t relevant for application-level monitoring. See Example 3 for how to filter them.
If you’re falling behind, you’ll see increasing delays between entry creation and processing. Monitor your processing time and queue sizes. Consider sampling or better filtering if you can’t keep up.

Next Steps

Remember: Entry monitoring is for advanced research. Most apps work better with transaction, account, or block monitoring.
I