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

# LaserStreamによるトランザクション監視

> LaserStreamでSolanaトランザクションをリアルタイムにストリーム — プログラムフィルタリング、実行詳細、トークン残高変更、再接続安全なリプレイ。

トランザクション監視により、トランザクションの実行、成功/失敗ステータス、プログラムとの相互作用、Solana全体のトークン残高変更をリアルタイムで追跡できます。このガイドでは、[`helius-laserstream`](/ja/laserstream/clients) SDKを使用したフィルタリング戦略と実践的な実装を紹介します。

<Info>
  **前提条件:** このガイドは、[LaserStream gRPC クイックスタート](/ja/laserstream/grpc)を完了し、APIキーを持っていることを前提としています。
</Info>

***

## トランザクションフィルタリングオプション

LaserStreamは、Yellowstone gRPCと同じフィルタ形状を使用し、`tokenAccounts` (ATA拡張)フィルタを含みます。あなたが設定するフィールドは次のとおりです:

* **`accountInclude`** — これらのアカウントがいずれかが出現した場合に一致 (論理OR)
* **`accountRequired`** — これらのアカウントがすべて出現した場合のみ一致 (論理AND)
* **`accountExclude`** — これらのアカウントのいずれかが出現した場合に除外
* **`vote` / `failed`** — 投票と失敗したトランザクションのためのブールフラグ
* **`tokenAccounts`** — オプトイン関連トークンアカウント(ATA)拡張 (`"balanceChanged"`, `"all"`, または `"none"`)、したがって `accountInclude` ウォレットは、SPLトークン残高を保有するトランザクションも一致します。詳細は[トークンアカウント(ATA)フィルタリング](/ja/laserstream/token-account-filtering) と下の**ウォレットの監視**タブをご覧ください。

