> ## Documentation Index
> Fetch the complete documentation index at: https://docs.solanatracker.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Transaction Monitoring

> Watch Solana transactions in real time with Yellowstone gRPC — track swaps, transfers, program interactions, and signature events instantly.

## Overview

Transaction monitoring lets you watch Solana transactions as they happen. Track swaps, transfers, success/failures, program calls, and token changes - all in real-time.

<Info>
  **You'll need:** Run `npm install @triton-one/yellowstone-grpc` first
</Info>

## Installation

```bash theme={null}
npm install @triton-one/yellowstone-grpc
```

## Complete Working Example

Here's a ready-to-use transaction monitor that you can customize:

```javascript theme={null}
const Client = require("@triton-one/yellowstone-grpc").default;
const { CommitmentLevel } = require("@triton-one/yellowstone-grpc");

// Example program IDs - replace with programs you want to monitor
const TOKEN_PROGRAM = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
const SYSTEM_PROGRAM = "11111111111111111111111111111111";

// Connect to Yellowstone gRPC
const getClient = async () => {
  let client = false;
  try {
    client = new Client(
      process.env.GRPC_ENDPOINT || "https://grpc.solanatracker.io",
      process.env.GRPC_API_KEY,
      {
        "grpc.max_receive_message_length": 100 * 1024 * 1024,
      }
    );
    const version = await client.getVersion();
    if (version) {
      console.log("Connected to Yellowstone gRPC! Version:", version);
      return client;
    }
  } catch (e) {
    console.error("Failed to connect:", e);
  }
  if (!client) {
    throw new Error("Failed to connect!");
  }
};

// Transaction statistics
const txStats = {
  totalTransactions: 0,
  successfulTx: 0,
  failedTx: 0,
  totalFees: 0,
  totalComputeUnits: 0,
  programCounts: new Map(),
  lastReportTime: Date.now()
};

// Calculate SOL balance changes
function calculateSolChanges(meta) {
  const changes = [];
  
  if (!meta.preBalances || !meta.postBalances) return changes;
  
  meta.preBalances.forEach((preBalance, index) => {
    const postBalance = meta.postBalances[index] || 0;
    const change = (postBalance - preBalance) / 1e9; // Convert lamports to SOL
    
    if (Math.abs(change) > 0.000001) { // Ignore tiny changes
      changes.push({ 
        accountIndex: index,
        change: change
      });
    }
  });
  
  return changes;
}

// Calculate token balance changes
function calculateTokenChanges(meta) {
  const changes = [];
  
  if (!meta.preTokenBalances || !meta.postTokenBalances) return changes;
  
  meta.preTokenBalances.forEach((preBalance, index) => {
    const postBalance = meta.postTokenBalances[index];
    if (preBalance && postBalance && preBalance.mint === postBalance.mint) {
      const preAmount = preBalance.uiTokenAmount?.uiAmount || 0;
      const postAmount = postBalance.uiTokenAmount?.uiAmount || 0;
      const change = postAmount - preAmount;
      
      if (change !== 0) {
        changes.push({ 
          mint: preBalance.mint, 
          change,
          decimals: preBalance.uiTokenAmount?.decimals || 0
        });
      }
    }
  });
  
  return changes;
}

(async () => {
  const client = await getClient();
  const stream = await client.subscribe();

  // Handle stream lifecycle
  const streamClosed = new Promise((resolve, reject) => {
    stream.on("error", (error) => {
      console.error("Stream error:", error);
    });
    stream.on("end", () => {
      console.log("Stream ended");
      resolve();
    });
    stream.on("close", () => {
      console.log("Stream closed");
      resolve();
    });
  });

  // Handle incoming transactions
  stream.on("data", (data) => {
    if (data?.transaction?.transaction) {
      const tx = data.transaction.transaction;
      const slot = data.transaction.slot;
      
      txStats.totalTransactions++;
      
      const success = !tx.meta?.err;
      if (success) {
        txStats.successfulTx++;
      } else {
        txStats.failedTx++;
      }
      
      const fee = tx.meta?.fee || 0;
      const computeUnits = tx.meta?.computeUnitsConsumed || 0;
      
      txStats.totalFees += fee;
      txStats.totalComputeUnits += computeUnits;
      
      // Track which programs were used
      const accountKeys = tx.transaction?.message?.accountKeys || [];
      const instructions = tx.transaction?.message?.instructions || [];
      
      instructions.forEach((ix) => {
        const programId = accountKeys[ix.programIdIndex];
        if (programId) {
          const count = txStats.programCounts.get(programId) || 0;
          txStats.programCounts.set(programId, count + 1);
        }
      });
      
      console.log(`\n[Transaction #${txStats.totalTransactions}]`);
      console.log(`  Signature: ${tx.signature}`);
      console.log(`  Slot: ${slot}`);
      console.log(`  Status: ${success ? '✅ SUCCESS' : '❌ FAILED'}`);
      console.log(`  Fee: ${(fee / 1e9).toFixed(6)} SOL`);
      console.log(`  Compute Units: ${computeUnits.toLocaleString()}`);
      console.log(`  Instructions: ${instructions.length}`);
      
      // Show SOL balance changes
      const solChanges = calculateSolChanges(tx.meta);
      if (solChanges.length > 0) {
        console.log(`  SOL Changes:`);
        solChanges.forEach(change => {
          console.log(`Account ${change.accountIndex}: ${change.change > 0 ? '+' : ''}${change.change.toFixed(6)} SOL`);
        });
      }
      
      // Show token balance changes
      const tokenChanges = calculateTokenChanges(tx.meta);
      if (tokenChanges.length > 0) {
        console.log(`  Token Changes:`);
        tokenChanges.forEach(change => {
          console.log(`${change.mint.slice(0, 8)}... ${change.change > 0 ? '+' : ''}${change.change.toFixed(6)}`);
        });
      }
      
      // Show program IDs used
      if (accountKeys.length > 0) {
        const uniquePrograms = new Set();
        instructions.forEach((ix) => {
          const programId = accountKeys[ix.programIdIndex];
          if (programId) uniquePrograms.add(programId);
        });
        
        if (uniquePrograms.size > 0) {
          console.log(`  Programs Called:`);
          uniquePrograms.forEach(program => {
            const name = program === TOKEN_PROGRAM ? "Token Program" 
              : program === SYSTEM_PROGRAM ? "System Program"
              : program.slice(0, 12) + "...";
            console.log(`    - ${name}`);
          });
        }
      }
      
      // Show error details for failed transactions
      if (!success && tx.meta?.err) {
        console.log(`  Error: ${JSON.stringify(tx.meta.err)}`);
        if (tx.meta?.logMessages && tx.meta.logMessages.length > 0) {
          console.log(`  Last Log: ${tx.meta.logMessages[tx.meta.logMessages.length - 1]}`);
        }
      }
      
      // Report statistics every 5 minutes
      const now = Date.now();
      if (now - txStats.lastReportTime > 300000) {
        const avgFee = txStats.totalTransactions > 0 
          ? txStats.totalFees / txStats.totalTransactions / 1e9
          : 0;
        const successRate = txStats.totalTransactions > 0
          ? (txStats.successfulTx / txStats.totalTransactions * 100)
          : 0;
          
        console.log(`\n=== Transaction Statistics Report ===`);
        console.log(`  Total Transactions: ${txStats.totalTransactions}`);
        console.log(`  Successful: ${txStats.successfulTx}`);
        console.log(`  Failed: ${txStats.failedTx}`);
        console.log(`  Success Rate: ${successRate.toFixed(2)}%`);
        console.log(`  Total Fees: ${(txStats.totalFees / 1e9).toFixed(4)} SOL`);
        console.log(`  Average Fee: ${avgFee.toFixed(6)} SOL`);
        console.log(`  Avg Compute Units: ${(txStats.totalComputeUnits / txStats.totalTransactions).toFixed(0)}`);
        
        console.log(`\n  Most Used Programs:`);
        Array.from(txStats.programCounts.entries())
          .sort((a, b) => b[1] - a[1])
          .slice(0, 5)
          .forEach(([program, count]) => {
            const name = program === TOKEN_PROGRAM ? "Token Program" 
              : program === SYSTEM_PROGRAM ? "System Program"
              : program.slice(0, 12) + "...";
            console.log(`    ${name}: ${count} transactions`);
          });
        
        txStats.lastReportTime = now;
      }
    }
  });

  // Subscribe to transactions
  // Customize the accountInclude array with programs you want to monitor
  const request = {
    accounts: {},
    slots: {},
    transactions: {
      allTransactions: {
        vote: false,
        failed: false,
        signature: undefined,
        accountInclude: [TOKEN_PROGRAM, SYSTEM_PROGRAM], // Add your program IDs here
        accountExclude: [],
        accountRequired: [],
      },
    },
    transactionsStatus: {},
    entry: {},
    blocks: {},
    blocksMeta: {},
    accountsDataSlice: [],
    ping: undefined,
    commitment: CommitmentLevel.CONFIRMED,
  };

  // Send subscribe request
  await new Promise((resolve, reject) => {
    stream.write(request, (err) => {
      if (err === null || err === undefined) {
        console.log("✅ Monitoring transactions\n");
        resolve();
      } else {
        reject(err);
      }
    });
  }).catch((reason) => {
    console.error("Subscribe failed:", reason);
    throw reason;
  });

  await streamClosed;
})();

