> ## 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.

# Truyền phát dữ liệu Solana Pump AMM bằng LaserStream

> Tập lệnh LaserStream có thể sao chép và dán để truyền phát hoạt động tài khoản và giao dịch Pump.fun theo thời gian thực, kèm số liệu thống kê liên tục và giải mã chữ ký.

Truyền phát mọi cập nhật tài khoản và giao dịch Pump.fun theo thời gian thực bằng [LaserStream SDK](/docs/vi/laserstream/clients). Trang này hướng dẫn một tập lệnh TypeScript hoàn chỉnh mà bạn có thể chạy từ đầu đến cuối: tập lệnh in từng giao dịch ngay khi giao dịch xuất hiện, báo cáo số liệu thống kê liên tục (thông lượng, số người trả phí duy nhất, tổng phí bằng SOL) mỗi phút một lần và tự động duy trì kết nối qua các lần kết nối lại.

Đây là điểm khởi đầu hữu ích cho mọi ứng dụng cần khả năng theo dõi Pump.fun liên tục — bot giao dịch, bảng điều khiển phân tích, quy trình cảnh báo hoặc đơn giản là khám phá cấu trúc dữ liệu để xây dựng một giải pháp chuyên biệt hơn dựa trên đó.

<Info>
  **Điều kiện tiên quyết:** Khóa [Helius API](https://dashboard.helius.dev/) (gói Business hoặc Professional dành cho Mainnet LaserStream) và Node.js 18 trở lên. Việc quen thuộc với [Đăng ký tài khoản](/docs/vi/laserstream/guides/account-subscription) và [Giám sát giao dịch](/docs/vi/laserstream/guides/transaction-monitoring) sẽ hữu ích nhưng không bắt buộc.
</Info>

***

## Thiết lập

Tạo một dự án mới, cài đặt SDK + `bs58` (được dùng để giải mã chữ ký giao dịch) và đặt khóa API:

```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
```

Chọn điểm cuối gần nhất với nơi tập lệnh này sẽ chạy — xem [các khu vực LaserStream](/docs/vi/laserstream/grpc#điểm-cuối-mainnet).

***

## Tập lệnh

Lưu nội dung này dưới dạng `index.ts`, sau đó chạy `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);
```

***

## Cách tập lệnh hoạt động

### Một lượt đăng ký, hai khối bộ lọc

Một lệnh gọi `subscribe(...)` duy nhất chứa hai nhóm bộ lọc có tên:

* Bộ lọc **`accounts`** nhận mọi tài khoản thuộc sở hữu của chương trình Pump.fun (đường cong liên kết, trạng thái mint, v.v.) mỗi khi dữ liệu của chúng thay đổi.
* Bộ lọc **`transactions`** nhận mọi giao dịch tương tác với chương trình Pump.fun. Các cờ `vote: false, failed: false` loại bỏ phiếu bầu và giao dịch thất bại trước khi chúng đến trình xử lý của bạn.

Cả hai đều được truyền qua một luồng gRPC duy nhất, vì vậy SDK chỉ cần quản lý một kết nối. Nếu kết nối bị gián đoạn, SDK sẽ tự động kết nối lại và bạn tiếp tục nhận cập nhật từ cả hai bộ lọc.

### Đọc các trường thô

Một vài giá trị trong tải trọng cần được chuyển đổi đôi chút trước khi có thể sử dụng dễ dàng:

* **`tx.signature`** là một [`Buffer`](https://nodejs.org/api/buffer.html). Truyền giá trị này qua `bs58.encode(...)` để lấy chữ ký base58 mà bạn sẽ thấy trong trình khám phá hoặc trong phản hồi `getTransaction`.
* **`tx.meta.fee`** là một giá trị u64 được lưu dưới dạng chuỗi để các giá trị lớn hơn `Number.MAX_SAFE_INTEGER` không bị mất độ chính xác trong JavaScript. Bọc giá trị đó trong `Number(...)` trước khi thực hiện phép toán. Quy ước dùng chuỗi cho u64 tương tự cũng áp dụng cho một số trường cấp khối — xem [Giám sát slot và khối](/docs/vi/laserstream/guides/slot-and-block-monitoring) để biết cấu trúc dữ liệu đầy đủ.

### Số liệu thống kê liên tục

Một `setInterval` in ảnh chụp số liệu sau mỗi 60 giây: tổng số giao dịch, số giao dịch thành công so với thất bại, thông lượng, số người trả phí duy nhất và tổng phí bằng SOL. Nhấn `Ctrl+C` để in ảnh chụp cuối cùng trước khi tập lệnh thoát.

***

## Mở rộng tập lệnh

Một vài hướng để phát triển thêm:

* **Giải mã các bộ phân biệt lệnh** để gắn nhãn giao dịch là `buy` / `sell` / `create` — xem [Giải mã dữ liệu giao dịch](/docs/vi/laserstream/guides/decoding-transaction-data) để biết mẫu phân tích cú pháp.
* **Lưu trữ lâu dài** số liệu thống kê liên tục vào cơ sở dữ liệu (Postgres, Redis, ClickHouse) thay vì dùng `Set` / bộ đếm trong bộ nhớ.
* **Cảnh báo** khi đạt ngưỡng — người trả phí lớn, khối lượng tăng đột biến, tài khoản đường cong liên kết mới.
* **Phát lại** hoạt động bị bỏ lỡ khi khởi động bằng cách đặt `fromSlot` trên `SubscribeRequest` (tối đa 48 giờ trước — xem [Phát lại lịch sử](/docs/vi/laserstream/historical-replay)).
* **Kết hợp** với [Giám sát slot và khối](/docs/vi/laserstream/guides/slot-and-block-monitoring) để có ngữ cảnh cấp khối.
* **Theo dõi một token** thay vì toàn bộ chương trình: đặt mint trong `accountInclude` và đặt `matchMints: true` trên bộ lọc giao dịch để nhận mọi thao tác chuyển, hoán đổi, mint và đốt của token đó (xem [Lọc theo token mint](/docs/vi/laserstream/mint-filtering)).

***

## Các bước tiếp theo

<CardGroup cols={2}>
  <Card title="Transaction Monitoring" icon="receipt" href="/docs/vi/laserstream/guides/transaction-monitoring">
    Các mẫu lọc giao dịch chung mà tập lệnh này sử dụng.
  </Card>

  <Card title="Decoding Transaction Data" icon="binary" href="/docs/vi/laserstream/guides/decoding-transaction-data">
    Phân tích tải trọng giao dịch nhị phân thành các giao dịch Solana dễ đọc.
  </Card>

  <Card title="Account Subscriptions" icon="user" href="/docs/vi/laserstream/guides/account-subscription">
    Theo dõi các thay đổi trạng thái của tài khoản cụ thể bằng bộ lọc.
  </Card>

  <Card title="Historical Replay" icon="clock-rotate-left" href="/docs/vi/laserstream/historical-replay">
    Phát lại tối đa 24 giờ hoạt động Pump.fun trước đây từ một slot bắt đầu.
  </Card>
</CardGroup>
