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

# Cara Menggunakan transactionSubscribe

> Streaming pembaruan transaksi Solana secara real-time dengan `transactionSubscribe`. Pantau aktivitas blockchain, filter berdasarkan akun, dan terima notifikasi secara instan.

## Apa itu `transactionSubscribe`?

Metode WebSocket `transactionSubscribe` (ekstensi Helius untuk API WebSocket Solana standar) memungkinkan peristiwa transaksi secara real-time.

Untuk menggunakannya, berikan `TransactionSubscribeFilter` dan sertakan `TransactionSubscribeOptions` secara opsional untuk penyesuaian lebih lanjut.

`transactionSubscribe` tersedia pada [endpoint](https://www.helius.dev/docs/api-reference/endpoints) `wss://mainnet.helius-rpc.com` dan `wss://devnet.helius-rpc.com` terpadu yang sama dengan metode langganan Solana standar.

### `TransactionSubscribeFilter`

* `vote`: flag boolean untuk menyertakan/mengecualikan transaksi terkait voting
* `failed`: flag boolean untuk menyertakan/mengecualikan transaksi yang gagal
* `signature`: memfilter pembaruan ke transaksi tertentu berdasarkan tanda tangannya
* `accountInclude`: daftar akun yang pembaruan transaksinya ingin Anda terima. Hanya salah satu akun yang harus disertakan dalam pembaruan transaksi (misalnya, Akun 1 ATAU 2).
* `accountExclude`: daftar akun yang ingin Anda kecualikan dari pembaruan transaksi
* `accountRequired`: transaksi harus menyertakan semua akun yang ditentukan agar disertakan dalam pembaruan (misalnya, Akun 1 DAN 2)
* `tokenAccounts`: perluasan associated token account (ATA) yang dapat diaktifkan (`balanceChanged`, `all`, atau `none`). Lihat [Memantau dompet, termasuk transfer token](#memantau-dompet-termasuk-transfer-token) di bawah.

<Tip>
  Anda dapat menyertakan hingga 50.000 alamat dalam array `accountInclude`, `accountExclude`, dan `accountRequired`.
</Tip>

### TransactionSubscribeOptions (Opsional)

* `commitment`: tingkat commitment untuk mengambil data (`processed`, `confirmed`, atau `finalized`)
* `encoding`: format encoding data yang dikembalikan (`base58`, `base64`, atau `jsonParsed`)
* `transactionDetails`: tingkat detail data yang dikembalikan (`full`, `signatures`, `accounts`, dan `none`)
* `showRewards`: flag boolean yang menunjukkan apakah data reward harus disertakan dalam pembaruan
* `maxSupportedTransactionVersion`: menentukan versi transaksi tertinggi yang pembaruannya ingin Anda terima. Tetapkan nilainya ke `1` untuk menerima transaksi legacy, v0, dan v1. Lihat [Dukungan transaksi v1](/docs/id/rpc/transaction-v1).

<Info>
  `maxSupportedTransactionVersion` diperlukan untuk mengembalikan akun dan detail tingkat penuh dari transaksi tertentu (yaitu, `transactionDetails: "accounts" | "full"`).
</Info>

## Contoh Langganan Transaksi

Dalam contoh ini, kami berlangganan transaksi yang memuat akun Raydium `675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8`.

Ketika terjadi transaksi yang memuat akun `675k...1Mp8` dalam `accountKeys` transaksi tersebut, kami akan menerima notifikasi WSS.

Berdasarkan opsi langganan, notifikasi transaksi akan dikirim pada tingkat commitment `processed`, menggunakan encoding `jsonParsed` dan detail transaksi `full`, serta akan menampilkan reward.

<CodeGroup>
  ```javascript theme={"system"}
  const WebSocket = require('ws');

  // Create a WebSocket connection
  const ws = new WebSocket('wss://mainnet.helius-rpc.com/?api-key=<API_KEY>');

  // Function to send a request to the WebSocket server
  function sendRequest(ws) {
      const request = {
          jsonrpc: "2.0",
          id: 420,
          method: "transactionSubscribe",
          params: [
              {
                  accountInclude: ["675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"]
              },
              {
                  commitment: "processed",
                  encoding: "jsonParsed",
                  transactionDetails: "full",
                  showRewards: true,
                  maxSupportedTransactionVersion: 1
              }
          ]
      };
      ws.send(JSON.stringify(request));
  }

  // Function to send a ping to the WebSocket server
  function startPing(ws) {
      setInterval(() => {
          if (ws.readyState === WebSocket.OPEN) {
              ws.ping();
              console.log('Ping sent');
          }
      }, 30000); // Ping every 30 seconds
  }

  // Define WebSocket event handlers

  ws.on('open', function open() {
      console.log('WebSocket is open');
      sendRequest(ws);  // Send a request once the WebSocket is open
      startPing(ws);    // Start sending pings
  });

  ws.on('message', function incoming(data) {
      const messageStr = data.toString('utf8');
      try {
          const messageObj = JSON.parse(messageStr);
          console.log('Received:', messageObj);
      } catch (e) {
          console.error('Failed to parse JSON:', e);
      }
  });

  ws.on('error', function error(err) {
      console.error('WebSocket error:', err);
  });

  ws.on('close', function close() {
      console.log('WebSocket is closed');
  });
  ```
</CodeGroup>

### Contoh Notifikasi

<CodeGroup>
  ```json theme={"system"}
  {
      "jsonrpc": "2.0",
      "method": "transactionNotification",
      "params": {
          "subscription": 4743323479349712,
          "result": {
              "transaction": {
                  "transaction": [
                      "Ae6zfSExLsJ/E1+q0jI+3ueAtSoW+6HnuDohmuFwagUo2BU4OpkSdUKYNI1dJfMOonWvjaumf4Vv1ghn9f3Avg0BAAEDGycH0OcYRpfnPNuu0DBQxTYPWpmwHdXPjb8y2P200JgK3hGiC2JyC9qjTd2lrug7O4cvSRUVWgwohbbefNgKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0HcpwKokfYDDAJTaF/TWRFWm0Gz5/me17PRnnywHurMBAgIAAQwCAAAAoIYBAAAAAAA=",
                      "base64"
                  ],
                  "meta": {
                      "err": null,
                      "status": {
                          "Ok": null
                      },
                      "fee": 5000,
                      "preBalances": [
                          28279852264,
                          158122684,
                          1
                      ],
                      "postBalances": [
                          28279747264,
                          158222684,
                          1
                      ],
                      "innerInstructions": [],
                      "logMessages": [
                          "Program 11111111111111111111111111111111 invoke [1]",
                          "Program 11111111111111111111111111111111 success"
                      ],
                      "preTokenBalances": [],
                      "postTokenBalances": [],
                      "rewards": null,
                      "loadedAddresses": {
                          "writable": [],
                          "readonly": []
                      },
                      "computeUnitsConsumed": 0
                  }
              },
              "signature": "5moMXe6VW7L7aQZskcAkKGQ1y19qqUT1teQKBNAAmipzdxdqVLAdG47WrsByFYNJSAGa9TByv15oygnqYvP6Hn2p",
              "slot": 224341380,
              "transactionIndex": 42
          }
      }
  }
  ```
</CodeGroup>

## Memantau Dompet, Termasuk Transfer Token

Saat Anda memantau dompet dengan `accountInclude`, hanya transaksi yang kunci publik dompetnya muncul secara langsung dalam kunci akun yang akan cocok. Satu kasus umum dapat terlewat: ketika seseorang mengirim token SPL (misalnya USDC) ke dompet tersebut, transfer menyentuh **associated token account (ATA)** milik dompet, bukan kunci publik dompet—sehingga langganan `accountInclude: [wallet]` biasa tidak pernah mendeteksinya.

Tetapkan bidang `tokenAccounts` untuk memperluas pencocokan agar akun yang dipantau juga cocok dengan transaksi ketika akun tersebut **memiliki** saldo token:

* `balanceChanged`: cocok ketika dompet memiliki saldo token yang jumlahnya berubah (atau yang akun tokennya ditutup) dalam transaksi. Gunakan opsi ini untuk "beri tahu saya saat dana benar-benar berpindah." Ini adalah pilihan yang lebih sempit, dengan volume lebih rendah, dan paling umum.
* `all`: cocok dengan transaksi apa pun yang merujuk pada saldo token milik dompet, meskipun tidak berubah. Volumenya lebih tinggi.
* `none`: tanpa perluasan. Sama seperti menghilangkan bidang tersebut (nilai default).

Pencocokan didasarkan pada pemilik: metode ini menangkap akun token apa pun yang dimiliki dompet, termasuk akun nonkanonis, bukan hanya alamat ATA turunan. Nilai yang tidak valid mengembalikan kesalahan JSON-RPC `-32602`. Langganan yang tidak menyertakan `tokenAccounts` berperilaku sama persis seperti sebelumnya. Untuk ringkasan lengkap tentang cara kerja perluasan ATA, lihat [Pemfilteran Akun Token (ATA) melalui WebSocket](/docs/id/rpc/websocket/token-account-filtering).

```javascript theme={"system"}
const ws = new WebSocket('wss://mainnet.helius-rpc.com/?api-key=<API_KEY>');

ws.on('open', () => {
  ws.send(JSON.stringify({
    jsonrpc: '2.0',
    id: 1,
    method: 'transactionSubscribe',
    params: [
      {
        accountInclude: ['<WALLET_PUBKEY>'],
        tokenAccounts: 'balanceChanged' // also match the wallet's ATAs
      },
      { commitment: 'confirmed', encoding: 'jsonParsed', maxSupportedTransactionVersion: 1 }
    ]
  }));
  setInterval(() => ws.ping(), 30_000);
});

ws.on('message', (data) => {
  const msg = JSON.parse(data.toString());
  const result = msg.params?.result;
  if (!result) return;
  // Token balances this wallet owns that changed in the tx
  const owned = (result.transaction.meta.postTokenBalances || [])
    .filter((b) => b.owner === '<WALLET_PUBKEY>');
  console.log(result.signature, owned);
});
```

## Memantau DCA Jupiter Baru

DCA Jupiter, atau Dollar Cost Averaging, adalah cara untuk menjadwalkan perdagangan berulang di Solana. Karena order beli/jual terjadwal ini dicatat secara on-chain, trader dapat menggunakan metode `transactionSubscribe` dan [`getAsset`](/docs/id/api-reference/das/getasset) untuk memantau order baru.

<CodeGroup>
  ```javascript theme={"system"}
  const WebSocket = require('ws');   
  const bs58      = require('bs58').default;

  /* ───────────────────── 1.  CONFIG ──────────────────────────── */
  const API_KEY   = process.env.HELIUS_API_KEY || (() => { throw new Error('Set HELIUS_API_KEY'); })();
  const HELIUS_WS  = `wss://mainnet.helius-rpc.com?api-key=${API_KEY}`;
  const HELIUS_RPC = `https://mainnet.helius-rpc.com/?api-key=${API_KEY}`;
  const DCA_PROGRAM_ID = 'DCA265Vj8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M';

  /* ───────────────────── 2.  BINARY DECODER ──────────────────── */
  function decodeOpenDcaV2(base58Data) {
    const buf = Buffer.from(bs58.decode(base58Data));
    return {
      appIdx:    buf.readBigUInt64LE(8), // Application Index
      inAmount:  buf.readBigUInt64LE(16), // Input Amount
      perCycle:  buf.readBigUInt64LE(24), // Per Cycle
      interval:  buf.readBigUInt64LE(32) // Interval
    };
  }

  const TOKEN_META = new Map();   // mint → { symbol, decimals }
  /**
   * Fetch symbol & decimals for a mint once then cache.
   * Uses Helius getAsset DAS method: https://www.helius.dev/docs/api-reference/das/getasset
   */
  async function getMeta(mint) {
    if (TOKEN_META.has(mint)) return TOKEN_META.get(mint);

    const body = {
      jsonrpc: '2.0',
      id:      'meow',
      method:  'getAsset',
      params:  { id: mint, displayOptions: { showFungible: true } }
    };

    const { result } = await fetch(HELIUS_RPC, {
      method:  'POST',
      headers: { 'Content-Type': 'application/json' },
      body:    JSON.stringify(body)
    }).then(r => r.json());

    const tokenInfo = result.token_info || {};
    const metadata = { symbol: tokenInfo.symbol || '?', decimals: tokenInfo.decimals ?? 0 };
    TOKEN_META.set(mint, metadata);
    return metadata;
  }

  /* ───────────────────── 4.  PRETTY HELPERS ──────────────────── */
  function formatTimestamp(unixSeconds) {
      return new Date(Number(unixSeconds) * 1_000)
               .toISOString()
               .replace('T', ' ')
               .replace('.000Z', ' UTC');
  }
  function formatInterval(seconds) {
      if (seconds % 86_400 === 0) return `every ${seconds / 86_400}d`;
      if (seconds %  3_600 === 0) return `every ${seconds /  3_600}h`;
      if (seconds %     60 === 0) return `every ${seconds /     60}m`;
      return `every ${seconds}s`;
    }

    function formatAmount(raw, decimals, symbol) {
      const ui = Number(raw) / 10 ** decimals;
      return `${ui} ${symbol}`;
    }
  /* ───────────────────── 5.  WEBSOCKET SETUP ─────────────────── */
  const ws = new WebSocket(HELIUS_WS);

  ws.on('open', () => {
    ws.send(JSON.stringify({
      jsonrpc: '2.0',
      id:      1,
      method:  'transactionSubscribe',
      params: [
        { failed: false, accountInclude: [DCA_PROGRAM_ID] },
        {
          commitment: 'confirmed',
          encoding:   'jsonParsed',
          transactionDetails: 'full',
          maxSupportedTransactionVersion: 1
        }
      ]
    }));

    setInterval(() => ws.ping(), 10_000);
  });

  /* ───────────────────── 6.  MAIN MESSAGE HANDLER ────────────── */
  ws.on('message', async raw => {
    const payload = JSON.parse(raw);
    const result  = payload.params?.result;
    if (!result) return;

    // Look for the `OpenDcaV2` log message
    const logs = result.transaction.meta.logMessages || [];
    if (!logs.some(l => l.includes('OpenDcaV2'))) return;

    // loop through all instructions in the transaction to find the DCA instruction
    for (const ix of result.transaction.transaction.message.instructions) {
      if (ix.programId !== DCA_PROGRAM_ID) continue;

      try {
        // 1) decode binary payload
        const d = decodeOpenDcaV2(ix.data);

        // 2) fetch token symbols / decimals (cached)
        const [inMeta, outMeta] = await Promise.all([
          getMeta(ix.accounts[3]),   // input mint
          getMeta(ix.accounts[4])    // output mint
        ]);

        // 3) create a nice looking table
        console.table({
          user:        ix.accounts[2],
          pair:        `${inMeta.symbol} → ${outMeta.symbol}`,
          opened:      formatTimestamp(d.appIdx),
          'total in':  formatAmount(d.inAmount,  inMeta.decimals, inMeta.symbol),
          'per cycle': formatAmount(d.perCycle,  inMeta.decimals, inMeta.symbol),
          interval:    formatInterval(Number(d.interval))
        });
      } catch (e) {}
    }
  });

  ws.on('error', console.error);

  ws.on('close', () => process.exit(1));
  ```
</CodeGroup>

### Contoh Notifikasi

<Frame>
  <img src="https://mintcdn.com/helius/RGuN9Tphu9J_7kRM/images/enhanced-websockets-example-1.png?fit=max&auto=format&n=RGuN9Tphu9J_7kRM&q=85&s=0cbc0eb2c0eecf83b37217011cb9e3c7" alt="Terminal tables of new Jupiter DCA orders showing the user wallet, token pair, open time, total input, amount per cycle, and interval" width="566" height="622" data-path="images/enhanced-websockets-example-1.png" />
</Frame>

## Memantau Token pump.fun Baru

<CodeGroup>
  ```javascript theme={"system"}
  const WebSocket = require('ws');

  const KEY    = process.env.HELIUS_API_KEY ?? (() => { throw new Error('Set HELIUS_API_KEY'); })();
  const WS_URL = `wss://mainnet.helius-rpc.com?api-key=${KEY}`;
  const PUMP_FUN_PROG = '6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P';

  /* ────────── 2.  OPEN WEBSOCKET & SUBSCRIBE ──────────────────── */
  const ws = new WebSocket(WS_URL);

  ws.on('open', () => {
    ws.send(JSON.stringify({
      jsonrpc : '2.0',
      id      : 1,
      method  : 'transactionSubscribe',
      params  : [
        { failed:false, accountInclude:[PUMP_FUN_PROG] },
        { commitment:'confirmed', encoding:'jsonParsed',
          transactionDetails:'full', maxSupportedTransactionVersion:1 }
      ]
    }));
    // ping every 10 s so we don't get dropped
    setInterval(() => ws.ping(), 10_000);
  });

  /* ────────── 3.  MESSAGE HANDLER ─────────────────────────────── */
  ws.on('message', raw => {
    const payload = JSON.parse(raw);
    const result  = payload.params?.result;
    if (!result) return;

    const logs = result.transaction.meta.logMessages || [];
    // filter for the pump.fun "InitializeMint2" log
    if (!logs.some(l => l.includes('Instruction: InitializeMint2'))) return;

    const sig   = result.signature;    // transaction signature
    const keys  = result.transaction.transaction.message.accountKeys
                               .map(k => k.pubkey);
    //   keys[0] → creator wallet
    //   keys[1] → the new token
    console.table({
      tx:      sig,
      creator: keys[0],
      token:   keys[1]
    });
  });

  ws.on('error', console.error);
  ws.on('close', () => process.exit(1));  
  ```
</CodeGroup>

### Contoh Notifikasi

<Frame>
  <img src="https://mintcdn.com/helius/RGuN9Tphu9J_7kRM/images/enhanced-websockets-example-2.png?fit=max&auto=format&n=RGuN9Tphu9J_7kRM&q=85&s=8febc28503381b0da3cd0f1bb40459cb" alt="Terminal tables of newly created pump.fun tokens showing the transaction signature, creator wallet, and token mint address" width="738" height="355" data-path="images/enhanced-websockets-example-2.png" />
</Frame>

## Mengelola Langganan

### ID Langganan

Ketika `transactionSubscribe` berhasil, server mengembalikan ID langganan dalam bidang `result`. Ini adalah nomor yang sama dengan yang muncul dalam `params.subscription` pada setiap notifikasi dari langganan tersebut:

<CodeGroup>
  ```json Subscribe Response theme={"system"}
  {
    "jsonrpc": "2.0",
    "result": 4743323479349712,
    "id": 420
  }
  ```

  ```json Notification theme={"system"}
  {
    "jsonrpc": "2.0",
    "method": "transactionNotification",
    "params": {
      "subscription": 4743323479349712,
      "result": {}
    }
  }
  ```
</CodeGroup>

Simpan ID langganan dari respons. Anda memerlukannya untuk berhenti berlangganan.

### Berhenti Berlangganan

Untuk berhenti menerima notifikasi, panggil `transactionUnsubscribe` dengan ID langganan. Setiap panggilan `transactionSubscribe` pada koneksi yang sama membuat langganan terpisah dengan ID-nya sendiri. Jadi, pastikan Anda berhenti berlangganan sebelum berlangganan kembali agar tidak menerima notifikasi duplikat.

<CodeGroup>
  ```json Request theme={"system"}
  {
    "jsonrpc": "2.0",
    "id": 421,
    "method": "transactionUnsubscribe",
    "params": [4743323479349712]
  }
  ```

  ```json Response theme={"system"}
  {
    "jsonrpc": "2.0",
    "result": true,
    "id": 421
  }
  ```
</CodeGroup>

Dalam contoh ini, kami berlangganan transaksi Raydium, mengambil ID langganan dari respons server, lalu berhenti berlangganan menggunakan ID tersebut. Beberapa pesan yang sedang diproses mungkin masih tiba sesaat setelah memanggil `transactionUnsubscribe`. Ini adalah perilaku yang diharapkan.

<CodeGroup>
  ```javascript theme={"system"}
  const WebSocket = require('ws');

  const ws = new WebSocket('wss://mainnet.helius-rpc.com/?api-key=<API_KEY>');
  let subscriptionId = null;

  ws.on('open', () => {
      ws.send(JSON.stringify({
          jsonrpc: '2.0',
          id: 420,
          method: 'transactionSubscribe',
          params: [
              { accountInclude: ['675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8'] },
              {
                  commitment: 'processed',
                  encoding: 'jsonParsed',
                  transactionDetails: 'full',
                  maxSupportedTransactionVersion: 1,
              },
          ],
      }));
      setInterval(() => ws.ping(), 30000);
  });

  ws.on('message', (data) => {
      const msg = JSON.parse(data.toString());

      // Capture the subscription ID from the subscribe response
      if (msg.id === 420 && msg.result !== undefined) {
          subscriptionId = msg.result;
          console.log('Subscribed, ID:', subscriptionId);
          return;
      }

      // Handle transaction notifications
      if (msg.method === 'transactionNotification') {
          console.log('Received:', msg.params.result.signature);
      }
  });

  function unsubscribe() {
      if (subscriptionId !== null) {
          ws.send(JSON.stringify({
              jsonrpc: '2.0',
              id: 421,
              method: 'transactionUnsubscribe',
              params: [subscriptionId],
          }));
          subscriptionId = null;
      }
  }
  ```
</CodeGroup>