<Tabs>
  <Tab title="プログラムフィルタリング">
    **特定のプログラムに関与するトランザクションを監視**

    関心のあるプログラムに触れるすべてのトランザクションを監視します。

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

    const subscriptionRequest: SubscribeRequest = {
      transactions: {
        "program-filter": {
          accountInclude: [
            "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", // Token Program
            "11111111111111111111111111111111",              // System Program
            "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"  // Your program
          ],
          accountExclude: [],
          accountRequired: [],
          vote: false,
          failed: false
        }
      },
      commitment: CommitmentLevel.CONFIRMED,
      accounts: {},
      slots: {},
      transactionsStatus: {},
      blocks: {},
      blocksMeta: {},
      entry: {},
      accountsDataSlice: [],
    };
    ```

    **最適用途:** プログラム固有の監視、DeFiプロトコルトラッキング、スマートコントラクトの相互作用。
  </Tab>

  <Tab title="アカウント固有">
    **特定のアカウントに影響するトランザクションを監視**

    ```typescript theme={"system"}
    const subscriptionRequest: SubscribeRequest = {
      transactions: {
        "wallet-filter": {
          accountInclude: [
            "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC mint
            "YourWalletAddress"                                // Your wallet
          ],
          accountExclude: [],
          accountRequired: [],
          vote: false,
          failed: true // Include failures to track errors
        }
      },
      commitment: CommitmentLevel.CONFIRMED,
      accounts: {}, slots: {}, transactionsStatus: {},
      blocks: {}, blocksMeta: {}, entry: {}, accountsDataSlice: [],
    };
    ```

    **使用例:** ウォレット監視、トークン発行監視、アカウント活動ダッシュボード。
  </Tab>

  <Tab title="高度なフィルタリング">
    **複数のフィルタ基準を組み合わせる**

    ```typescript theme={"system"}
    const subscriptionRequest: SubscribeRequest = {
      transactions: {
        "advanced-filter": {
          accountInclude: ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"],
          accountRequired: ["YourProgramId"], // Must include this program
          accountExclude: ["VoteProgram"],     // Exclude vote-related txs
          vote: false,
          failed: false
        }
      },
      commitment: CommitmentLevel.CONFIRMED,
      accounts: {}, slots: {}, transactionsStatus: {},
      blocks: {}, blocksMeta: {}, entry: {}, accountsDataSlice: [],
    };
    ```

    **フィルタロジック:** `accountInclude` (OR) **AND** `accountRequired` (AND) **AND NOT** `accountExclude`。
  </Tab>

  <Tab title="ウォレットの監視">
    **直接活動のみならず、受信トークン転送もキャッチ**

    `accountInclude` はウォレットが直接アカウントキーに出現するトランザクションのみ一致します。誰かがウォレットにSPLトークンを送信すると、転送はウォレットの\*\*関連トークンアカウント(ATA)\*\*に触れ、ウォレット公開鍵には触れません—そのため平凡な`accountInclude: [wallet]` では捕えられません。

    ウォレットがトークン残高を**所有する**場合のトランザクションにも一致するように`tokenAccounts`を設定します。これは文字列をとります:

    * **`"balanceChanged"`** — 保有トークンのバランスが変更された場合に一致 (またはトークンアカウントが閉鎖された場合)。"実際にマネーが動いたら教えて" のために最適 — より狭く、より低いボリューム、推奨のデフォルト。

    * **`"all"`** — 変更がなくても保有トークンバランスを参照するあらゆるトランザクションに一致。実質的に高いボリューム。

    * **`"none"`** — 拡張なし (フィールドを省略するのと同じ)。

    ```typescript theme={"system"}
    const subscriptionRequest: SubscribeRequest = {
      transactions: {
        "wallet-with-tokens": {
          accountInclude: ["YourWalletAddress"],
          accountExclude: [],
          accountRequired: [],
          vote: false,
          failed: false,
          tokenAccounts: "balanceChanged" // also match the wallet's ATAs
        }
      },
      commitment: CommitmentLevel.CONFIRMED,
      accounts: {}, slots: {}, transactionsStatus: {},
      blocks: {}, blocksMeta: {}, entry: {}, accountsDataSlice: [],
    };
    ```

    <Note>
      一致は所有者に基づいています — ウォレットが所有するトークンアカウント（標準でないものを含む）をすべて捕えます。SDKは文字列をワイヤーレベルの`TokenAccountExpansionControlFlag`列挙型に変換します。
    </Note>
  </Tab>
</Tabs>

***

## 実践的な例

### 例 1: DEX トランザクションを監視

人気のあるDEXプログラムに触れるトランザクションを追跡します。

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

async function monitorDEXTransactions() {
  const subscriptionRequest: SubscribeRequest = {
    transactions: {
      "dex-filter": {
        accountInclude: [
          "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8", // Raydium
          "CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK", // Raydium CLMM
          "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"   // Jupiter
        ],
        accountExclude: [],
        accountRequired: [],
        vote: false,
        failed: false
      }
    },
    commitment: CommitmentLevel.CONFIRMED,
    accounts: {}, slots: {}, transactionsStatus: {},
    blocks: {}, blocksMeta: {}, entry: {}, accountsDataSlice: [],
  };

  const config: LaserstreamConfig = {
    apiKey: 'YOUR_API_KEY',
    endpoint: 'https://laserstream-mainnet-ewr.helius-rpc.com', // Choose your closest region
  };

  await subscribe(config, subscriptionRequest, async (data) => {
    if (!data.transaction?.transaction) return;
    const tx = data.transaction.transaction;
    console.log(`\n🔄 DEX Transaction:`);
    console.log(`  Signature: ${bs58.encode(tx.signature)}`);
    console.log(`  Slot: ${data.transaction.slot}`);
    console.log(`  Status: ${tx.meta?.err ? 'Failed' : 'Success'}`);
    console.log(`  Fee: ${tx.meta?.fee || 0} lamports`);
    console.log(`  Compute Units: ${tx.meta?.computeUnitsConsumed || 0}`);

    // Token balance changes
    if (tx.meta?.preTokenBalances?.length > 0) {
      console.log(`  Token Balance Changes:`);
      tx.meta.preTokenBalances.forEach((preBalance: any, index: number) => {
        const postBalance = tx.meta.postTokenBalances[index];
        if (preBalance && postBalance) {
          const change = postBalance.uiTokenAmount.uiAmount - preBalance.uiTokenAmount.uiAmount;
          if (change !== 0) {
            console.log(`    ${preBalance.mint}: ${change > 0 ? '+' : ''}${change}`);
          }
        }
      });
    }
  }, async (error) => {
    console.error('Stream error:', error);
  });
}

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

### 例 2: 失敗したトランザクションを監視

アプリケーションの問題を浮き彫りにするために失敗したトランザクションを追跡します。

```typescript [expandable] theme={"system"}
async function monitorFailedTransactions() {
  const subscriptionRequest: SubscribeRequest = {
    transactions: {
      "failures": {
        accountInclude: ["YourProgramId"],
        accountExclude: [],
        accountRequired: [],
        vote: false,
        failed: true // Only failed transactions
      }
    },
    commitment: CommitmentLevel.CONFIRMED,
    accounts: {}, slots: {}, transactionsStatus: {},
    blocks: {}, blocksMeta: {}, entry: {}, accountsDataSlice: [],
  };

  const config: LaserstreamConfig = {
    apiKey: 'YOUR_API_KEY',
    endpoint: 'https://laserstream-mainnet-ewr.helius-rpc.com',
  };

  await subscribe(config, subscriptionRequest, async (data) => {
    if (!data.transaction?.transaction?.meta?.err) return;
    const tx = data.transaction.transaction;
    console.log(`\n❌ Failed Transaction:`);
    console.log(`  Signature: ${bs58.encode(tx.signature)}`);
    console.log(`  Slot: ${data.transaction.slot}`);
    console.log(`  Error: ${JSON.stringify(tx.meta.err)}`);
    console.log(`  Fee: ${tx.meta.fee} lamports`);
    console.log(`  Compute Units: ${tx.meta.computeUnitsConsumed || 0}`);
  }, async (error) => {
    console.error('Stream error:', error);
  });
}
```

### 例 3: 高額トランザクションを監視

重要なSOL転送を伴うトランザクションを追跡します。

```typescript [expandable] theme={"system"}
async function monitorHighValueTransactions() {
  const subscriptionRequest: SubscribeRequest = {
    transactions: {
      "system-program": {
        accountInclude: ["11111111111111111111111111111111"],
        accountExclude: [],
        accountRequired: [],
        vote: false,
        failed: false
      }
    },
    commitment: CommitmentLevel.CONFIRMED,
    accounts: {}, slots: {}, transactionsStatus: {},
    blocks: {}, blocksMeta: {}, entry: {}, accountsDataSlice: [],
  };

  const config: LaserstreamConfig = {
    apiKey: 'YOUR_API_KEY',
    endpoint: 'https://laserstream-mainnet-ewr.helius-rpc.com',
  };

  await subscribe(config, subscriptionRequest, async (data) => {
    if (!data.transaction?.transaction?.meta) return;
    const tx = data.transaction.transaction;
    const preBalances = tx.meta.preBalances || [];
    const postBalances = tx.meta.postBalances || [];

    let maxChange = 0;
    preBalances.forEach((preBalance: number, index: number) => {
      const postBalance = postBalances[index] || 0;
      maxChange = Math.max(maxChange, Math.abs(postBalance - preBalance));
    });

    const changeInSOL = maxChange / 1e9;
    if (changeInSOL > 10) {
      console.log(`\n💰 High-Value Transaction:`);
      console.log(`  Signature: ${bs58.encode(tx.signature)}`);
      console.log(`  Slot: ${data.transaction.slot}`);
      console.log(`  Max SOL Transfer: ${changeInSOL.toFixed(2)} SOL`);
      console.log(`  Fee: ${tx.meta.fee / 1e9} SOL`);
    }
  }, async (error) => {
    console.error('Stream error:', error);
  });
}
```

### 例 4: ウォレットを監視 (トークン転送を含む)

`tokenAccounts` をプレーンな `accountInclude` フィルタに追加することで、ウォレットのためにマネーを動かすすべてを監視します — その ATA に触れる受信 SPL トークン転送を含めて。

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

async function watchWallet(wallet: string) {
  const subscriptionRequest: SubscribeRequest = {
    transactions: {
      "wallet-activity": {
        accountInclude: [wallet],
        accountExclude: [],
        accountRequired: [],
        vote: false,
        failed: false,
        // Also match txs touching token accounts this wallet owns.
        // "balanceChanged" = only when an owned token balance actually moved.
        tokenAccounts: "balanceChanged"
      }
    },
    commitment: CommitmentLevel.CONFIRMED,
    accounts: {}, slots: {}, transactionsStatus: {},
    blocks: {}, blocksMeta: {}, entry: {}, accountsDataSlice: [],
  };

  const config: LaserstreamConfig = {
    apiKey: 'YOUR_API_KEY',
    endpoint: 'https://laserstream-mainnet-ewr.helius-rpc.com',
  };

  await subscribe(config, subscriptionRequest, async (data) => {
    if (!data.transaction?.transaction) return;
    const tx = data.transaction.transaction;
    console.log(`\n👛 Wallet activity:`);
    console.log(`  Signature: ${bs58.encode(tx.signature)}`);
    console.log(`  Slot: ${data.transaction.slot}`);

    // Surface token balances this wallet owns that changed in the tx
    const owned = (tx.meta?.postTokenBalances || []).filter((b: any) => b.owner === wallet);
    owned.forEach((post: any) => {
      const pre = (tx.meta.preTokenBalances || []).find(
        (b: any) => b.accountIndex === post.accountIndex
      );
      const before = pre?.uiTokenAmount?.uiAmount || 0;
      const after = post.uiTokenAmount?.uiAmount || 0;
      if (after !== before) {
        console.log(`  ${post.mint}: ${after - before > 0 ? '+' : ''}${after - before}`);
      }
    });
  }, async (error) => {
    console.error('Stream error:', error);
  });
}
```

