Open-source project
jarrodwatts/jev-trader avatar
jarrodwatts/jev-trader

jev-trader lets an AI model quote both sides of a Monad order book every block

One AI trade decision every Monad block. Jev on Kuru MON-USDC.

1,178 stars228 forksTypeScriptMIT

At a glance

What is it?
A Bun and TypeScript bot that asks a Jev model buy or sell once per Monad block and posts post-only limit orders on the Kuru MON-USDC book. Dry run by default; live trading is one private key away.
Who is it for?
jev-trader suits builders experimenting with AI-driven market making on Monad who will keep it in dry-run mode until the event stream convinces them, fund a margin account they can afford to lose, and read the README's failure-state prose carefully. Skip it if you need a proven strategy, multi-pair support, or anything resembling investment advice; the repository offers a spread-capture experiment on one pair and says so.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 19, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

A market maker with a model in the loop

jev-trader runs one loop, and the README states it without embellishment: once every Monad block, roughly every 300 milliseconds, a TypeSafe Jev model looks at the Kuru MON-USDC order book and answers buy or sell. The bot then posts a real post-only limit order on that side, one tick inside the touch, replacing whatever it had resting before. Because the order rests instead of taking, fills arrive when someone else crosses it, and the bot earns the spread rather than paying it.

This is a narrow, honest design. There is no strategy zoo and no dashboard-driven configuration; there is one pair, one decision per block, one order on the book. The interesting experiment is not whether the bot prints money but whether a language-model judgment can arrive, decide and quote inside a single block, reliably.

The 300 millisecond budget

The engineering constraint gives the repository its shape: a decision and an order must fit inside one block, so the hot path makes exactly two RPC round trips. One eth_call reads the book, about 18 milliseconds at the median on the public RPC. One eth_sendRawTransaction submits the order and returns as soon as the transaction is accepted.

Everything else was pushed off the path, and the README enumerates what that means: no eth_estimateGas, because Monad charges gas on the limit, so the limit is hardcoded or derived once at startup; no synchronous send variant that blocks until proposal; static type-2 fees with a MAX_FEE_GWEI cap and 2 gwei of priority. Receipts, fee estimates and the vault check run on later blocks. Two benchmark scripts, bench-read.ts and dry-encode.ts, let you verify the book reader's latency and confirm the hand-encoded order calldata matches the SDK offline.

Running it: dry run first, by design

The README's run block is three commands:

bash
cp .env.example .env
bun install
bun run start

Without a PRIVATE_KEY set, the bot dry-runs: a real order book, real model decisions, simulated fills where an order rests for one block and fills if a real print crosses its price. The default MODEL is mock, a momentum heuristic stand-in; setting MODEL=jev with a TYPESAFE_AI_API_KEY points the loop at the actual Jev model. Only when you add a private key does anything real leave your machine.

That progression is the correct default for software that can move money, and the environment file carries the knobs the risk story depends on: TRADE_SIZE_MON with a 200 MON minimum, QUOTE_INSIDE_TICKS, MAX_POSITION_MON and BANKROLL_USD.

What the event stream tells you

A small server exposes the bot's inner life. The snapshot endpoint reports model, wallet, dry-run flag and the latest block; /history returns the last thousand block events; /events is an SSE stream with one event per block plus fill events. A block event carries the mid price, best bid and ask, the spread in basis points, the model's decision with full probabilities and latency, the quote it posted, current resting size, position and running totals down to gas in MON and PnL in USD.

The event vocabulary encodes the failure modes honestly. A hold appears only when the model missed the block, flagged as late. A capped flag means the position cap blocked one side and the quote went to the other while the probabilities still show the model's real call. In dry runs the quote status reads sim; live quotes read sent, then placed with an order id, reverted, or lost if no receipt lands within ten blocks.

The honest accounting of live trading

Live sends are fired and forgotten, and the README prices that decision precisely. The gas figure on a block event is an estimate, gas limit times the last known base fee plus priority, and Monad charges the limit whether or not the order lands. A reverted order still cost gas. Fills are never in the bot's own transactions: a taker hits the resting order, and the trade log arrives through the same eth_getLogs polling that feeds the model.

Position accounting updates only when those fills land, so the displayed PnL lags the book by design. Kuru limit orders draw from a margin account rather than the wallet, so the bot deposits at startup, which is one more place where real funds sit under automatic control. None of this is hidden; the README's plain prose about failure states is the best documentation in the repository.

Where the risk concentrates

The risky line in the whole repository is one environment variable. PRIVATE_KEY set and DRY_RUN unset turns the same loop that simulated fills into a live quoter, posting real orders every block, with an AI model choosing the side. The README's guidance is to use a dedicated RPC provider for live trading rather than the public endpoint, which caps at 25 requests per second and sits close to that with one read and one log poll per block.

Model risk has a shape too: the horizon is 100 blocks, about 30 seconds, and a late model means a missed block, which the bot converts into a hold rather than a stale order. And the strategy itself is spread capture on one pair, which lives or dies on volume and volatility that no amount of code in this repository controls. The bot reports its own scoreboard in the totals, including jevUsd and pnlUsd, and the honest reading of any short run is that gas and spread compete for the same thin margin.

Set against the alternatives

The obvious comparison is a conventional open-source market-making bot wired to a rules engine: deterministic signals, decades of practice, no model latency in the loop. Against that, jev-trader trades predictability for a language model's read of the tape, and commits to making that comparison measurable, every decision, probability and latency lands in the event stream. Manual trading on the same pair is the other baseline, and the bot's post-only, one-tick-inside discipline is frankly more consistent than a human quoting by hand.

What the repository does not claim matters as well. It does not claim profitability, and its own sample totals show PnL fractions of a cent against real gas spend. Its claim is narrower and more interesting: the loop closes inside a block, with a model in it, and every step is observable.

Editorial conclusion

jev-trader suits builders experimenting with AI-driven market making on Monad who will keep it in dry-run mode until the event stream convinces them, fund a margin account they can afford to lose, and read the README's failure-state prose carefully. Skip it if you need a proven strategy, multi-pair support, or anything resembling investment advice; the repository offers a spread-capture experiment on one pair and says so. Verify first: run it with the mock model and no private key for a few hours, then compare the decisions against your own read of the same blocks before trusting the Jev path with real funds.

Frequently asked questions

Does jev-trader trade real money by default?

No. Without a PRIVATE_KEY, or with DRY_RUN=true, it dry-runs: real order book, real decisions, simulated fills. Live trading starts only when you set a private key.

Which model does jev-trader use for decisions?

The default MODEL=mock is a momentum heuristic stand-in. Setting MODEL=jev with a TYPESAFE_AI_API_KEY uses the TypeSafe Jev model, with JEV_MODEL_ID defaulting to jev-latest.

What market does jev-trader quote on?

The Kuru MON-USDC order book on Monad. Each block it posts a post-only limit order of TRADE_SIZE_MON, minimum 200 MON, QUOTE_INSIDE_TICKS inside the touch, and cancels its previous order in the same batch.

Official sources

  1. Issues
  2. jarrodwatts/jev-trader on GitHub
  3. License: MIT
  4. README
Community notes

Community notes