The keeper is an off-chain Rust async bot that owns three operational duties on Continuum:Documentation Index
Fetch the complete documentation index at: https://continuum-ec12e897.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
- Oracle updates - publishes Pyth/Hermes observations to the oracle program every ~15 s.
- Peg-enforcing arbitrage - closes price divergence between Meteora DLMM pools and protocol NAV.
- DLMM position management - opens, rebalances, and closes the protocol’s LP positions on Meteora.
Privileges
The keeper signs as the privilegedkeeper_authority stored on each market account. This grants it access to:
keeper_mint_single(is_long, collateral)- fee-free, single-sided mint at NAV.keeper_redeem_single(is_long, token_amount)- fee-free, single-sided redeem at NAV.update_risk_state(new_state)- mirror oracle risk state into the market.- All
clp_*proxy instructions - Meteora position management on the CLP PDA’s behalf.
Architecture
Arc<RwLock<KeeperState>>. Failure of one loop doesn’t crash the others.
What each loop does
Oracle (15s)
Fetches Pyth Hermes for each active market, picks the session-aware feed (regular / pre-market / post-market / overnight), and callsoracle::update_price_observation on-chain.
Risk monitor (15s)
Reads each market’sOracleConfig, derives the right risk state (Normal / ProxyMode / Stress / Recovery), and calls mint_redeem::update_risk_state if it differs from the current on-chain value.
Inventory (60s)
Computes per-market q-imbalance (long supply vs short supply weighted by NAV) and drawdown vs peak. Tracks soft and hard bounds. Drives downstream gating in arb (won’t push past hard bounds even if profitable).DLMM rebalancer (120s)
For each market × side, decides whether to remove + close + reopen the Meteora position. Signals to rebalance:- Active bin has overflowed the position’s range (bin price moved past the edge).
- NAV target has moved enough that the existing range no longer covers
[active, target]. - Volatility regime has shifted (calm ↔ stressed) - wider/narrower bin radius.
[min(active, target), max(active, target)] with a small slack. Saves ~0.05 SOL of rent + several CU-heavy txs per skipped cycle.
→ Full DLMM details
Arb scanner (15s)
Two paths, evaluated each tick per market × side: Single-side (primary): pool diverges from per-side NAV by more thanpool_fee_bps + slip/2.
redeem_paired if both sides have stock, or keeper_redeem_single for one-sided residue.
→ Full arb path details
Liquidity seeder (600s)
Allocates capital fromGlobalClp (the global cUSDC vault) into per-market vaults. Edge-weighted: each market gets a weight blending its OI-cap share with its observed (fee_rate + arb_rate) / (risk_q_deviation + drawdown) edge. Recalls from worst-edge markets when global vault free balance drops below a threshold.
Peer heartbeat (30s)
Multi-keeper primary/standby coordination. Currently basic - one canonical keeper at a time. Future versions will support active/active across multiple operators.Solvency guarantees
Every operational invariant in the keeper is designed so that a crash mid-arb leaves the protocol in a recoverable state.- Boot-time sweep. First action after startup: read keeper wallet’s L and S balances. Both sides →
redeem_paired. One side →keeper_redeem_single. Reclaims any leftover synth from prior crashes. - End-of-cycle sweep. After every arb (single-side or paired), measure post-swap L/S balances and redeem residue. Built at execution time so the slippage-buffer micro-leak is recovered, not accumulated.
- Profit deposit gating.
deposit_profitto the global vault sendsmin(actual_delta, expected_profit). A favorable-slippage run doesn’t over-deposit; an unprofitable arb deposits nothing. - Dynamic trade sizing. Per scan,
trade_amount = min(pool_depth, 90% × keeper_balance)so the keeper never bites off more than its wallet can chew.
Configuration
The reference keeper is configured via.env. Key knobs:
| Variable | Default | Meaning |
|---|---|---|
MAINNET_RPC_URL / DEVNET_RPC_URL | official cluster | Primary RPC |
KEEPER_KEYPAIR | ~/.config/solana/id.json | Payer + arb signer |
CLP_AUTHORITY_KEYPAIR | keeper/key/keeper-keypair.json | CLP proxy signer |
ARB_INTERVAL_SECS | 15 | Arb scan cadence |
DLMM_INTERVAL_SECS | 120 | DLMM rebalance cadence |
MIN_ARB_PROFIT | 100000 (= $0.10) | Skip arbs below this |
MAX_SLIPPAGE_BPS | 50 | Profit buffer |
LIQUIDITY_SEED_FRACTION_BPS | 6500 | Fraction of global vault per seed cycle |
ACTIVE_MARKETS | QQQ | Comma-separated symbols |
Can I run my own keeper?
Yes - and there’s a public reference implementation in the protocol repo. Limitations to know:- Only one canonical keeper authority per market. The market’s
keeper_authorityfield is single-pubkey today. Two keepers signing as the same pubkey will collide. Multi-keeper requires either coordination (peer heartbeat with primary/standby logic) or future on-chain support for keeper sets. - You don’t need keeper authority to arb. Single-sided keeper-only paths are fee-free; paired arb is open to anyone. An external arb bot can run paired-arb without any privilege at all - just ordinary
mint_paired+redeem_paired+ Meteora swaps. - The CLP authority is separate from the payer. The keeper signs CLP instructions with
CLP_AUTHORITY_KEYPAIR, distinct from the payer keypair. This lets operators rotate signers without re-creating positions.
Dashboard
The reference keeper exposes a local HTTP dashboard athttp://localhost:8484 (mainnet) / 8485 (devnet). It shows:
- Oracle state and last-update timestamps per market.
- Risk state per market.
- Outstanding arb opportunities (size, direction, expected profit).
- Position status per Meteora pool (active bin, range, allocated capital).
- Vault balances (global, per-market).
Read more
Arb paths in detail
The four arb cycles, sizing logic, profit accounting.
DLMM position management
Vol-adaptive radius, fee-weighted centering, skip-rebuild optimization.
Run a keeper
Config, keypairs, dashboard, troubleshooting.
Solvency invariants
What the protocol enforces no matter what the keeper does.

