Hybrid swap gas: benchmark and tune dApp constants under warm bot load (#248 follow-up) #252

Closed
opened 2026-05-31 12:29:11 +00:00 by PlasticDigits · 20 comments
PlasticDigits commented 2026-05-31 12:29:11 +00:00 (Migrated from gitlab.com)

Summary

After contract-side CW20 aggregation lands (#248, main @ 4f26d11), run a LocalTerra gas benchmark campaign and recalibrate dApp (and swarm) gas constants so retail swaps never hit out of gas — including when the DEX is actively traded by the localnet bot swarm (“bots warm”: resting book depth, concurrent swaps, indexer/tape moving).

This issue is the measurement + floor-tuning track. Quote-driven dynamic limits are tracked separately in #249; both must share the same calibrated baselines.

Current codebase

Location Role
frontend-dapp/src/utils/constants.ts SWAP_GAS_PER_HOP (600k), SWAP_GAS_BUFFER (1.3), EXECUTE_SWAP_OPS_MIN_GAS_PER_HOP (661k), padding / safety margin
frontend-dapp/src/services/terraclassic/terraGas.ts HYBRID_SWAP_GAS_LIMIT (1.2M/hop), gasLimitForExecuteSwapOperations, getGasLimitForTx
packages/localnet-trading-swarm/src/gas.ts Must stay in lockstep with dApp constants (GitLab #115)
frontend-dapp/src/services/terraclassic/__tests__/transactions.test.ts Regression floors for broadcast Fee.gasLimit
scripts/bots/launch-swarm.sh / make swarm-local Warm-load: AMM swaps + limit placement workers populate book
skills/AGENTS_TERRACLASSIC_GAS.md Gas invariants, Station/Keplr caveats
skills/AGENTS_LOCALNET_TRADING_SWARM.md Swarm rules; bot gas parity with dApp

Today: Hybrid execute gas dropped on-chain post-#248, but the dApp still budgets flat 1.2M/hop hybrid floor and conservative pool hop math from pre-aggregation measurements (#115). Users overpay LUNC (unused gas not refunded on Classic), while under-tuning risks OOG when the book is deep or chain is hot.

Why this is needed

  • #248 reduces wasm work (fewer CW20 submessages) — static constants may be stale high (wasted fees) or, if lowered too aggressively, stale low under worst-case fills.
  • Bots warm changes the failure mode: resting ladder depth, concurrent bot + manual swaps, and storage variance on a busy LocalTerra node can push gas_used above shallow-book measurements taken on a cold chain.
  • Swarm bots use the same gas helpers as the dApp; tuning must update both or bots succeed while wallets fail (or vice versa).

Constraints / guardrails

  • gas_used ≤ gas_wanted with margin: keep SWAP_GAS_SAFETY_MARGIN + buffer ≥ observed peak under warm load (document p50 / p95 / max).
  • Never below #115 regression floors until benchmarks prove new floors; update transactions.test.ts with measured values, do not delete tests.
  • Warm benchmark required: measurements taken with swarm running (AMM + limit workers) for at least N minutes before sampling; include M = 0, 1, 3, 5 maker hybrid fills and split book+pool legs.
  • Fallback conservatism: if quote/makers unknown, retain current flat hybrid ceiling until #249 wires dynamic estimates.
  • Wallet matrix on LocalTerra: Keplr + dev wallet only for gas verification (#235); not Station built-in localterra.
  • Lockstep: any change to SWAP_GAS_BUFFER, hybrid base, or per-hop floors must touch dApp + localnet-trading-swarm in one PR.
  1. Benchmark script (or extend QA harness): deploy fresh LocalTerra wasm post-#248, seed book, start make swarm-local / launch-swarm.sh, then sample gas_used from:
    • pool-only single-hop direct pair swap
    • hybrid book-only (0 pool leg), M ∈ {1, 3, 5} makers
    • hybrid split book + pool
    • 2-hop router hybrid (if applicable)
      Record block height, maker count, and whether swarm was active.
  2. Derive new floors: e.g. HYBRID_BASE, optional HYBRID_PER_MAKER, update HYBRID_SWAP_GAS_LIMIT and/or EXECUTE_SWAP_OPS_MIN_GAS_PER_HOP only where benchmarks show headroom; add warm-load peak + safety margin.
  3. Update tests + docs: transactions.test.ts, docs/frontend.md § Terra Classic gas limits, docs/limit-orders.md, crosslink L10 / #248 in skills/AGENTS_TERRACLASSIC_GAS.md.
  4. Optional: make verify-issue-248-gas script mirroring verify-issue-238 pattern for repeatable LCD tx gas assertions.

Acceptance criteria

  • Benchmark table committed (M=0/1/3/5, pool-only, split hybrid, warm vs cold) with gas_used and proposed gas_limit.
  • dApp constants + terraGas.ts updated; swarm gas.ts matches.
  • npm test — transactions.test.ts / terraGas tests reflect new floors.
  • Manual hybrid swap on /trade and Swap page succeeds with warm swarm running; no out of gas.
  • Swarm bots run ≥30 min without OOG on hybrid paths (or documented cap if bot strategy exceeds MAX_MAKER_FILLS_HARD_CAP).
  • Documented rule: tune only after warm benchmarks; link from #248 QA note.

Test plan — functional paths

  • Cold LocalTerra: shallow hybrid (1 maker) — tx succeeds, gas_used logged.
  • Warm LocalTerra: same swap shape with swarm active — tx succeeds, gas_used ≤ new limit.
  • Deep book (5 makers): wallet broadcast succeeds under warm load.
  • Pool-only regression: unchanged success path.
  • Multi-hop 2-hop hybrid: sum-of-hops limit still safe under warm load.

Test plan — attack / abuse vectors

  • Under-gas (self-grief): artificially low constant → reproducible OOG; fix before merge.
  • Book drift between quote and execute: warm swarm adds orders after quote — ensure max_maker_fills buffer (see #249) or conservative floor prevents truncation/OOG.
  • Peak contention: burst 10+ bot txs then immediate manual swap — manual tx still within limit.

Verification criteria

  • QA sign-off on warm-load matrix attached to PR.
  • @qa-agent-team runs checklist on LocalTerra with make swarm-local active.
  • Cross-link closed/open status with #248 (contract) and #249 (dynamic frontend).
  • Contract aggregation: #248 (L10)
  • Dynamic quote-driven limits: #249
  • Gas buffer / floors: #115, #114
  • LocalTerra wallet matrix: #235
  • Swarm load: #119
## Summary After contract-side CW20 aggregation lands ([#248](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/248), `main` @ 4f26d11), run a **LocalTerra gas benchmark campaign** and **recalibrate dApp (and swarm) gas constants** so retail swaps never hit `out of gas` — including when the DEX is **actively traded by the localnet bot swarm** (“bots warm”: resting book depth, concurrent swaps, indexer/tape moving). This issue is the **measurement + floor-tuning** track. Quote-driven dynamic limits are tracked separately in [#249](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/249); both must share the same calibrated baselines. ## Current codebase | Location | Role | |----------|------| | [`frontend-dapp/src/utils/constants.ts`](frontend-dapp/src/utils/constants.ts) | `SWAP_GAS_PER_HOP` (600k), `SWAP_GAS_BUFFER` (1.3), `EXECUTE_SWAP_OPS_MIN_GAS_PER_HOP` (661k), padding / safety margin | | [`frontend-dapp/src/services/terraclassic/terraGas.ts`](frontend-dapp/src/services/terraclassic/terraGas.ts) | `HYBRID_SWAP_GAS_LIMIT` (1.2M/hop), `gasLimitForExecuteSwapOperations`, `getGasLimitForTx` | | [`packages/localnet-trading-swarm/src/gas.ts`](packages/localnet-trading-swarm/src/gas.ts) | **Must stay in lockstep** with dApp constants ([GitLab #115](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/115)) | | [`frontend-dapp/src/services/terraclassic/__tests__/transactions.test.ts`](frontend-dapp/src/services/terraclassic/__tests__/transactions.test.ts) | Regression floors for broadcast `Fee.gasLimit` | | [`scripts/bots/launch-swarm.sh`](scripts/bots/launch-swarm.sh) / [`make swarm-local`](Makefile) | Warm-load: AMM swaps + limit placement workers populate book | | [`skills/AGENTS_TERRACLASSIC_GAS.md`](skills/AGENTS_TERRACLASSIC_GAS.md) | Gas invariants, Station/Keplr caveats | | [`skills/AGENTS_LOCALNET_TRADING_SWARM.md`](skills/AGENTS_LOCALNET_TRADING_SWARM.md) | Swarm rules; bot gas parity with dApp | **Today:** Hybrid execute gas dropped on-chain post-#248, but the dApp still budgets **flat 1.2M/hop** hybrid floor and conservative pool hop math from pre-aggregation measurements (#115). Users overpay LUNC (unused gas not refunded on Classic), while **under-tuning** risks OOG when the book is deep or chain is hot. ## Why this is needed - **#248** reduces wasm work (fewer CW20 submessages) — static constants may be **stale high** (wasted fees) or, if lowered too aggressively, **stale low** under worst-case fills. - **Bots warm** changes the failure mode: resting ladder depth, concurrent bot + manual swaps, and storage variance on a busy LocalTerra node can push `gas_used` above shallow-book measurements taken on a cold chain. - Swarm bots use the **same gas helpers** as the dApp; tuning must update **both** or bots succeed while wallets fail (or vice versa). ## Constraints / guardrails - **`gas_used` ≤ `gas_wanted`** with margin: keep `SWAP_GAS_SAFETY_MARGIN` + buffer ≥ observed peak under warm load (document p50 / p95 / max). - **Never below #115 regression floors** until benchmarks prove new floors; update `transactions.test.ts` with measured values, do not delete tests. - **Warm benchmark required:** measurements taken **with swarm running** (AMM + limit workers) for at least N minutes before sampling; include M = 0, 1, 3, 5 maker hybrid fills and split book+pool legs. - **Fallback conservatism:** if quote/makers unknown, retain current flat hybrid ceiling until [#249](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/249) wires dynamic estimates. - **Wallet matrix on LocalTerra:** Keplr + dev wallet only for gas verification ([#235](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/235)); not Station built-in localterra. - **Lockstep:** any change to `SWAP_GAS_BUFFER`, hybrid base, or per-hop floors must touch **dApp + `localnet-trading-swarm`** in one PR. ## Recommended solution direction 1. **Benchmark script** (or extend QA harness): deploy fresh LocalTerra wasm post-#248, seed book, start `make swarm-local` / `launch-swarm.sh`, then sample `gas_used` from: - pool-only single-hop direct pair swap - hybrid book-only (0 pool leg), M ∈ {1, 3, 5} makers - hybrid split book + pool - 2-hop router hybrid (if applicable) Record block height, maker count, and whether swarm was active. 2. **Derive new floors:** e.g. `HYBRID_BASE`, optional `HYBRID_PER_MAKER`, update `HYBRID_SWAP_GAS_LIMIT` and/or `EXECUTE_SWAP_OPS_MIN_GAS_PER_HOP` only where benchmarks show headroom; add warm-load **peak + safety margin**. 3. **Update tests + docs:** `transactions.test.ts`, [`docs/frontend.md` § Terra Classic gas limits](docs/frontend.md#terra-classic-gas-limits), [`docs/limit-orders.md`](docs/limit-orders.md), crosslink L10 / #248 in [`skills/AGENTS_TERRACLASSIC_GAS.md`](skills/AGENTS_TERRACLASSIC_GAS.md). 4. **Optional:** `make verify-issue-248-gas` script mirroring `verify-issue-238` pattern for repeatable LCD tx gas assertions. ## Acceptance criteria - [ ] Benchmark table committed (M=0/1/3/5, pool-only, split hybrid, warm vs cold) with `gas_used` and proposed `gas_limit`. - [ ] dApp constants + `terraGas.ts` updated; **swarm `gas.ts` matches**. - [ ] `npm test` — `transactions.test.ts` / `terraGas` tests reflect new floors. - [ ] Manual hybrid swap on `/trade` and Swap page succeeds with **warm swarm running**; no `out of gas`. - [ ] Swarm bots run ≥30 min without OOG on hybrid paths (or documented cap if bot strategy exceeds `MAX_MAKER_FILLS_HARD_CAP`). - [ ] Documented rule: tune only after warm benchmarks; link from #248 QA note. ## Test plan — functional paths - [ ] Cold LocalTerra: shallow hybrid (1 maker) — tx succeeds, `gas_used` logged. - [ ] Warm LocalTerra: same swap shape with swarm active — tx succeeds, `gas_used` ≤ new limit. - [ ] Deep book (5 makers): wallet broadcast succeeds under warm load. - [ ] Pool-only regression: unchanged success path. - [ ] Multi-hop 2-hop hybrid: sum-of-hops limit still safe under warm load. ## Test plan — attack / abuse vectors - [ ] **Under-gas (self-grief):** artificially low constant → reproducible OOG; fix before merge. - [ ] **Book drift between quote and execute:** warm swarm adds orders after quote — ensure `max_maker_fills` buffer (see #249) or conservative floor prevents truncation/OOG. - [ ] **Peak contention:** burst 10+ bot txs then immediate manual swap — manual tx still within limit. ## Verification criteria - [ ] QA sign-off on warm-load matrix attached to PR. - [ ] `@qa-agent-team` runs checklist on LocalTerra with `make swarm-local` active. - [ ] Cross-link closed/open status with #248 (contract) and #249 (dynamic frontend). ## Related - Contract aggregation: [#248](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/248) (L10) - Dynamic quote-driven limits: [#249](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/249) - Gas buffer / floors: [#115](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/115), [#114](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/114) - LocalTerra wallet matrix: [#235](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/235) - Swarm load: [#119](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/119)
PlasticDigits commented 2026-05-31 12:29:12 +00:00 (Migrated from gitlab.com)

marked as related to #248

marked as related to #248
PlasticDigits commented 2026-05-31 12:29:12 +00:00 (Migrated from gitlab.com)

marked as related to #249

marked as related to #249
PlasticDigits commented 2026-05-31 12:29:17 +00:00 (Migrated from gitlab.com)

mentioned in issue #248

mentioned in issue #248
PlasticDigits commented 2026-05-31 13:10:23 +00:00 (Migrated from gitlab.com)

mentioned in commit 0be09de77c

mentioned in commit 0be09de77c28c09b9e5d5a52089a05226532085e
PlasticDigits commented 2026-05-31 13:15:59 +00:00 (Migrated from gitlab.com)

mentioned in issue #249

mentioned in issue #249
PlasticDigits commented 2026-05-31 13:16:34 +00:00 (Migrated from gitlab.com)

Follow-up from #249 (merged main @ d587048)

#249 landed quote-driven hybrid gas and single-hop direct-to-pair routing. The formula constants are calibrated to issue targets, not yet field-measured on LocalTerra under warm swarm load:

Constant Current value Defined in
HYBRID_SWAP_BASE_GAS 550_000 frontend-dapp/src/utils/constants.ts
HYBRID_SWAP_PER_MAKER_GAS 65_000 same
HYBRID_SWAP_MAKER_GAS_BUFFER 2 same
HYBRID_SWAP_GAS_FLOOR 600_000 same
Flat fallback ceiling 1_200_000 hybridSwapGas.ts

This issue (#252) should record measured gas_used vs gas_wanted for representative swaps (M = 0, 1, 2, 3, 5 maker fills; pool-only vs split book+pool) with bots warm (make swarm-local / launch-swarm.sh), then adjust the table above + keep packages/localnet-trading-swarm/src/gas.ts in lockstep.

Suggested doc sink after benchmark: docs/limit-orders.md § Execution order (hybrid gas paragraph added in #249) and skills/AGENTS_TERRACLASSIC_GAS.md.

Regression: update shallow-book cases in transactions.test.ts only when new floors are proven above warm-load gas_used peaks — do not delete #115 floors without evidence.

Cross-link: #249 implementation note.

## Follow-up from [#249](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/249) (merged `main` @ `d587048`) #249 landed **quote-driven** hybrid gas and single-hop direct-to-pair routing. The formula constants are **calibrated to issue targets**, not yet field-measured on LocalTerra under warm swarm load: | Constant | Current value | Defined in | |----------|---------------|------------| | `HYBRID_SWAP_BASE_GAS` | 550_000 | `frontend-dapp/src/utils/constants.ts` | | `HYBRID_SWAP_PER_MAKER_GAS` | 65_000 | same | | `HYBRID_SWAP_MAKER_GAS_BUFFER` | 2 | same | | `HYBRID_SWAP_GAS_FLOOR` | 600_000 | same | | Flat fallback ceiling | 1_200_000 | `hybridSwapGas.ts` | **This issue (#252) should record measured `gas_used` vs `gas_wanted`** for representative swaps (M = 0, 1, 2, 3, 5 maker fills; pool-only vs split book+pool) **with bots warm** (`make swarm-local` / `launch-swarm.sh`), then adjust the table above + keep [`packages/localnet-trading-swarm/src/gas.ts`](packages/localnet-trading-swarm/src/gas.ts) in lockstep. Suggested doc sink after benchmark: [`docs/limit-orders.md` § Execution order](docs/limit-orders.md#execution-order-in-execute_swap) (hybrid gas paragraph added in #249) and [`skills/AGENTS_TERRACLASSIC_GAS.md`](skills/AGENTS_TERRACLASSIC_GAS.md). Regression: update shallow-book cases in [`transactions.test.ts`](frontend-dapp/src/services/terraclassic/__tests__/transactions.test.ts) only when new floors are proven above warm-load `gas_used` peaks — do not delete #115 floors without evidence. Cross-link: [#249 implementation note](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/249#note_3403217565).
PlasticDigits commented 2026-05-31 13:19:24 +00:00 (Migrated from gitlab.com)

mentioned in issue #250

mentioned in issue #250
PlasticDigits commented 2026-05-31 13:26:13 +00:00 (Migrated from gitlab.com)

Follow-up from #250 — expired park cap (merged main @ ff7a680)

Contract-side hybrid match walks now cap park_expired_limit_order_for_claim at MAX_EXPIRED_PARKS_PER_SWAP = 5 per match_bids / match_asks invocation; additional expired head orders are skipped without storage writes. This bounds taker gas griefing when many expired limits stack at best prices.

Add to warm-load benchmark matrix (#252)

Please include a 10-expired-head scenario when sampling gas_used under swarm load:

Scenario Notes
10 expired bids at head, book-only hybrid (taker sells token0) Expect ≤ 5 limit_order_expired_parked events + 5 skipped; swap attrs expired_parks_used=5, expired_parks_capped=true, expired_parks_skipped=5
Same shape with fillable bid on 11th Parks 5, skips until live liquidity; compare gas_used vs shallow 1-maker hybrid
Ask side symmetric (10 expired asks, taker sells token1) Same cap semantics

Goal: confirm post-#250 gas_used for the 10-expired-head case is ≤ 5 × (park cost) + walk/skip reads + any fill cost, and decide whether HYBRID_SWAP_* / per-maker constants in constants.ts need a separate expired-park component or if the existing hybrid base + per-maker floor already covers capped parks.

Baseline comparison (optional): if a pre-#250 wasm artifact is still available locally, one cold measurement of uncapped 10-park walk would quantify savings; not blocking if only post-cap wasm is deployed.

Docs / invariants already updated (#250)

QA note

When running the #252 warm matrix, verify hybrid swaps still succeed with warm swarm + stacked expired head (not just M=1/3/5 live makers). Skipped expired orders may remain visible on indexer/LCD book APIs until a later park — expected; not a gas regression signal.

/cc @qa-agent-team — fold the rows above into the #252 acceptance checklist when benchmarking.

## Follow-up from [#250](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/250) — expired park cap (merged `main` @ ff7a680) Contract-side hybrid match walks now cap **`park_expired_limit_order_for_claim`** at **`MAX_EXPIRED_PARKS_PER_SWAP = 5`** per `match_bids` / `match_asks` invocation; additional expired head orders are skipped without storage writes. This bounds taker gas griefing when many expired limits stack at best prices. ### Add to warm-load benchmark matrix (#252) Please include a **10-expired-head** scenario when sampling `gas_used` under swarm load: | Scenario | Notes | |----------|-------| | **10 expired bids at head, book-only hybrid** (taker sells token0) | Expect **≤ 5** `limit_order_expired_parked` events + **5 skipped**; swap attrs `expired_parks_used=5`, `expired_parks_capped=true`, `expired_parks_skipped=5` | | Same shape with **fillable bid on 11th** | Parks 5, skips until live liquidity; compare `gas_used` vs shallow 1-maker hybrid | | **Ask side symmetric** (10 expired asks, taker sells token1) | Same cap semantics | **Goal:** confirm post-#250 `gas_used` for the 10-expired-head case is **≤ 5 × (park cost) + walk/skip reads + any fill cost**, and decide whether `HYBRID_SWAP_*` / per-maker constants in [`constants.ts`](frontend-dapp/src/utils/constants.ts) need a **separate expired-park component** or if the existing hybrid base + per-maker floor already covers capped parks. **Baseline comparison (optional):** if a pre-#250 wasm artifact is still available locally, one cold measurement of uncapped 10-park walk would quantify savings; not blocking if only post-cap wasm is deployed. ### Docs / invariants already updated (#250) - [`docs/limit-orders.md` § Expiry](docs/limit-orders.md#expiry-expires_at) — cap + skip semantics - Invariant **L5** in [`docs/contracts-security-audit.md`](docs/contracts-security-audit.md) - [`skills/AGENTS_TERRACLASSIC_GAS.md`](skills/AGENTS_TERRACLASSIC_GAS.md) cross-link ### QA note When running the #252 warm matrix, verify hybrid swaps still succeed with **warm swarm + stacked expired head** (not just M=1/3/5 live makers). Skipped expired orders may remain visible on indexer/LCD book APIs until a later park — expected; not a gas regression signal. /cc @qa-agent-team — fold the rows above into the #252 acceptance checklist when benchmarking.
PlasticDigits commented 2026-05-31 13:29:21 +00:00 (Migrated from gitlab.com)

mentioned in issue #251

mentioned in issue #251
PlasticDigits commented 2026-05-31 13:54:59 +00:00 (Migrated from gitlab.com)

mentioned in issue #254

mentioned in issue #254
PlasticDigits commented 2026-05-31 13:55:46 +00:00 (Migrated from gitlab.com)

mentioned in issue #260

mentioned in issue #260
PlasticDigits commented 2026-05-31 13:55:48 +00:00 (Migrated from gitlab.com)

marked as related to #260

marked as related to #260
PlasticDigits commented 2026-05-31 13:57:57 +00:00 (Migrated from gitlab.com)

mentioned in issue #255

mentioned in issue #255
Brouie commented 2026-06-01 01:57:55 +00:00 (Migrated from gitlab.com)

Ran the absolute gas profile on the current build (6b22feb), live on-chain against LocalTerra. Taker sells T0 into resting maker bids on the EMBER/CORAL dual-CW20 pair; gas_used is the executed-tx value, fills verified per scenario (M=1→1, M=3→3, M=5→5, expiry park confirmed with a refund row).

scenario gas_used note
hybrid swap M=1 (cold) 625,896 first swap in the window, 1 maker + pool
hybrid swap M=1 (warm) 481,066 2nd+ swap within the 5-min window
hybrid swap M=3 519,574 3 makers + pool
hybrid swap M=5 557,994 5 makers + pool
pool-only swap 387,596 book_input=0 comparison point
limit place single (1) 387,096 cold; a warm single read ~310k
limit place batch (3) 355,555
limit place batch (5) 400,771
cancel single 213,922
cancel batch (5) 290,011 ~19k/order marginal
claim-expired single 207,771 parked by a taker walk, then claimed

Verdict: practical, no redesign needed. Top number is the cold M=1 at ~626k gas — well within block limits.

On fees: the chain's fee check required 20,989,137 uluna (~21 LUNC) for that cold M=1 in my run — that's the observed required amount straight from the ante (code=13 insufficient-fee), not a derived rate, and it's trivial at current LUNC. I'm deliberately not quoting a uluna/gas price: the fee tracks gas_wanted (the simulated limit), not the 626k gas_used, and it ran roughly flat near 21M uluna across scenarios — so don't back-derive a per-gas rate from gas_used. Reconcile against the node's min-gas-price config for the exact figure.

Scaling is linear and cheap:

  • Hybrid: ~19.2k gas per extra maker fill (M=1→3 = 19.3k/maker, M=3→5 = 19.2k/maker — dead consistent). The dominant cost is the fixed per-tx base, not per-maker work.
  • Batch placement: ~22.6k per extra rung (~288k fixed base). Batch cancel: ~19k per extra order.
  • Worst case stays bounded: even a full book walk is capped by #254's MAX_SCAN_STEPS, so a taker can't be gas-bombed by a long/expired book.

Cold/warm: the first hybrid swap in each ~5-min window costs ~145k more (625,896 vs 481,066). That's #251's CL8Y fee-discount cache (300s TTL) populating on the first fee-bearing op — a cross-contract registry query + a storage write that warm ops within the window skip. Source-confirmed; details on #251.

On the ">=30% reduction" acceptance criterion — being straight: I can't demonstrate it. There's no clean pre-wave (ff7a680) baseline, and the one data point we do have (~615k for an M=1 cross) is essentially the current cold M=1 (~626k), so the simple single-swap case did not drop ~30%. The real win this wave delivers isn't a simple-case reduction — it's bounded worst-case gas (#254) plus cheap linear scaling, and #251 shaving ~145k off repeat swaps by the same trader inside the 5-min window.

@PlasticDigits — how do you want to handle the criterion? Either accept this reframing (bounded worst case + linear scaling + #251 warm-path win, in place of a flat % on the simple case), or I run a proper ff7a680 baseline with the same cold/warm split so we can state a real apples-to-apples %.

Layer: live on-chain on 6b22feb on the current LocalTerra. If your LocalTerra update lands these absolute numbers shift — the rig is parametrized so I can re-run in one command. Needs your call on the criterion before this is closed.

Ran the absolute gas profile on the current build (6b22feb), live on-chain against LocalTerra. Taker sells T0 into resting maker bids on the EMBER/CORAL dual-CW20 pair; gas_used is the executed-tx value, fills verified per scenario (M=1→1, M=3→3, M=5→5, expiry park confirmed with a refund row). | scenario | gas_used | note | |---|---|---| | hybrid swap M=1 (cold) | 625,896 | first swap in the window, 1 maker + pool | | hybrid swap M=1 (warm) | 481,066 | 2nd+ swap within the 5-min window | | hybrid swap M=3 | 519,574 | 3 makers + pool | | hybrid swap M=5 | 557,994 | 5 makers + pool | | pool-only swap | 387,596 | book_input=0 comparison point | | limit place single (1) | 387,096 | cold; a warm single read ~310k | | limit place batch (3) | 355,555 | | | limit place batch (5) | 400,771 | | | cancel single | 213,922 | | | cancel batch (5) | 290,011 | ~19k/order marginal | | claim-expired single | 207,771 | parked by a taker walk, then claimed | Verdict: practical, no redesign needed. Top number is the cold M=1 at ~626k gas — well within block limits. On fees: the chain's fee check required 20,989,137 uluna (~21 LUNC) for that cold M=1 in my run — that's the observed required amount straight from the ante (code=13 insufficient-fee), not a derived rate, and it's trivial at current LUNC. I'm deliberately not quoting a uluna/gas price: the fee tracks gas_wanted (the simulated limit), not the 626k gas_used, and it ran roughly flat near 21M uluna across scenarios — so don't back-derive a per-gas rate from gas_used. Reconcile against the node's min-gas-price config for the exact figure. Scaling is linear and cheap: - Hybrid: ~19.2k gas per extra maker fill (M=1→3 = 19.3k/maker, M=3→5 = 19.2k/maker — dead consistent). The dominant cost is the fixed per-tx base, not per-maker work. - Batch placement: ~22.6k per extra rung (~288k fixed base). Batch cancel: ~19k per extra order. - Worst case stays bounded: even a full book walk is capped by #254's MAX_SCAN_STEPS, so a taker can't be gas-bombed by a long/expired book. Cold/warm: the first hybrid swap in each ~5-min window costs ~145k more (625,896 vs 481,066). That's #251's CL8Y fee-discount cache (300s TTL) populating on the first fee-bearing op — a cross-contract registry query + a storage write that warm ops within the window skip. Source-confirmed; details on #251. On the ">=30% reduction" acceptance criterion — being straight: I can't demonstrate it. There's no clean pre-wave (ff7a680) baseline, and the one data point we do have (~615k for an M=1 cross) is essentially the current cold M=1 (~626k), so the simple single-swap case did not drop ~30%. The real win this wave delivers isn't a simple-case reduction — it's bounded worst-case gas (#254) plus cheap linear scaling, and #251 shaving ~145k off repeat swaps by the same trader inside the 5-min window. @PlasticDigits — how do you want to handle the criterion? Either accept this reframing (bounded worst case + linear scaling + #251 warm-path win, in place of a flat % on the simple case), or I run a proper ff7a680 baseline with the same cold/warm split so we can state a real apples-to-apples %. Layer: live on-chain on 6b22feb on the current LocalTerra. If your LocalTerra update lands these absolute numbers shift — the rig is parametrized so I can re-run in one command. Needs your call on the criterion before this is closed.
PlasticDigits commented 2026-06-01 02:03:03 +00:00 (Migrated from gitlab.com)

Dropping the arbitrary >=30% reduction criteria

Dropping the arbitrary >=30% reduction criteria
PlasticDigits commented 2026-06-01 02:20:45 +00:00 (Migrated from gitlab.com)

At current terraclassic gas fee rates, 100k gas is 2.835 LUNC, about $0.000242 at todays prices. Gas fee rates below $0.1 are acceptable, at these prices that is ~400m gas. From above table marginal on placing limit orders is 22.5k and marginal on takes is 19k, acceptable - however griefing the orderbook is still theoretically possible by an agent willing to spend enough funds to do so due to the gas cap - not gas costs. The gas cap would be reached with 15k makers + orders, however our onchain limits would hit before then increasing the slippage.

(1) We need to raise limits for the loops, given the low additional gas cost of processing additional orders, we should raise the limit to 1k orders for takers and 100 for makers.

(2) We need to add a "sweep" method that expires orders under a governance set size (ideally less than $0.01 but should be configurable per pair in either side of the token). It should both expire and clean expired orders. By default if the sweep is set to 0 tokens for both side (default) should only clean book of expired orders, up to X at a time per call to avoid the 30M gas cap.

(3) We need review permissionlessly sweeping expired orders out of the book. The gas costs are likely minimal. An onchain method for now should be sufficient, but could be useful in the future to have a watcher that sweeps expired orders. This should be documentation only as long as the method already exists, for a future update.

Once issues for 1,2,3 are created this issue can be clsoed

At current terraclassic gas fee rates, 100k gas is 2.835 LUNC, about $0.000242 at todays prices. Gas fee rates below $0.1 are acceptable, at these prices that is ~400m gas. From above table marginal on placing limit orders is 22.5k and marginal on takes is 19k, acceptable - however griefing the orderbook is still theoretically possible by an agent willing to spend enough funds to do so due to the gas cap - not gas costs. The gas cap would be reached with 15k makers + orders, however our onchain limits would hit before then increasing the slippage. (1) We need to raise limits for the loops, given the low additional gas cost of processing additional orders, we should raise the limit to 1k orders for takers and 100 for makers. (2) We need to add a "sweep" method that expires orders under a governance set size (ideally less than $0.01 but should be configurable per pair in either side of the token). It should both expire and clean expired orders. By default if the sweep is set to 0 tokens for both side (default) should only clean book of expired orders, up to X at a time per call to avoid the 30M gas cap. (3) We need review permissionlessly sweeping expired orders out of the book. The gas costs are likely minimal. An onchain method for now should be sufficient, but could be useful in the future to have a watcher that sweeps expired orders. This should be documentation only as long as the method already exists, for a future update. Once issues for 1,2,3 are created this issue can be clsoed
PlasticDigits commented 2026-06-01 02:30:49 +00:00 (Migrated from gitlab.com)

mentioned in issue #262

mentioned in issue #262
Brouie commented 2026-06-01 03:44:37 +00:00 (Migrated from gitlab.com)

mentioned in issue #256

mentioned in issue #256
Brouie commented 2026-06-01 04:11:04 +00:00 (Migrated from gitlab.com)

mentioned in issue #257

mentioned in issue #257
Brouie commented 2026-06-02 18:03:58 +00:00 (Migrated from gitlab.com)

#252 — the close condition is met; good to close.

Recap of where this landed:

  1. Benchmark campaign is on record (my warm/cold gas profile note above, live on 6b22feb): hybrid M=1/3/5 + pool-only + place/batch + cancel single/batch + claim, with the cold->warm #251 cache delta (~145k on the first fee-bearing op per 5-min window), ~19.2k gas per extra maker fill (dead linear), and worst case bounded by #254's scan cap. Conclusion at the time: practical, no redesign needed.
  2. The arbitrary >=30% reduction criterion was dropped.
  3. Your stated close condition was "once issues for 1,2,3 are created this issue can be closed." All three exist and are now CLOSED:
    • (1) raise loop limits (1k takers / 100 makers) -> #262 "Raise limit book walk caps: 1k scan steps, 100 maker fills" (closed; MAX_MAKER_FILLS_HARD_CAP=100, MAX_SCAN_STEPS=500 in dex-common).
    • (2) governance sweep that expires + cleans -> #263 "Limit book sweep: governance dust expiry + permissionless cleanup" (closed; CleanLimitBook execute msg).
    • (3) permissionless sweep + future keeper watcher (docs) -> same #263 (permissionless CleanLimitBook + keeper-watcher documentation).

So the measurement track is complete, the criterion is settled, and the three follow-up issues are created and closed. Note that the offline gas LIMITS were subsequently raised for safety by #260/#262 (book-leg sizes for the 500-step worst case) — the measured gas_USED numbers above still inform the floors; #249/#260 verified those separately. Good to close. @PlasticDigits

#252 — the close condition is met; good to close. Recap of where this landed: 1. Benchmark campaign is on record (my warm/cold gas profile note above, live on 6b22feb): hybrid M=1/3/5 + pool-only + place/batch + cancel single/batch + claim, with the cold->warm #251 cache delta (~145k on the first fee-bearing op per 5-min window), ~19.2k gas per extra maker fill (dead linear), and worst case bounded by #254's scan cap. Conclusion at the time: practical, no redesign needed. 2. The arbitrary >=30% reduction criterion was dropped. 3. Your stated close condition was "once issues for 1,2,3 are created this issue can be closed." All three exist and are now CLOSED: - (1) raise loop limits (1k takers / 100 makers) -> #262 "Raise limit book walk caps: 1k scan steps, 100 maker fills" (closed; MAX_MAKER_FILLS_HARD_CAP=100, MAX_SCAN_STEPS=500 in dex-common). - (2) governance sweep that expires + cleans -> #263 "Limit book sweep: governance dust expiry + permissionless cleanup" (closed; CleanLimitBook execute msg). - (3) permissionless sweep + future keeper watcher (docs) -> same #263 (permissionless CleanLimitBook + keeper-watcher documentation). So the measurement track is complete, the criterion is settled, and the three follow-up issues are created and closed. Note that the offline gas LIMITS were subsequently raised for safety by #260/#262 (book-leg sizes for the 500-step worst case) — the measured gas_USED numbers above still inform the floors; #249/#260 verified those separately. Good to close. @PlasticDigits
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-06-03 02:05:48 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
code/cl8y-dex-terraclassic#252
No description provided.