// Graceful shutdown
process.on('SIGINT', () => {
  console.log('\n\nShutting down gracefully...');
  console.log(`Final Stats - Total: ${txStats.totalTransactions}, Success: ${txStats.successfulTx}, Failed: ${txStats.failedTx}`);
  process.exit(0);
});
```

## How to Filter Transactions

<Tabs>
  <Tab title="By Program">
    **Watch transactions using specific programs:**

    ```javascript theme={null}
        const request = {
          // ... other fields
          transactions: {
            myTransactions: {
              vote: false,
              failed: false,
              accountInclude: [
                "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
                "11111111111111111111111111111111"
              ],
              accountExclude: [],
              accountRequired: [],
            }
          },
          commitment: CommitmentLevel.CONFIRMED,
        };
    ```
  </Tab>

  <Tab title="By Account">
    **Watch transactions affecting certain accounts:**

    ```javascript theme={null}
        const request = {
          // ... other fields
          transactions: {
            walletTransactions: {
              vote: false,
              failed: true, // Include failures
              accountInclude: [
                "YourWalletAddress",
                "YourTokenMintAddress"
              ],
              accountExclude: [],
              accountRequired: [],
            }
          },
          commitment: CommitmentLevel.CONFIRMED,
        };
    ```
  </Tab>

  <Tab title="Advanced">
    **Combine multiple filters:**

    ```javascript theme={null}
        const request = {
          // ... other fields
          transactions: {
            filteredTransactions: {
              vote: false,
              failed: false,
              accountInclude: ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"],
              accountRequired: ["YourProgramId"], // Must include
              accountExclude: ["Vote111111111111111111111111111111111111111"],
            }
          },
          commitment: CommitmentLevel.CONFIRMED,
        };
    ```
  </Tab>
</Tabs>

## More Examples

### Example 2: Track Failed Transactions

Find out why transactions fail:

```javascript theme={null}
const failureStats = {
  total: 0,
  errorTypes: new Map()
};

