レッスン11 — Capstone — 築いたもの、先送りしたもの、次にくるもの
問い
Funding pipeline を記憶からホワイトボードに描けるか? v0 で 意図的に 先送りした項目を名指し、なぜ crates/funding/ の守備範囲外かを説明できるか? このレッスンにコードはなし — メンタルモデルだけだ。
原理(最小モデル)
pipeline は 1 本の決定論的な変換: (mark, index) → premium → rate → settlements、それを clock が「十分な時間が経過したか?」で gate する。crate は pure な state machine(I/O ゼロ、外部依存は openhl-clob の AccountId のみ)で、すべて RATE_SCALE = 1e9 の符号付き固定小数点整数で計算し、overflow は saturate する。
具体例
MarkPrice ──┐
IndexPrice ─┴─► compute_premium ─► Premium
│
FundingParams ─────────► compute_rate ─► FundingRate (±rate_cap に clamp)
│
&[Position] ──────────► apply_funding ─► Vec<Settlement> { account, delta: Notional }
→ bridge → balance 更新 (将来)
╔══ FundingClock::tick ══════════════════════════════════╗
║ guard: now ≥ last_settled_at + interval_secs? ║
║ no → None / yes → 上の pipeline 実行、now に advance ║
╚════════════════════════════════════════════════════════╝
この Vec<Settlement> レーンは Step 2(CLOB)の Vec<Fill> と同じく、まだ EVM 本流(BlockExecutor)の外側を並走している — 直交レーンとして先に独立実装し、統合点を後で合わせるのが openhl の一貫した設計規律。
失敗例(誤解)
「funding crate に oracle / balance 更新 / liquidation も入れれば self-contained で便利」は誤り — それぞれが独自の関心事(oracle は staleness/aggregation、balance は storage、liquidation は別 cadence の state machine)を持ち込み、pure な state machine を汚す。これらは bridge レイヤー(将来)が tick() に接続する。crate は計算するだけ、永続化も I/O もしない。
各モジュールが届けたもの
- Determinism + 型(レッスン1〜3) — 固定小数点の語彙:
RATE_SCALE = 1e9、9 newtype(MarkPrice/IndexPrice/Premium/FundingRate/Notional/PositionSize/Position/Settlement/FundingParams)、hyperliquid_default()(3600s / ±4% / divisor 8)。学び: newtype が引数順バグをコンパイル時に防ぐ、符号規約は型の定義 doc に。 - 純粋な compute(レッスン4〜7) — stateless な数学:
compute_premium(i128 中間値・saturate)/compute_rate(割ってから clamp・defensive.abs())/apply_funding(単項マイナスで longs-pay-shorts・flat フィルタ)/saturate_i128_to_i64。15 テスト(手書き 13 + proptest 2)。学び: panic/wrap/saturate のうち saturate だけが consensus-safe。 - Clock state machine(レッスン8〜10) — discrete event loop:
FundingClock/FundingTick/tick()、7 テスト(guard / 境界 / interval 持続 / no-catch-up)。学び: composition テストが接続ミスを捕まえる、state machine は multi-call テストが要る、設計哲学は doc/コード/テストの 3 箇所に。
正直に先送り(意図的な scope cut)
5 項目、どれも実プロダクションギャップ:
- Oracle 統合 —
compute_premiumはmark/indexを入力に取るだけ。価格を 取得 する方法(CLOB mid / Pyth / Chainlink、staleness チェック、aggregation)はなし。bridge レイヤーがtick()直前に読む。 - Balance 更新 —
tick()はVec<Settlement>を返すだけ。delta を balance に 適用 するのは bridge(funding crate は storage-free)。 - Liquidation(= Step 5) — settlement は balance を負まで押しうるが、吸収可否チェックや処理はなし。独自の不変条件(insurance fund / ADL / mark トリガー)と別 cadence(funding は時間単位、liquidation はブロック単位)を持つ別 crate。
- Multi-market funding — 単一マーケットの
FundingClock1 つ。複数マーケットは bridge のHashMap<MarketId, FundingClock>で管理(crate は 1 マーケットに正しければ十分)。 - EVM event としての funding — コントラクトが funding tick を 観測 する手段なし。非 EVM コードから event を emit する plumbing は bridge の関心事。
次に来るもの
複雑度順に 4 つ:
- Oracle adapter(2-3 日) —
crates/oracle/、複数ソースを staleness チェック付きで aggregate しcurrent_index_price() -> Option<IndexPrice>を公開。難しいのは threshold 決め。 - Bridge 側の funding tick(1 週間) —
FundingClockをLiveRethEvmBridgeに組み込む。mark を CLOB から、index を oracle から、position をストアから読みtick()、settlement を balance に適用。ほとんど plumbing、funding crate は自己完結のまま。 - Liquidation エンジン(3-4 週間) —
crates/liquidation/、funding-tick 後の balance を監視し under-margined を insurance fund / ADL waterfall で処理。独立した 1 コース規模。 - Multi-market manager(1 週間) —
crates/markets/、HashMap<MarketId, FundingClock>でマーケットごとに dispatch。価値はマーケットごとの isolation。
このコースの位置(L1 Architect トラック)
- コース1〜5(Reth internals): pipeline / payload / NodeBuilder / evm crate / RPC。
- Step 1(Consensus)/ Step 2(CLOB): Malachite 統合 → マッチングエンジン。
- Step 3(Precompiles): カスタム precompile 経由の EVM ↔ CLOB ブリッジ。
- Step 4(Funding、本コース): funding state machine。pure な state、I/O なし — Step 3 の bridge plumbing と対をなす位置づけ。
- Step 5(Liquidation): funding / oracle / liquidation を
LiveRethEvmBridgeに組み込み、動作する perp DEX として組み上がる。
ここで内面化したパターンは perp funding を超えて一般化する: consensus システムの固定小数点演算(実数 x をスケール S で X = x·S にし、積は wider 型で受けて最後に S で割り戻す — fee なら S=10_000、vesting なら S=86_400)/ saturation(consensus-safe な唯一の overflow 戦略)/ 意味的区別のための newtype / レイヤー化コードの composition テスト / 設計哲学を doc・コード・テスト・散文に分散。
最終答え合わせ
cd ~/code/openhl-reference
git checkout cd94137
diff -u ~/code/my-openhl/crates/funding/ ./crates/funding/ --recursive
コース完走後、crates/funding/ ディレクトリ全体が openhl の cd94137 HEAD と byte-identical に一致する。
main に戻す:
git checkout main
まとめ(3行)
- pipeline は
(mark,index)→ premium → rate → settlements の決定論的変換をFundingClockが interval で gate するだけ。pure な state machine(I/O ゼロ、外部依存はAccountIdのみ)。 - 3 モジュールの成果: 固定小数点の語彙 → stateless な数学(saturate)→ no-catch-up な clock。22 tests(手書き 20 + proptest 2)が
cd94137に一致。 - 5 つの先送り(oracle / balance 更新 / liquidation / multi-market / funding-as-EVM-event)はすべて bridge レイヤーまたは別 crate の関心事 — drop-in 可能な、Hyperliquid 型の perp funding crate が手元にある。