***

## トランザクションデータ構造

<Accordion title="トランザクションメッセージ構造">
  ```typescript theme={"system"}
  {
    signature: string;
    isVote: boolean;
    transaction: {
      message: {
        accountKeys: string[];        // All accounts involved
        instructions: Instruction[];  // Program instructions
        recentBlockhash: string;
      };
      signatures: string[];
    };
    meta: {
      err: any;                      // Error details if failed
      fee: number;                   // Transaction fee in lamports
      computeUnitsConsumed: number;
      preBalances: number[];
      postBalances: number[];
      preTokenBalances: TokenBalance[];
      postTokenBalances: TokenBalance[];
      logMessages: string[];
    };
  }
  ```
</Accordion>

<Accordion title="トークンバランスの変化">
  ```typescript theme={"system"}
  {
    accountIndex: number;
    mint: string;
    owner: string;
    uiTokenAmount: {
      amount: string;
      decimals: number;
      uiAmount: number;
      uiAmountString: string;
    };
  }
  ```
</Accordion>

<Accordion title="命令の詳細">
  ```typescript theme={"system"}
  {
    programIdIndex: number; // Index in accountKeys array
    accounts: number[];
    data: string;           // Instruction data (base58)
  }
  ```
</Accordion>

***

## フィルタロジックのリファレンス