(async () => {
  const client = await getClient();
  const stream = await client.subscribe();

  const streamClosed = new Promise((resolve, reject) => {
    stream.on("error", (error) => console.error("Stream error:", error));
    stream.on("end", () => resolve());
    stream.on("close", () => resolve());
  });

  stream.on("data", (data) => {
    if (data?.transaction?.transaction?.meta?.err) {
      const tx = data.transaction.transaction;
      const slot = data.transaction.slot;
      
      failureStats.total++;
      
      const errorType = JSON.stringify(tx.meta.err);
      const count = failureStats.errorTypes.get(errorType) || 0;
      failureStats.errorTypes.set(errorType, count + 1);
      
      console.log(`\n[Failed Transaction #${failureStats.total}]`);
      console.log(`  Signature: ${tx.signature}`);
      console.log(`  Slot: ${slot}`);
      console.log(`  Error: ${errorType}`);
      console.log(`  Fee Paid: ${(tx.meta.fee / 1e9).toFixed(6)} SOL`);
      
      // Log messages for debugging
      if (tx.meta?.logMessages) {
        console.log(`  Last 5 Log Messages:`);
        tx.meta.logMessages.slice(-5).forEach((msg) => {
          console.log(`    ${msg}`);
        });
      }
      
      // Report failure patterns every 20 failures
      if (failureStats.total % 20 === 0) {
        console.log(`\n=== Failure Analysis ===`);
        console.log(`  Total Failures: ${failureStats.total}`);
        console.log(`\n  Top Error Types:`);
        Array.from(failureStats.errorTypes.entries())
          .sort((a, b) => b[1] - a[1])
          .slice(0, 5)
          .forEach(([error, count]) => {
            const percentage = (count / failureStats.total * 100).toFixed(1);
            console.log(`    ${error}: ${count} (${percentage}%)`);
          });
      }
    }
  });

  const request = {
    accounts: {},
    slots: {},
    transactions: {
      failedTransactions: {
        vote: false,
        failed: true, // Only failed transactions
        accountInclude: ["YourProgramId"],
        accountExclude: [],
        accountRequired: [],
      },
    },
    transactionsStatus: {},
    entry: {},
    blocks: {},
    blocksMeta: {},
    accountsDataSlice: [],
    ping: undefined,
    commitment: CommitmentLevel.CONFIRMED,
  };

  await new Promise((resolve, reject) => {
    stream.write(request, (err) => {
      if (err === null || err === undefined) {
        console.log("✅ Monitoring failed transactions\n");
        resolve();
      } else {
        reject(err);
      }
    });
  });

  await streamClosed;
})();
```

### Example 3: Watch Big Transfers

Track large SOL transfers:

```javascript theme={null}
const HIGH_VALUE_THRESHOLD = 10; // 10 SOL

