# How Percolator Coordinates Pluggable Matchers, Keeper Cranking, and Real-Time Slab Polling

> Learn how Percolator coordinates pluggable matchers, keeper cranking, and real-time slab polling using a shared PercolatorConfig for a fresh on-chain order book and hot-swappable matching engines.

- Repository: [AL/CloddsBot](https://github.com/alsk1992/CloddsBot)
- Tags: internals
- Published: 2026-09-13

---

**Percolator synchronizes pluggable matchers, keeper cranking, and real-time slab polling through a shared `PercolatorConfig` that binds the keeper’s periodic crank transactions to the feed’s polling logic, ensuring the on-chain order book stays fresh while allowing hot-swappable matching engines.**

The Percolator module in [CloddsBot](https://github.com/alsk1992/CloddsBot) provides a Solana-native perpetual futures infrastructure that separates market data ingestion from order execution. By decoupling the **keeper** (state advancement), **feed** (real-time polling), and **matcher** (pluggable program IDs), the system achieves low-latency coordination without tight coupling between components.

## Architecture Overview

The Percolator subsystem consists of three loosely-coupled services defined in [`src/percolator/index.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/percolator/index.ts). The `createPercolatorService` factory instantiates:

- A **keeper** that periodically sends crank transactions to advance the on-chain slab
- A **feed** that polls the slab account via RPC and emits parsed market state
- An **execution** service that routes trades through configurable matcher programs

All three components reference the same `PercolatorConfig` interface declared in [`src/percolator/types.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/percolator/types.ts), ensuring they operate on identical slab addresses, oracle feeds, and optional matcher program IDs.

## The Three Core Components

### Pluggable Matchers

**Pluggable matchers** allow operators to swap the on-chain matching logic without restarting the bot. The `PercolatorConfig` accepts optional `matcherProgram` and `matcherContext` fields that identify the Solana program ID and context account for the active matcher.

When `createPercolatorExecution` builds a transaction in [`src/percolator/execution.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/percolator/execution.ts), it includes these accounts as writable references in the CPI instruction. If the matcher fields are omitted, the system falls back to the default matching engine.

The feed service also inspects these fields to detect **passive matcher LP** status. When `matcherProgram` is defined, the feed treats the matcher as an active liquidity provider, influencing how it calculates available liquidity from the slab parser in [`src/percolator/slab.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/percolator/slab.ts).

### Keeper Cranking

The **keeper** is an optional background task created by `createPercolatorKeeper` in [`src/percolator/keeper.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/percolator/keeper.ts). When `keeperEnabled` is set to `true` in the configuration, the keeper starts a timer that fires at `intervalMs` frequency (default 5000ms).

Each tick executes a crank transaction defined in [`src/percolator/tx.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/percolator/tx.ts). The crank:

1. Reads the current oracle price from `oracleAddress`
2. Invokes the matcher program (if configured) to settle pending LP orders
3. Advances the slab state stored at `slabAddress`

If the slab or oracle address is missing, the keeper logs a warning and skips the iteration, preventing failed transactions.

### Real-Time Slab Polling

The **feed** service, instantiated via `createPercolatorFeed` in [`src/percolator/feed.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/percolator/feed.ts), maintains a continuous WebSocket or HTTP polling loop against the `slabAddress`. Each poll fetches the raw account buffer and passes it to the binary parser in [`src/percolator/slab.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/percolator/slab.ts).

The parser extracts `bestBid`, `bestAsk`, `lastPrice`, `openInterest`, and user-specific positions via `getPositions`. When the parsed `PercolatorMarketState` differs from the previous iteration, the feed emits a `'state'` event. Because the feed and keeper target the same slab account, the keeper’s cranking guarantees the feed never propagates stale order-book data.

## Coordination Flow

The synchronization between components follows four distinct phases:

1. **Configuration Loading** – The bot loads `PercolatorConfig` from [`src/utils/config.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/utils/config.ts), validating that `slabAddress` is present and optionally populating `matcherProgram`, `matcherContext`, and `keeperEnabled`.

2. **Keeper Initialization** – If enabled, the keeper begins its interval loop in [`src/percolator/keeper.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/percolator/keeper.ts), committing crank transactions that update the on-chain slab. Each successful crank advances the market state and refreshes the oracle price.

3. **Feed Polling** – Concurrently, the feed polls the identical `slabAddress`. Since Solana confirms transactions within 400-600ms, the feed typically observes the keeper’s updates within one or two polling cycles.

4. **Trade Execution** – When a skill invokes `execution.marketBuy()`, the execution service constructs a transaction referencing the current `matcherProgram` and `matcherContext` from the config. The transaction targets the same slab the feed is polling, ensuring the trade executes against the latest liquidity.

## Implementation Examples

### Initializing the Percolator Service

```typescript
import { createPercolatorService } from './percolator/index.js';
import type { PercolatorConfig } from './percolator/types.js';

const percolatorConfig: PercolatorConfig = {
  slabAddress: process.env.PERCOLATOR_SLAB,
  oracleAddress: process.env.PERCOLATOR_ORACLE,
  matcherProgram: process.env.PERCOLATOR_MATCHER_PROGRAM,
  matcherContext: process.env.PERCOLATOR_MATCHER_CONTEXT,
  keeperEnabled: true,
  keeperIntervalMs: 5_000,
};

const { feed, execution, keeper } = createPercolatorService(percolatorConfig);

```

### Listening for Market State Updates

```typescript
feed.on('state', (state) => {
  console.log('🧭 New Percolator market state:', state);
});

```

### Manual Keeper Cranking

```typescript
if (keeper) {
  const { crank } = await import('./percolator/tx.js');
  await crank(percolatorConfig);
}

```

### Executing Trades Through Pluggable Matchers

```typescript
await execution.marketBuy({ size: 1_000_000n });

```

## Summary

- **Pluggable matchers** are configured via `matcherProgram` and `matcherContext` in `PercolatorConfig`, allowing hot-swapping of matching logic without service restarts.
- **Keeper cranking** runs as an optional background interval task in [`src/percolator/keeper.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/percolator/keeper.ts), periodically advancing the on-chain slab state and refreshing oracle prices.
- **Real-time slab polling** occurs in [`src/percolator/feed.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/percolator/feed.ts), parsing binary slab data from the same address the keeper updates, ensuring fresh market data.
- All three components coordinate through the shared `PercolatorConfig` interface, with the keeper acting as the state producer and the feed acting as the state consumer.

## Frequently Asked Questions

### How does the keeper prevent failed transactions when the slab is unavailable?

The `createPercolatorKeeper` function in [`src/percolator/keeper.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/percolator/keeper.ts) validates that both `slabAddress` and `oracleAddress` exist in the config before executing the crank. If either address is undefined, the keeper logs a warning and skips the iteration, avoiding RPC errors or failed on-chain transactions.

### Can I run the feed without the keeper enabled?

Yes. The feed operates independently in [`src/percolator/feed.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/percolator/feed.ts) and polls the `slabAddress` regardless of the keeper’s status. However, without the keeper running, the slab state only updates when external transactions modify the order book, potentially resulting in stale data during low-activity periods.

### What happens if I change the matcher program ID while the bot is running?

The `execution` service reads `matcherProgram` and `matcherContext` from the config at transaction build time. If you update these environment variables and restart the bot, subsequent trades will route through the new matcher. The keeper also references these fields when cranking, ensuring the new matcher settles liquidity correctly after the restart.

### How frequently does the slab polling update market state?

The feed polls the slab account continuously via WebSocket or HTTP, while the keeper advances the state at `keeperIntervalMs` intervals (default 5000ms). In practice, the feed observes new state within milliseconds of the keeper’s transaction confirmation, as Solana slots finalize in approximately 400-600ms.