<CardGroup cols={2}>
  <Card title="包含ロジック (OR)" icon="plus">
    **`accountInclude`:** トランザクションはこれらのアカウントのいずれかを含む必要があります。

    `["A", "B"]` はアカウントAまたはアカウントBを含むトランザクションと一致します。
  </Card>

  <Card title="必須ロジック (AND)" icon="check">
    **`accountRequired`:** トランザクションはこれらのアカウントすべてを含む必要があります。

    `["A", "B"]` はアカウントAとアカウントBを含むトランザクションと一致します。
  </Card>

  <Card title="除外ロジック (NOT)" icon="minus">
    **`accountExclude`:** トランザクションはこれらのアカウントのいずれも含まない必要があります。
  </Card>

  <Card title="複合ロジック" icon="code">
    最終フィルタ: `(accountInclude OR empty) AND (accountRequired AND all) AND NOT (accountExclude OR any)`。
  </Card>
</CardGroup>

***

## パフォーマンスの考慮事項

<Tabs>
  <Tab title="ボリューム管理">
    トランザクションストリームは高ボリュームになる可能性があります。以下の方法で対応します。

    * 特定のプログラムフィルタから始める (「すべてのトランザクション」を購読しない)
    * `confirmed` を `processed` よりも使用する\~1.5秒の遅延を許容できる場合
    * カウンターで処理能力を監視する
    * キューの後ろで並行してコンシューマを実行することを検討する

    ```typescript theme={"system"}
    let count = 0;
    const startTime = Date.now();
    // inside your subscribe handler:
    count++;
    if (count % 100 === 0) {
      const elapsed = (Date.now() - startTime) / 1000;
      console.log(`Processing ${(count / elapsed).toFixed(1)} tx/sec`);
    }
    ```
  </Tab>

  <Tab title="データ処理">
    メモリを低く抑えるために必要なものだけを抽出します。

    ```typescript theme={"system"}
    import bs58 from 'bs58';

    function extractTransactionData(tx: any) {
      return {
        signature: bs58.encode(tx.signature),
        slot: tx.slot,
        success: !tx.meta?.err,
        fee: tx.meta?.fee || 0,
        computeUnits: tx.meta?.computeUnitsConsumed || 0,
      };
    }
    ```
  </Tab>
</Tabs>

***

## エラーハンドリング

<Accordion title="トランザクションが多すぎる">
  **症状:** 圧倒的なトランザクションボリューム。

  **解決策:** 厳しいフィルタを追加 (`accountRequired`, `accountExclude`); より高いコミットメントを使用する; サンプリングやレート制限を実装する; 非同期で処理する。
</Accordion>

<Accordion title="トランザクションが見つからない">
  **症状:** 期待されたトランザクションが表示されない。

  **解決策:** プログラムアドレスが正しいか確認する; トランザクションが実際に存在するか確認する; より速い更新のために`processed`を試す; 制限の厳しい`accountRequired`/`accountExclude`フィルタを緩める。
</Accordion>

<Accordion title="解析エラー">
  **症状:** トランザクションデータを解析できない。

  **解決策:** 欠落したフィールドを適切に処理する; 処理前に構造を検証する; 解析をtry/catchでラップする; [トランザクションデータのデコード](/ja/laserstream/guides/decoding-transaction-data)を参照する。
</Accordion>

***
