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

# Trade on Preprocessed Transactions

> Watch a program's Solana transactions before they reach the processed commitment level with preprocessedSubscribe.

`preprocessedSubscribe` streams signed transactions before they reach the `processed` commitment level, decoded from shreds as they arrive at the validator, with no deshredding infrastructure required on your side.

This guide builds a monitor that watches a trading program pre-execution, deduplicates the feed, stays under the server's backpressure limit, and reconciles against processed data before acting.

<Tip>
  [Preprocessed Transactions](/docs/preprocessed-transactions/overview) are available on all paid plans and cost 0.1 credits per message, with up to 10 concurrent connections per API key.
</Tip>

## Scope the subscription to your target

There is no unfiltered stream: `accountInclude` and `accountRequired` must name at least one account between them. For a trading monitor, include the program you trade against and exclude noise you'd otherwise pay for:

```json theme={"system"}
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "preprocessedSubscribe",
  "params": {
    "accountInclude": ["JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"],
    "accountExclude": ["Vote111111111111111111111111111111111111111"],
    "accountRequired": []
  }
}
```

Filters combine with AND, each list accepts up to 5,000 addresses, and Helius resolves address lookup tables server-side, so an account loaded through an ALT still matches. To watch a specific wallet's interactions with the program, put both in the `accountRequired` field instead.

## Decode frames and deduplicate by signature

Notifications arrive as binary frames: a 73-byte prefix, then the bincode-serialized transaction. The signature is in the prefix so you can deduplicate without decoding the body. The stream aggregates several pre-execution sources, and the same transaction can arrive more than once:

```javascript monitor.js theme={"system"}
const WebSocket = require('ws');
const bs58module = require('bs58');
const bs58 = bs58module.default ?? bs58module;

const ws = new WebSocket('wss://beta.helius-rpc.com/?api-key=YOUR_API_KEY');
const seen = new Set(); // rotate or expire entries in production
const queue = [];       // decode off the receive path; see next step

ws.on('open', () => {
  ws.send(JSON.stringify({
    jsonrpc: '2.0',
    id: 1,
    method: 'preprocessedSubscribe',
    params: {
      accountInclude: ['JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4'],
      accountExclude: ['Vote111111111111111111111111111111111111111'],
      accountRequired: []
    }
  }));
  setInterval(() => ws.ping(), 30_000);
});

ws.on('message', (data, isBinary) => {
  if (!isBinary) {
    const msg = JSON.parse(data.toString());
    if (msg.id === 1) console.log('Subscribed, ID:', msg.result);
    return;
  }

  // version (u8) | slot (u64 LE) | signature ([u8; 64]) | bincode(VersionedTransaction)
  const buf = Buffer.from(data);
  if (buf.readUInt8(0) !== 1) return; // unknown schema version; update your decoder
  const signature = bs58.encode(buf.subarray(9, 73));
  if (seen.has(signature)) return;
  seen.add(signature);

  queue.push({
    slot: buf.readBigUInt64LE(1),
    signature,
    txBytes: buf.subarray(73)
  });
});

ws.on('error', console.error);
ws.on('close', () => process.exit(1)); // supervisor restarts and resubscribes
```

## Keep the receive loop faster than the stream

Helius does not buffer indefinitely for slow consumers: if more than 4,000 messages back up server-side, the connection is closed. That is why the handler above only reads the 73-byte prefix and enqueues.

Deserialization and strategy logic run in a separate loop:

```javascript theme={"system"}
setImmediate(async function drain() {
  const { VersionedTransaction } = require('@solana/web3.js');
  while (true) {
    const item = queue.shift();
    if (!item) { await new Promise(r => setTimeout(r, 1)); continue; }
    const tx = VersionedTransaction.deserialize(item.txBytes);
    strategy.onPreExecution(item.slot, item.signature, tx);
  }
});
```

If the queue grows without bound, tighten the filters. Transactions you discard client-side still cost 0.1 credits each and still count toward the backpressure limit.

## Reconcile against processed data

A preprocessed transaction shows what the sender tried to do, not what happened. It carries no execution status, balance changes, or logs, and it can fail, be dropped, or land on a different fork. Delivery is best-effort with no historical replay, so:

* Reconcile against [`transactionSubscribe`](/docs/rpc/websocket/transaction-subscribe) at `processed` or `confirmed` commitment before your strategy books anything as fact.
* On disconnect, resubscribe immediately and accept the gap, since there is no replay to backfill it.
* If you need real-time account state rather than transaction intents, that only exists post-execution: use [LaserStream gRPC](/docs/laserstream) at `processed`.

## Related guides

<CardGroup cols={2}>
  <Card title="preprocessedSubscribe reference" icon="code" href="/docs/preprocessed-transactions/preprocessed-subscribe">
    Full payload layout, filter rules, backpressure, and pricing
  </Card>

  <Card title="Trade on Preconfirmations" icon="stopwatch" href="/docs/pre-confirmations/guides/trade-on-preconfirmations">
    The earliest transaction signal Helius offers, from the scheduler itself
  </Card>
</CardGroup>
