jev-trader: a Monad market maker that decides once per block
jev-trader is a Monad market maker: once per block it asks Jev buy or sell and posts a post-only limit order one tick inside the touch. No key means dry-run.
Project facts
GitHub Ecosystem- License
- MIT
- Language
- TypeScript
- Stars
- 2,954
- Data checked
- 2026-10-12
Snapshot figures reflect the check date and may change over time.
Most on-chain trading bots are built around speed: watch the price, fire a market order, pay the spread and the slippage. jev-trader, open-sourced in mid-September 2026 (MIT, TypeScript, 2.9k stars as of 2026-10-12), takes the other side of the book: a market maker on Monad that watches Kuru’s MON-USDC order book, asks Jev buy-or-sell once per block (about 300 ms), and posts a post-only limit order one tick inside the touch, waiting for a taker to pay the spread.
Core features
- Pure maker strategy: only post-only limit orders, filled when a taker hits them; each new order cancels all outstanding ones in the same batchUpdate, so the book always holds exactly one.
- The decision contract: the model sees the order book and answers buy or sell over a default horizon of 100 blocks (about 30 s); the execution layer turns that into one post-only limit order a tick inside the touch, never a market order.
- A dissected 300 ms budget: the hot loop makes exactly two RPC round trips — one eth_call to read the book, one eth_sendRawTransaction that returns on acceptance. The README records p50 18 ms for book reads and p50 100 ms for the whole loop (dry run with the mock model).
- What it deliberately skips: no eth_estimateGas (Monad charges the gas limit, so it is hardcoded or derived once at startup), no synchronous sends, no gas price queries (static type-2 fees); receipts, fee estimates, and vault checks all move off the hot path to later blocks.
- No private key means dry-run: real order book, real decisions, simulated fills; Jev only decides when you set MODEL=jev and TYPESAFE_AI_API_KEY — the default mock is a momentum heuristic stand-in.
- Observable event stream: SSE pushes decision, quote, and fill events per block with a ping every 15 s; /history keeps the last 1,000 block events; a receipt that misses 10 blocks is marked lost.
- Two benchmark scripts ship with it: bench-read.ts compares the custom book reader against the official SDK for exactness and latency; dry-encode.ts signs a buy and a sell offline and asserts the calldata matches the SDK.
Typical use cases
- Quant developers validating a market-making strategy with zero funds: run the local dry-run and watch per-block decisions, quotes, and simulated fills in the SSE stream.
- Monad ecosystem users who want to make markets automatically: set PRIVATE_KEY, MODEL=jev, and the API key, and the bot keeps quoting MON-USDC.
- Latency researchers: use bench-read.ts to measure how much faster your reader is than the official SDK, and whether it loses exactness.
Quick start
cp .env.example .env
bun install
bun run start
Starting without PRIVATE_KEY is the dry-run. The README also links a Railway-hosted dry-run instance (https://jev-trader-production.up.railway.app) where you can watch the event stream directly. HORIZON_BLOCKS defaults to 100 (about 30 s), the horizon the model predicts over.
Summary
jev-trader is a useful reference implementation for anyone researching on-chain market making and low-latency decision loops, and a poor candidate for “set a key and collect yield”. Know the boundaries: one chain, one market — Kuru’s MON-USDC on Monad; Monad charges the gas limit whether or not the order lands; live sends are fired-and-forgotten, with receipts arriving one or two blocks later and a miss after ten blocks recorded as lost. Most importantly, the README publishes no track record or backtest — the small P&L in its example event stream is demo data — and no risk disclaimer. Treat it as an experiment, not a strategy. MIT-licensed, TypeScript, run with bun. This is the project closest to real money in the ten Jev use cases roundup.