Moro SDK · ResearchReport #5

Gas constants measured on real swaps, and a bug the fork found

@moromoro/moro-sdk 1.3.3 → 1.3.4 measured 2026-09-13 Anvil forks · HyperEVM via dRPC · BNB via bsc-dataseed / publicnode
TL;DR

เรารัน swap จริงผ่าน router บน Anvil fork แล้วเทียบ gas ที่ใช้จริงกับที่ SDK ประเมิน — พบว่า split swap บน BNB revert มาตั้งแต่ 1.1.0 (แก้แล้วใน 1.3.3) และค่า gas ของ HyperEVM ที่ copy มาจาก BSC ผิดในระดับ hop: hop ของ Uniswap-V3 fork ประเมินสูงเกิน, hop ของ Hybra V4 ประเมินต่ำไป ค่าที่วัดจริงเข้าไปใน 1.3.4 error เฉลี่ยลดจาก 24 % เหลือ 12 %

The optimiser decides how many paths a trade is worth from gas constants. Until now HyperEVM's were copied from BSC with a TODO: measure. benchmark/gas-calibration.ts forks a chain with Anvil, executes real swaps for several path shapes, compares receipt.gasUsed with the SDK's estimate and fits the constants. The very first BNB run could not execute a single split swap, which turned out to be a production bug.

reverted
every BNB split swap since 1.1.0
fixed in 1.3.3
15
real HyperEVM swaps measured
1–3 hops, 1–4 paths
24 % → 12 %
mean gas-estimate error, HyperEVM
11 / 15 cases within ±10 %
0.00 bps
price change from the new constants
A/B 1.3.1 vs 1.3.4, same paths

1 · The bug: split swaps on BNB never executed

Every split case on the BNB fork failed with WardenSwap: _subRoutes and volumes lengths mismatched. The Warden-era routers (BNB, Ethereum, Polygon, Avalanche) require one volume per path; the HyperEVM router (MizuSwap ≥ 2.1) takes the last share as the remainder, and 1.1.0 adopted that convention for every chain. Single-path swaps worked, split swaps reverted — silently for the SDK, since nothing executed them in CI.

1.3.3 sends the full list or all-but-last per chain (ROUTER_VOLUMES_INCLUDE_LAST). Verified by executing a 6-path split (1,376,355 gas) on the BNB fork. Lesson for the protocol: quotes were A/B-tested at pinned blocks for weeks; execution was not. Fork replay of the winning split is now part of the checklist.

2 · HyperEVM: the error was per hop, not per router

With dRPC as the fork source, 15 real swaps executed. Backing the models' own tick-dependent gas out of each estimate and fitting router overhead + per-family hop gas jointly gave a physically sensible picture:

constantbefore (copied from BSC)fitted on 15 swapsshipped in 1.3.4
router overhead, single path95,78595,85695,785 (kept)
router overhead, split92,360 + 20,000 / path29,054 + 20,904 / path29,000 + 21,000 / path
Uniswap-V3-fork hop (PRJX, HyperSwap, Laminar, Ultrasolid, Hybra V3)111,091 / 121,18987,638 (incl. 4,700 router)82,900
Hybra V4 hop111,091172,249 (incl. 4,700)167,500
estimate beforeestimate afteractual on fork

Two outliers remain and point at the tick model, not the router: a Hybra V4 tick-spacing-1 stable pool cost 770k gas (estimate 272k) and a 3-hop path through a thin feUSD pool cost 1.24 M (estimate 0.58 M). Both cross far more initialised ticks than maxInitializedTicks lets the model see — the adaptive tick window (R6) is where that error gets fixed.

3 · BNB: single-path constant, split still unmeasured

Six single-path swaps across two runs put the BNB single-path router overhead at 55–71k against the 95,785 assumed; 1.3.4 ships the mean, 63,000. Split constants could not be fitted: bsc-dataseed and publicnode both throttle Anvil's state-fetch bursts after two cases even with retries and 12 s pauses. The one split sample (6 paths) suggests the split overhead is ~5× lower than assumed, consistent with HyperEVM. A private BNB endpoint (NodeReal free tier is enough) unblocks this.

4 · Reproduce

# needs Foundry's anvil on PATH
npx ts-node -T -O '{"module":"commonjs"}' benchmark/gas-calibration.ts --chain hyperevm --fork https://hyperliquid.drpc.org --pause 3000
npx ts-node -T -O '{"module":"commonjs"}' benchmark/gas-calibration.ts --chain bsc --fork <private rpc> --append

Samples and fits: benchmark/results/gas-calibration-*.json; constants: src/constants/gas.ts, src/constants/routes/hyperevm.ts — korrio/moro-sdk-optimized.

← Report #4 · All reports · Report #6 → Report #5 · Gas calibration · Moro SDK research