const highValueStats = {
  totalMonitored: 0,
  highValueCount: 0,
  totalVolume: 0,
  largestTransfer: 0,
  largestSignature: ""
};

(async () => {
  const client = await getClient();
  const stream = await client.subscribe();

  const streamClosed = new Promise((resolve, reject) => {
    stream.on("error", (error) => console.error("Stream error:", error));
    stream.on("end", () => resolve());
    stream.on("close", () => resolve());
  });

  stream.on("data", (data) => {
    if (data?.transaction?.transaction?.meta) {
      const tx = data.transaction.transaction;
      const slot = data.transaction.slot;
      
      highValueStats.totalMonitored++;
      
      const preBalances = tx.meta.preBalances || [];
      const postBalances = tx.meta.postBalances || [];
      
      // Calculate largest balance change
      let maxChange = 0;
      let changedAccountIndex = -1;
      
      preBalances.forEach((preBalance, index) => {
        const postBalance = postBalances[index] || 0;
        const change = Math.abs(postBalance - preBalance);
        if (change > maxChange) {
          maxChange = change;
          changedAccountIndex = index;
        }
      });
      
      const changeInSOL = maxChange / 1e9;
      
      if (changeInSOL > HIGH_VALUE_THRESHOLD) {
        highValueStats.highValueCount++;
        highValueStats.totalVolume += changeInSOL;
        
        if (changeInSOL > highValueStats.largestTransfer) {
          highValueStats.largestTransfer = changeInSOL;
          highValueStats.largestSignature = tx.signature;
        }
        
        const accountKeys = tx.transaction?.message?.accountKeys || [];
        const changedAccount = accountKeys[changedAccountIndex];
        
        console.log(`\n[High-Value Transaction #${highValueStats.highValueCount}]`);
        console.log(`  Signature: ${tx.signature}`);
        console.log(`  Slot: ${slot}`);
        console.log(`  💰 Transfer Amount: ${changeInSOL.toFixed(2)} SOL`);
        console.log(`  Fee: ${(tx.meta.fee / 1e9).toFixed(6)} SOL`);
        console.log(`  Changed Account: ${changedAccount || 'Unknown'}`);
        
        // Report summary every 10 high-value transactions
        if (highValueStats.highValueCount % 10 === 0) {
          console.log(`\n=== High-Value Summary ===`);
          console.log(`  Monitored: ${highValueStats.totalMonitored} transactions`);
          console.log(`  High-Value: ${highValueStats.highValueCount} transactions`);
          console.log(`  Total Volume: ${highValueStats.totalVolume.toFixed(2)} SOL`);
          console.log(`  Largest Transfer: ${highValueStats.largestTransfer.toFixed(2)} SOL`);
        }
      }
      
      // Log progress every 100 monitored transactions
      if (highValueStats.totalMonitored % 100 === 0) {
        console.log(`\n[Progress] Monitored ${highValueStats.totalMonitored} transactions, found ${highValueStats.highValueCount} high-value`);
      }
    }
  });

  const request = {
    accounts: {},
    slots: {},
    transactions: {
      systemTransactions: {
        vote: false,
        failed: false,
        accountInclude: ["11111111111111111111111111111111"], // System Program
        accountExclude: [],
        accountRequired: [],
      },
    },
    transactionsStatus: {},
    entry: {},
    blocks: {},
    blocksMeta: {},
    accountsDataSlice: [],
    ping: undefined,
    commitment: CommitmentLevel.CONFIRMED,
  };

  await new Promise((resolve, reject) => {
    stream.write(request, (err) => {
      if (err === null || err === undefined) {
        console.log(`✅ Monitoring transactions with transfers > ${HIGH_VALUE_THRESHOLD} SOL\n`);
        resolve();
      } else {
        reject(err);
      }
    });
  });

  await streamClosed;
})();
```

## Understanding Filters

<CardGroup cols={2}>
  <Card title="Include (OR)" icon="plus">
    **accountInclude:** Transaction must touch ANY of these accounts
  </Card>

  <Card title="Required (AND)" icon="check">
    **accountRequired:** Transaction must touch ALL of these accounts
  </Card>

  <Card title="Exclude (NOT)" icon="minus">
    **accountExclude:** Transaction must NOT touch any of these
  </Card>

  <Card title="How They Work Together" icon="code">
    Transaction passes if: (include OR empty) AND (all required) AND NOT (any excluded)
  </Card>
</CardGroup>

## What You Can Build

<Accordion title="Trading Bots">
  Monitor DEX transactions to find trading opportunities, track competitors, or analyze market activity in real-time.
</Accordion>

<Accordion title="Transaction Trackers">
  Watch specific wallets or programs to see every transaction they make. Perfect for portfolio tracking or whale watching.
</Accordion>

<Accordion title="Failure Monitors">
  Track failed transactions to debug your program, understand user issues, or monitor network problems.
</Accordion>

<Accordion title="Volume Analyzers">
  Measure transaction volume, fees, and activity across programs to understand usage patterns and trends.
</Accordion>

<Accordion title="Alert Systems">
  Get instant notifications for high-value transfers, specific program calls, or suspicious activity.
</Accordion>

## Tips for Better Performance

<CardGroup cols={2}>
  <Card title="Pick Right Commitment" icon="check">
    CONFIRMED is usually best - fast but reliable
  </Card>

  <Card title="Filter Smart" icon="filter">
    Use specific program IDs to reduce data
  </Card>

  <Card title="Process Async" icon="bolt">
    Don't block - handle transactions asynchronously
  </Card>

  <Card title="Watch Resources" icon="chart-line">
    Monitor your memory and CPU usage
  </Card>
</CardGroup>

## Common Questions

<Accordion title="How much data will I get?">
  It depends on your filters. Watching all transactions = huge data. Watching one program = manageable. Watching a specific wallet = very little. Always use filters!
</Accordion>

<Accordion title="Can I watch multiple programs at once?">
  Yes! Just add them all to `accountInclude`. The transaction will match if it touches ANY of them.
</Accordion>

<Accordion title="What's the difference between Include and Required?">
  * **Include (OR):** Transaction touches ANY of these accounts
  * **Required (AND):** Transaction touches ALL of these accounts

  Use Include for "I want to see transactions involving A or B or C"
  Use Required for "I only want transactions that involve both A and B"
</Accordion>

<Accordion title="Should I include failed transactions?">
  Usually no - they're just noise. But set `failed: true` if you're debugging or analyzing failure patterns.
</Accordion>

<Accordion title="How do I track a specific wallet?">
  Add the wallet address to `accountInclude`. You'll see every transaction that touches that wallet.
</Accordion>

<Accordion title="Why do I see vote transactions?">
  Set `vote: false` in your filter. Vote transactions are validator consensus votes and usually aren't what you want.
</Accordion>

<Accordion title="What are compute units?">
  Compute units measure how much computation a transaction used. More complex transactions use more compute units and usually cost more.
</Accordion>

## Next Steps

<CardGroup cols={2}>
  <Card title="Account Monitoring" icon="user" href="/yellowstone-grpc/account-monitoring">
    Also watch account changes
  </Card>

  <Card title="Real Example: Pump.fun" icon="chart-line" href="/yellowstone-grpc/examples/pumpfun-transactions">
    See transaction parsing in action
  </Card>

  <Card title="Best Practices" icon="star" href="/yellowstone-grpc/best-practices">
    Optimize your setup
  </Card>

  <Card title="Quickstart" icon="rocket" href="/yellowstone-grpc/quickstart">
    Get started fast
  </Card>
</CardGroup>
