jev-trader は、Monad の約300ミリ秒ごとのブロックに合わせて 1ブロック1回だけ AI に「買いか売りか」を聞く、MIT ライセンスの TypeScript 製ボットだ。⭐1.2k・fork 230。聞く相手は Jev——文章を1文字も返さず、型つきの判定と確率だけを返す TypeSafe AI の System One モデルである。
先に書いておく。これは儲け方の記事ではない。作者自身が SPEC.md に「実験的なデモ、ごく小さな元手、投資助言ではない、モデルは利益を出そうとしていない」と明記している。この記事が扱うのは、判定モデルを300msの締め切りに載せると設計がどう変わるかだ。板取引という題材は当サイトの2本柱から外れているが、依頼を受けて実測した(記事末の Distribution memo に理由を残した)。
- ・正体:Monad(chainId 143)の板取引所 Kuru の MON-USDC 板を読み、Jev に方向を1問だけ聞いて
post-onlyの指値を出し続ける Bun 製のボット。MIT・⭐1.2k・リリースタグは0本。 - ・モデルへの質問は1問だけ:
choice型、選択肢はbuyとsellの2つのみ。holdは選択肢に無く、間に合わなかったブロックにだけ現れる。maxRetries: 0。 - ・実測できたこと:モデルに渡る
TradeStateを同形に組んで 1,123文字・推定281トークン。MockModelの80msはBun.sleep(80)のハードコードで、判定計算そのものは1,000回で0.36msだった。 - ・見つけた食い違い:モデルへの説明文だけが旧仕様(成行でスプレッドを払う)のまま。実装は post-only の指値(スプレッドを受け取る)に変わっている。
- ・コスト:README のサンプル値と既定 config から計算すると、1時間でガス約 $9.61/Jev 約 $0.14。ガスがモデルの約68倍。
- ・鍵もAPIキーも不要:
PRIVATE_KEYを空にすればドライラン。既定モデルはmock。
判定モデルという種族そのものが何であるかは、LLMとは?仕組み・主要モデル比較・ローカル実行・量子化を一気にまとめる2026年版 が扱う「文章を生成するモデル」の対極にある。生成側の前提を一度押さえてから読むと、なぜ300msに載るのかが腑に落ちる。
jev-traderとは——300msごとに1回、Jevが「買いか売りか」だけ答える
README の1行目がこの本体を言い切っている。「Monad のブロックごとに1つの決定。TypeSafe の Jev モデルが Kuru の MON-USDC 板を見て、およそ300msごとに買いか売りかを答える」。
毎ブロック、答えた側に post-only の指値 が1枚置かれる。post-only は「板を絶対に越えない」注文で、タッチ(最良気配)の QUOTE_INSIDE_TICKS(既定1)ぶん内側に置かれ、前のブロックの注文はキャンセルされる。約定は自分からは起こらない。誰かの成行注文が自分の指値に当たってきたときだけ成立する。だから手数料の向きが逆になる——スプレッドを払うのではなく、受け取る側に回る。
読者の3問に答えるとこうなる。何ができるか:ブロックチェーンの1ブロックという締め切りの中で、AI の判断を実際の注文に変換し続ける。何を解決するか:生成モデルでは間に合わない時間帯(数百ms)に、判断だけを差し込む方法を実演する。何を代替するか:ルールベースのマーケットメイク・ボットの「方向を決める部分」だけを代替する。板の読み取り・注文の組み立て・建玉管理はすべて従来どおりのコードだ。
モデルに聞くのは1問、選択肢は2つ
src/model.ts の QUESTIONS は定数1つしかない。
| 項目 | 中身 |
|---|---|
| 質問の型 | choice(Jev の3プリミティブのうちの1つ) |
| 質問文 | 「horizonBlocks ブロック後、MON は現在の mid より高いか低いか」 |
| 選択肢 | buy と sell の2つだけ(hold は無い) |
| 補足 | goal・timing・inputs の3項目で、市場・執行方法・どの入力が効くかを説明 |
| 呼び出し | AI SDK の experimental_evaluate、maxRetries: 0 |
hold が選択肢に無いのは意図的だ。型定義のコメントにこう書いてある。「モデルは buy か sell を答える。hold は遅延ブロック(判断が行われなかった)にだけ現れる」。300ms の中で「様子を見る」という答えを許すと、板の上に何も置かない時間が生まれる。毎ブロック必ずどちらかに寄せる、という割り切りである。
inputs の説明文も面白い。「テイカーのフローが最も強いシグナル:trades.cvdMon(ホライズン内のテイカー買い引くテイカー売り)、trades.lastSide、recentTrades が誰が板を叩いているかを示す」。作者が自分の相場観を、モデルへの補足として文章で渡している。判定モデルは文章を返さないが、受け取る側では文章がまだ効いている。
maxRetries: 0 が意味すること
experimental_evaluate の引数に maxRetries: 0 が明示されている。失敗したら諦める。300ms の予算では、1回リトライした時点で次のブロックに間に合わない。trader.ts 側も「同時に1リクエストだけ」を守り、前のブロックの処理が終わっていなければ新しいブロックを late として捨てる。
・間に合わなかったブロック:decision.late: true、action: "hold"、注文は出ない
・同時実行:常に1本(busy フラグ1つで管理)
・遅延の集計:totals.lateBlocks として表に出る
つまりこのシステムは、モデルが遅れたことを隠さない。遅延はエラーではなくカウンタになる。
jev-traderを動かす——ドライランなら秘密鍵もAPIキーも要らない
手を動かす。ランタイムは Bun(リポジトリの CLAUDE.md が「Node.js ではなく Bun を使え」と指示している)。
cp .env.example .env
bun install
bun run start
検証環境では bun install が 160パッケージを3.60秒で入れた。直接依存は4本しかない——ai(AI SDK 7.0.103)、@ai-sdk/typesafe-ai(3.0.0)、@kuru-labs/kuru-sdk(0.0.95)、ethers(5.8.0)。
.env.example をコピーしただけの状態は ドライランになる。PRIVATE_KEY が空なら config.dryRun が自動で true になり、板と判定は本物のまま、約定だけがシミュレートされる。モデルの既定も mock(モメンタム+板の偏り+平均回帰のヒューリスティック)なので、TypeSafe の API キーも要らない。実際の Jev を使うのは MODEL=jev と TYPESAFE_AI_API_KEY を設定したときだけだ。
ただし、ここから先はこの検証環境では動かせなかった。rpc.monad.xyz も公開デモの Railway も、ネットワーク制限で到達できない(CONNECT tunnel failed, response 403)。板を読めない以上、ループは回らない。そこで モデル層だけを切り出して動かす ことにした。src/model.ts は RPC に一切触らないので、状態を手で組めばそのまま呼べる。
Jevに何を見せているか——TradeStateをソースから組んで実測する
TradeState の定義(src/model.ts)は14キー。README のサンプルイベント(block 105488269)の数値に合わせて同じ形の状態を組み、長さを測った。
import { MockModel } from "./src/model";
const state = {
market: "MON-USDC", block: 105488269, horizonBlocks: 100, blockMs: 300,
mid: 0.022636, spreadBps: 7.07, bookImbalance: 0.12,
depth: { "10bps": { bid: 4200, ask: 3800 }, "25bps": { bid: 9100, ask: 8700 }, "50bps": { bid: 15400, ask: 16200 } },
book: {
bids: ["0.022628 x 1200", "0.022627 x 900", "0.022625 x 2400", "0.022620 x 800", "0.022618 x 1500"],
asks: ["0.022644 x 1100", "0.022646 x 700", "0.022650 x 2600", "0.022655 x 950", "0.022660 x 1800"],
},
returnsBps: { last1: -0.4, last5: 1.2, last20: 3.1, last100: -2.6 },
recentMids: Array.from({ length: 20 }, (_, i) => (0.02261 + i * 0.0000015).toFixed(6)).join(" "),
trades: { count: 37, buyMon: 9400, sellMon: 7100, cvdMon: 2300, vwap: 0.022631, lastPrice: 0.022636, lastSide: "buy" },
recentTrades: Array.from({ length: 8 }, (_, i) => `${105488261 + i} ${i % 2 ? "sell" : "buy"} ${200 + i * 50} @ 0.0226${30 + i}`),
allowed: { buy: true, sell: true },
};
const json = JSON.stringify(state);
console.log("TradeState の JSON 長:", json.length, "文字 / 推定トークン:", Math.round(json.length / 4));
const t0 = performance.now();
const d = await new MockModel().decide(state);
console.log(`action=${d.action} buy=${d.probabilities.buy.toFixed(3)} latencyMs=${d.latencyMs.toFixed(1)} wall=${(performance.now() - t0).toFixed(1)}ms`);
bun run ./jt-probe.ts
出た数字はこうだった。
| 測ったこと | 実測値 |
|---|---|
TradeState の JSON 長 |
1,123 文字 |
リポジトリ式の推定入力トークン(length / 4) |
281(トークナイザ実測ではない・後述) |
MockModel の latencyMs |
80.5 ms |
うち Bun.sleep(80) のハードコード |
80 ms |
| 判定計算そのもの(1,000回ぶん) | 0.36 ms |
ここが読み違えやすい。README は「ドライランで計測:読み取り p50 18ms、ループ全体 p50 100ms(うち80msはモックの推論スタンドイン)」と書く。この80msは計測値ではなく await Bun.sleep(80) という1行だ。MockModel のコメントも正直に「パイプラインが本番と同じように振る舞うよう、推論時間の代わりに置く」と書いている。つまり公称の「ループ100ms」は、実際のモデル以外の処理が約20msで収まることを示す数字であって、Jev の応答時間を測ったものではない。
281という数字の扱いには注意がいる。これはトークナイザで測った値ではない。MockModel が料金表示のために使っている JSON.stringify(state).length / 4 という近似そのもので、リポジトリはこの推定値に jevUsdPerMTok = 0.042 を掛けて totals.jevUsd を出している。本来なら cl100k_base などのトークナイザで実測して突き合わせるべきだが、検証環境では tiktoken が辞書ファイルを取得できず(egress 制限)実行できなかった。日本語を含まない JSON なので length / 4 は大きく外れないはずだが、この記事の料金試算はすべて「リポジトリ自身の見積り式に基づく概算」であり、トークナイザ実測ではないと理解してほしい。
渡している情報自体は、板の生データではなく要約なのが特徴だ。トップ5段を "0.022628 x 1200" という文字列にし、板の厚みを mid から10/25/50 bps の3バンドに畳み、値動きを1/5/20/100ブロックのベーシスポイントに畳む。1,123文字は、判定1回ぶんの入力としてはかなり小さい。
300msの予算——ホットパスをRPC往復2回に絞る
README に「The 300 ms budget」という節が独立して置かれている。設計の中心はここだ。ホットパスの RPC 往復はちょうど2回——板を読む eth_call 1回(公開 RPC で約18ms)と、注文を投げる eth_sendRawTransaction 1回。
やらないことが明示されている。
・eth_estimateGas を呼ばない:Monad はガスをリミットに対して課金するので、見積もりに意味がない。起動時に1回推定するか、ハードコードする
・eth_sendRawTransactionSync を使わない:トランザクションが Proposed になるまでブロックしてしまう
・ガス価格を問い合わせない:EIP-1559 の type-2 固定。MAX_FEE_GWEI は上限で、実効価格は base fee + priority
レシート確認・手数料見積もりの更新・マージン残高やボールトのチェックは、すべて後続のブロックに逃がしてある(refreshBlocks 既定200ブロックごと)。送信は fire-and-forget で、ブロックイベントが運ぶのは「送った」という意図だけ。レシートは1〜2ブロック後に別の SSE イベントとして届き、placed(注文IDつき)か reverted に変わる。10ブロック待って何も来なければ lost になる。
注文の一生——sent / placed / reverted / lost
送信が fire-and-forget である以上、「注文を出した」と「注文が板に乗った」は別の事実になる。README はこの2つを別々の SSE イベントに分けている。
| ステータス | 意味 | いつ分かるか |
|---|---|---|
sent |
署名して投げた。意図でしかない | そのブロックの block イベント |
placed |
板に乗った。orderId が付く |
1〜2ブロック後の quote イベント |
reverted |
乗らなかった(価格を板が通り抜けた/取消し対象が既に約定済み) | 同上 |
lost |
10ブロック待ってもレシートが来ない | pendingBlocks 経過後 |
sim |
ドライラン。1ブロック板に置かれ、実際の約定が価格を越えたら埋まる | 即座 |
ここで効いてくるのが「Monad はガスリミットに課金する」という性質だ。reverted でも lost でも、ガスは同じだけ取られる。README も「Monad はガスリミットに課金するので、注文が通ろうが通るまいがそれが実コストだ」と書いている。後半のコスト計算がきつくなる理由の一つがこれである。
約定(fill)はさらに別の経路で届く。自分のトランザクションの中には存在しないからだ。誰かのテイカー注文が自分の指値を叩き、その Trade ログが、モデルへ渡す状態を作るのと同じ eth_getLogs のポーリングに乗って返ってくる。txHash として記録されるのは相手のトランザクションである。
全体の流れをまとめるとこうなる。
その側に置けるか"} F -->|置ける| G["batchUpdate 1本
前の注文を取消し+post-only 指値を1枚"] F -->|反対側なら置ける| H["capped:true で反対側へ"] F -->|どちらも不可| I["注文なし"] G --> J["約定は他人のテイカーが当てたときだけ"]
注文の発注と取消しは batchUpdate という1本のトランザクションにまとまっている。ここも往復を増やさないための判断だ。
建玉上限とマージンが、モデルの答えを上書きする
trader.ts の allowed() は、モデルが選んだ側に本当に注文を置けるかを2段で確かめる。1段目は建玉の上限——現在の持ち高に、板に残っている自分の注文と今回の発注量を足した露出が MAX_POSITION_MON(既定1,000 MON)を超えるなら、その側は不可。2段目はライブ時だけで、Kuru のマージン口座の残高を見る。買いなら USDC、売りなら MON が要る。
置けない場合、ボットは反対側に注文を出す。イベントには capped: true が立ち、probabilities にはモデルの本来の答えがそのまま残る。つまり記録の上では「モデルはこう言ったが、リスク管理でこうした」が区別できる。判定モデルを組み込むときに欲しい性質が、ちゃんと実装されている。
逆に言えば、表に出ている取引の方向が、必ずしもモデルの判断ではない。ダッシュボードを見るときも、ログを解析するときも、capped を見ずに方向だけ数えると、モデルの的中率を誤って評価することになる。
ソースで見つけた食い違い——モデルへの説明だけが旧仕様のまま
src/ を読んでいて1件、明確な不整合を見つけた。
src/model.ts の48行目、Jev に渡す timing の説明文はこうなっている。「注文は次のブロックで immediate-or-cancel の成行注文として執行される」。続く goal にも「取引はスプレッドを渡る(spreadBps)ので、動きはそのコストを上回らなければならない」とある。
ところが実装は違う。grep で src/ 全体を当たると、”immediate-or-cancel” という記述はこの1か所にしか存在しない。market.ts・trader.ts・index.ts・config.ts はいずれも post-only の指値を扱っていて、コメントには「quoteInsideTicks ぶんタッチの内側。決して板を越えない」と書かれている。最新コミット(b587759・2026-09-16)のメッセージ自体が「Post-only limit orders every block: earn the spread instead of pay(毎ブロック post-only の指値:スプレッドを払うのではなく稼ぐ)」だ。
これは些細な文言の差ではない。モデルに与えているコスト構造と約定の前提が、両方とも逆を向いている。
| 論点 | 説明文が伝えていること | 実際に起きること |
|---|---|---|
| 執行 | 次ブロックで必ず約定する成行 | 板に置くだけ。約定するかは他人次第 |
| スプレッド | 払う(コスト) | 受け取る(収入) |
| 超えるべきハードル | 「スプレッドを上回る動き」 | 本来の敵は逆選択と不約定 |
| 判断への影響 | 存在しないコストを織り込んで慎重になる | 慎重さの根拠が実態とずれる |
post-only のメイカー注文にとって本当のリスクは「スプレッドを払うこと」ではなく、価格が動く方向の側だけ約定してしまう(逆選択)ことと、そもそも当ててもらえないことだ。モデルは実在しないハードルを課され、実在するリスクは知らされていない。
ついでに見つかった、2つの小さなずれ
同じ説明文をもう少し読むと、細かいずれが2つ見つかる。どちらも単体では害が小さいが、上の食い違いと同じ根から出ている。
1つ目は判断の頻度。goal は「決定は数ブロックごとに行われ、次の決定まで保持される」と書くが、実装は毎ブロック判断する(間に合わなかったブロックだけが飛ぶ)。2つ目はホライズンと注文の寿命の差。モデルが聞かれているのは horizonBlocks(既定100ブロック=約30秒)先の方向だが、置かれた注文は次のブロック、つまり約300ms後にはキャンセルされて置き直される。約100倍の開きがある。
後者は必ずしも誤りではない。方向の判断が安定していれば、同じ側に置き直され続けるので、実質的には建玉が維持される。ただし「30秒先を当てにいく判断」と「300msしか板に居ない注文」が同じループの中に同居している、という構造は意識しておいたほうがいい。約定するかどうかを決めるのは30秒後の価格ではなく、次の300msの間に誰かが叩きに来るかどうかだからだ。
判定モデルは文章を返さないので「モデルの出力がおかしい」という形では表面化しない。確率は今日も綺麗に返ってくる。だからこそ、入力側の説明文が実装から遅れていないかは、コードレビューの対象として明示的に持たないと落ちる。判定モデルを製品に入れるときに効く、一般的な教訓だと思う。
(誤解のないように書くと、これは作者を責める話ではない。実装を先に直し、周辺のテキストが後から追いつくのは普通の開発の順序だ。公開リポジトリだから読めた、というだけの話である。)
「AIはガス代より安い」を検算する——1時間$9.61 対 $0.14
SPEC.md が掲げる二の矢の売り文句はこうだ。「The AI costs less than the gas.(Jev の推論は1時間およそ $0.20、同じ1時間のガスは $2〜5)」。これを README のサンプル値と config.ts の既定値から自分で計算してみる。
まずガス1ブロックぶん。Monad の base fee の下限は100 gwei、eth_maxPriorityFeePerGas は2 gwei にハードコードされている。ガスリミットの既定フォールバックは 350_000。
・350,000 × 102 gwei = 0.0357 MON
README のサンプルイベントの gasMon はぴったり 0.0357 だった。導出が一致する。MON の価格も同じサンプルから逆算できる——gasUsd 0.0024 ÷ gasMon 0.107 = $0.02243、これはイベント中の mid: 0.022636 と整合する。
300ms ブロックなら1時間は12,000ブロック。毎ブロック1本注文するので:
| 項目 | 計算 | 1時間あたり |
|---|---|---|
| ガス | 12,000 × 0.0357 MON × $0.02243 | 428 MON = $9.61 |
| Jev 推論 | 12,000 × 281 tok × $0.042/Mtok | $0.142 |
| 比 | — | ガスが約68倍 |
「AI はガス代より安い」は正しい。しかも公称より差が大きい。Jev 側は SPEC の $0.20 とほぼ一致するが($0.142)、ガス側は SPEC の $2〜5 に対して約2〜5倍になった。Monad がガスをリミットに対して課金する以上、実際に使った量ではなく 350_000 が丸ごと請求される——これが効いている。
もう一段踏み込むと、この構造の厳しさが見える。既定の TRADE_SIZE_MON は200 MON(Kuru の最小注文)。サンプルのスプレッド7.07 bps を満額取れた場合の1約定の取り分は、200 × 0.022636 × 0.000707 = $0.0032。
$9.61 ÷ $0.0032 = 3,002回。1時間12,000ブロックのうち 25%で約定して、ようやくガス代と釣り合う。しかもそれは「満額スプレッドを取れて、逆選択を食らわない」という最良の仮定での話だ。既定の BANKROLL_USD は100ドルなので、ガスだけで元手の約9.6%が1時間ごとに溶ける計算になる。
ドライランの約定率は上振れする
もう一点、デモを評価するときに効く注意がある。ドライランの約定シミュレーションは、README によれば「注文は1ブロック板に置かれ、実際の約定がその価格を通り抜けたら埋まる」という規則で動く。
これは行列の順番を無視している。実際の板では、同じ価格には既に他人の注文が並んでいて、後から来た自分の注文はその後ろに付く。誰かが叩いてきても、手前の注文が全部食われない限り自分には届かない。しかも TRADE_SIZE_MON は200 MON(Kuru の最小)で、板の厚み(サンプルでは mid から10 bps 以内に4,000 MON 前後)に対してごく薄い。行列の最後尾に近い位置に置かれる可能性が高い。
つまりドライランで観測される約定率は上限であって、ライブで同じ数字が出るとは限らない。前節で出した「ガス代を賄うのに約定率25%」というハードルは、この点を考えるとさらに厳しくなる。公開デモの数字を見るときは、dryRun が立っているかを最初に確認したい(スナップショットの / エンドポイントが返す)。
この数字は作者の主張と矛盾しない。むしろ SPEC.md の「モデルは利益を出そうとしていない」「毎ブロック取引するので、スプレッドとガスの出血が見える。損失は利益と同じくらい平然と表示する」という設計方針を、数字で裏づけている。このリポジトリは収益戦略ではなくデモンストレーションであり、そう読むのが正しい。
何ができて何ができないのか
できること:判定モデルを数百msの締め切りに載せる構成を、動くコードで読むこと。質問の設計(1問・2択・hold 無し)、遅延の扱い(隠さずカウンタにする)、ホットパスから外すものの線引き。これらは板取引と関係なく応用が効く。
できないこと:これを動かして儲けること。作者がそう設計していないし、上の計算がその理由を説明している。また HORIZON_BLOCKS(既定100ブロック=約30秒)先の方向を当てるという問題は、判定モデルの得意分野だと確かめられてもいない。前回 CI で Jev を回したときも、確信度に較正された閾値が無いことが最大の制約だった——同じ注意がここにも当てはまる。Jev 周辺の OSS がどの層に何を出しているかは Jev対応OSS 11選|ブラウザ操作・MCP・コードレビュー・Claude Code文脈選別まで実測で見る使いどころ にまとめてある。
SPEC.md の「非目標」の欄も、このリポジトリの性格をよく表している。他モデルとの比較をしない(1モデル・1市場)、バックテストも戦略の説明もしない、チャットもモデルからのテキスト出力も画面のどこにも出さない。最後の1行は、判定モデルを使うという選択そのものを設計に貫き通した宣言だ。文章を返さないモデルを選んだ以上、画面に文章が出る余地はない、という筋の通し方である。リポジトリには web/(Next.js)のダッシュボードも同梱されているが、そこに表示されるのも数値と色だけになる。
なお Jev の呼び方は、当サイトで実測したものだけでこれで3通り目になった。同じモデルに対して経路が3本ある状態なので、記事やサンプルを読むときは、どの経路の話かを先に確かめたほうがいい。必要な鍵も、返ってくるものも違う。
| 経路 | 呼び方 | 必要な鍵 | 使っている例 |
|---|---|---|---|
| TypeSafe 公式 SDK | 自前の HTTP または公式 SDK で api.typesafe.ai を直接叩く |
TypeSafe の API キー | 当サイトの CI(tools/jev_judge.py) |
| Vercel AI Gateway(生 fetch) | ai-gateway.vercel.sh/v4/ai/evaluation-model へ POST。SDK 依存を増やさない |
Gateway のキー。TypeSafe のキーは不要 | json-render の experimental_createEvaluator |
| AI SDK の評価API | @ai-sdk/typesafe-ai + experimental_evaluate |
TYPESAFE_AI_API_KEY |
jev-trader(本記事) |
同じ choice という質問の型が、3つの経路のどれでも共通で使えている点は押さえておく価値がある。プリミティブの側は安定していて、揺れているのは運び方だけだ。2つ目の Gateway 経由の経路については、json-renderとは|VercelのUI生成OSSをJev連携まで実測、LLMに書かせず選ばせる設計 でリクエストの中身とエラー時の挙動を実測してある。こちらは締め切りが緩い代わりに、1つのUIを組むのに評価を2回使う。
検証環境:Linux 6.18.44/Bun 1.3.11/2026-09-20。リポジトリは main の b587759(2026-09-16)を git clone --depth 1。bun install は成功(160パッケージ・3.60秒)。未検証:rpc.monad.xyz・公開デモ(Railway)・scripts/dry-encode.ts・scripts/bench-read.ts はいずれもネットワーク制限で到達できず、ループ本体は一度も回していない。実際の Jev の応答・約定率・損益はすべて未測定。コスト計算は README のサンプルイベントと config.ts の既定値からの概算で、実測ではない。star 1.2k はリポジトリページの表示値。
この記事は投資助言ではない。 実際の資金と秘密鍵を使う運用を勧めるものでもない。手元で読む・ドライランで回すところまでが、この記事の想定範囲である。
参照ソース
・jarrodwatts/jev-trader — 公式リポジトリ。MIT、⭐1.2k、fork 230、open issues 1、タグ0本、最新コミット b587759(2026-09-16)
・README.md — 300msの予算、SSEイベントの形式、gasMon を含むサンプルイベント
・SPEC.md — プロダクト仕様。「AIはガス代より安い」の公称値と、実験デモである旨の但し書き
・src/model.ts — TradeState 定義、QUESTIONS、JevModel と MockModel
・src/config.ts — ガスリミット・手数料・マージン・jevUsdPerMTok の既定値