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

# Solana Pump AMM Data Streaming with LaserStream

> Copy-pasteable LaserStream script that streams Pump.fun account and transaction activity in real time, with rolling stats and signature decoding.

Stream every Pump.fun account update and transaction in real time using the [LaserStream SDK](/laserstream/clients). This page walks through a complete TypeScript script you can run end-to-end: it prints each transaction as it lands, reports rolling stats (throughput, unique fee payers, total fees in SOL) once a minute, and stays connected across reconnects automatically.

It's a useful starting point for anything that needs continuous Pump.fun visibility — trading bots, analytics dashboards, alerting pipelines, or just exploring the data shape so you can build something more specialised on top.

<Info>
  **Prerequisites:** A [Helius API key](https://dashboard.helius.dev/) (Business or Professional plan for Mainnet LaserStream) and Node.js 18+. Familiarity with [Account Subscriptions](/laserstream/guides/account-subscription) and [Transaction Monitoring](/laserstream/guides/transaction-monitoring) helps but isn't required.
</Info>

***

## Setup

Create a fresh project, install the SDK + `bs58` (used to decode transaction signatures), and set your API key:

```bash theme={"system"}
mkdir pump-monitor && cd pump-monitor
npm init -y
npm install helius-laserstream bs58
npm install --save-dev typescript ts-node @types/node
npx tsc --init
export HELIUS_API_KEY=your-key-here
```

Pick the endpoint closest to where this script will run — see the [LaserStream regions](/laserstream/grpc#mainnet-endpoints).

***

## The script

Save this as `index.ts`, then run `npx ts-node index.ts`:

```typescript [expandable] theme={"system"}
import { subscribe, CommitmentLevel, LaserstreamConfig, SubscribeRequest } from 'helius-laserstream';
import bs58 from 'bs58';

const PUMP_PROGRAM_ID = '6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P';

// Rolling stats across the session.
const stats = {
  startedAt: Date.now(),
  transactions: 0,
  successes: 0,
  failures: 0,
  uniquePayers: new Set<string>(),
  totalFeesLamports: 0,
  accountUpdates: 0,
};

async function handleUpdate(data: any) {
  if (data.transaction) {
    const tx = data.transaction.transaction;
    if (!tx) return;

    stats.transactions++;
    const failed = !!tx.meta?.err;
    failed ? stats.failures++ : stats.successes++;

    // tx.meta.fee is a u64 string — coerce before adding.
    stats.totalFeesLamports += Number(tx.meta?.fee ?? 0);

    // Fee payer is the first account in the message.
    const firstKey = tx.transaction?.message?.accountKeys?.[0];
    if (firstKey) {
      const payer = typeof firstKey === 'string' ? firstKey : bs58.encode(firstKey);
      stats.uniquePayers.add(payer);
    }

    // tx.signature is a Buffer in the SDK.
    const sig = bs58.encode(tx.signature);
    const slot = data.transaction.slot;
    const flag = failed ? '❌' : '✅';
    console.log(`${flag}  ${sig.slice(0, 12)}…  slot ${slot}  fee ${tx.meta?.fee} lamports`);
  }

  if (data.account) {
    stats.accountUpdates++;
    const acct = data.account.account;
    const pubkey = typeof acct.pubkey === 'string' ? acct.pubkey : bs58.encode(acct.pubkey);
    console.log(`📋  account ${pubkey}  ${acct.data?.length ?? 0} bytes`);
  }
}

function printReport() {
  const minutes = (Date.now() - stats.startedAt) / 60_000;
  console.log('\n📊  Pump.fun activity report');
  console.log(`   Runtime:           ${minutes.toFixed(1)} min`);
  console.log(`   Transactions:      ${stats.transactions} (${stats.successes} ok, ${stats.failures} failed)`);
  console.log(`   Throughput:        ${(stats.transactions / Math.max(minutes, 0.0001)).toFixed(1)} tx/min`);
  console.log(`   Unique fee payers: ${stats.uniquePayers.size}`);
  console.log(`   Total fees:        ${(stats.totalFeesLamports / 1e9).toFixed(4)} SOL`);
  console.log(`   Account updates:   ${stats.accountUpdates}\n`);
}

async function main() {
  const config: LaserstreamConfig = {
    apiKey: process.env.HELIUS_API_KEY ?? 'YOUR_API_KEY',
    endpoint: 'https://laserstream-mainnet-ewr.helius-rpc.com', // pick the region closest to you
  };

  const subscriptionRequest: SubscribeRequest = {
    accounts: {
      'pump-accounts': {
        account: [],
        owner: [PUMP_PROGRAM_ID],
        filters: [],
      },
    },
    transactions: {
      'pump-transactions': {
        accountInclude: [PUMP_PROGRAM_ID],
        accountExclude: [],
        accountRequired: [],
        vote: false,
        failed: false,
      },
    },
    commitment: CommitmentLevel.CONFIRMED,
    slots: {},
    transactionsStatus: {},
    blocks: {},
    blocksMeta: {},
    entry: {},
    accountsDataSlice: [],
  };

  console.log('🚀  Streaming Pump.fun activity. Press Ctrl+C to stop.\n');

  const reportTimer = setInterval(printReport, 60_000);
  process.on('SIGINT', () => {
    clearInterval(reportTimer);
    printReport();
    process.exit(0);
  });

  await subscribe(config, subscriptionRequest, handleUpdate, async (error) => {
    console.error('Stream error:', error);
  });
}

main().catch(console.error);
```

***

## What the script does

### One subscription, two filter blocks

A single `subscribe(...)` call carries two named filter groups:

* The **`accounts`** filter receives every account owned by the Pump.fun program (bonding curves, mint state, etc.) every time their data changes.
* The **`transactions`** filter receives every transaction that interacts with the Pump.fun program. The `vote: false, failed: false` flags drop votes and failed transactions before they reach your handler.

Both arrive over a single gRPC stream, so the SDK only has one connection to manage. If it drops, the SDK reconnects automatically and you keep receiving updates from both filters.

### Reading the raw fields

A couple of values in the payload need a small conversion before they're easy to use:

* **`tx.signature`** is a [`Buffer`](https://nodejs.org/api/buffer.html). Run it through `bs58.encode(...)` to get the base58 signature you'd see in an explorer or in a `getTransaction` response.
* **`tx.meta.fee`** is a u64 stored as a string so values above `Number.MAX_SAFE_INTEGER` don't lose precision in JavaScript. Wrap it in `Number(...)` before doing arithmetic. The same string-for-u64 convention applies to several block-level fields too — see [Slot & Block Monitoring](/laserstream/guides/slot-and-block-monitoring) for the full data shape.

### Rolling stats

A `setInterval` prints a snapshot every 60 seconds: total transactions, successes versus failures, throughput, unique fee payers, and total fees in SOL. Press `Ctrl+C` for one last snapshot before the script exits.

***

## Extending the script

A few directions to take this further:

* **Decode instruction discriminators** to label trades as `buy` / `sell` / `create` — see [Decoding Transaction Data](/laserstream/guides/decoding-transaction-data) for the parsing pattern.
* **Persist** the rolling stats to a database (Postgres, Redis, ClickHouse) instead of in-memory `Set` / counters.
* **Alert** on thresholds — large fee payers, sudden volume spikes, new bonding-curve accounts.
* **Replay** missed activity on startup by setting `fromSlot` on `SubscribeRequest` (up to 24 hours back — see [Historical Replay](/laserstream/historical-replay)).
* **Combine** with [Slot & Block Monitoring](/laserstream/guides/slot-and-block-monitoring) for block-level context.

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Transaction Monitoring" icon="receipt" href="/laserstream/guides/transaction-monitoring">
    The general transaction-filtering patterns this script uses.
  </Card>

  <Card title="Decoding Transaction Data" icon="binary" href="/laserstream/guides/decoding-transaction-data">
    Parse the binary transaction payload into readable Solana transactions.
  </Card>

  <Card title="Account Subscriptions" icon="user" href="/laserstream/guides/account-subscription">
    Watch specific account state changes with filters.
  </Card>

  <Card title="Historical Replay" icon="clock-rotate-left" href="/laserstream/historical-replay">
    Replay up to 24h of past Pump.fun activity from a starting slot.
  </Card>
</CardGroup>
