Limit orders impossible on UST1/USTR: 6-vs-18-decimal pair breaches MAX_LIMIT_PRICE ~79,000x #529

Closed
opened 2026-08-15 21:58:48 +00:00 by leonardocolucci · 20 comments
leonardocolucci commented 2026-08-15 21:58:48 +00:00 (Migrated from gitlab.com)

Summary

No limit order can be placed on UST1/USTR in either orientation, so one of the three launch pairs has no usable order book. Pool swaps are unaffected.

The pair contract bounds limit prices to [MIN_LIMIT_PRICE, MAX_LIMIT_PRICE] = [1e-9, 1e9] token1-per-token0 in raw base units (limit_placement.rs, from #467). UST1 has 6 decimals, USTR has 18, so the raw price carries a ×10¹² unit factor on top of the human price:

  • UST1/USTR: 78.76 × 10¹² ≈ 7.9e13 → over MAX by ~78,760×
  • USTR/UST1 (reversed): ≈ 1.3e-14 → under MIN by ~78,760×

Every place_limit_order_batch reverts with Invalid hybrid parameters (e.g. EF2AD63D440B9112E133FABCB97E63603E6CFACCF76E34955D9DD7D2DE66918F, 0A4DFD172517B67E5BBA743CE449F58A1DD77131B8B4F3C2219D090096CAC28A). Any pair with a large decimals gap will hit the same wall.

Root cause

#467's bounds guard against Uint128 overflow in fill × price / 1/price — a property of raw magnitudes — but "extreme" was implicitly calibrated for like-decimal pairs. A 6-vs-18 pair is economically ordinary (79 USTR per UST1) yet numerically extreme purely from unit scale.

Apply the [1e-9, 1e9] bounds to price_raw × 10^(dec0 − dec1) (i.e. the human-scale price), keeping the raw value for execution. This preserves #467's intent — reject economically absurd prices — without penalizing legitimate decimal gaps.

Overflow check (so this doesn't reopen #467): on UST1/USTR at 7.9e13 raw, fill × price for a 1,000,000-UST1 order is ≈7.9e25 vs Uint128::MAX ≈3.4e38 — ~4e12× headroom; a 1-UST1 order sits at 7.9e19. The raw price also fits Decimal (max ≈3.4e20) comfortably. Normalizing the validation is therefore safe without touching the fill arithmetic for any realistic 6/18 pair.

Alternatives considered

  • Widen the raw bounds to ±1e21 — one-line, but partially reopens #467 and still fails a 24-decimal token.
  • Pair 18-dec tokens only against 18-dec quotes — works, but constrains the venue, and USTR is yours.

Both inferior to normalization.

Happy to retest instantly once a fix is migrated — we have the book ready to go live on UST1/USTR.

## Summary No limit order can be placed on **UST1/USTR** in either orientation, so one of the three launch pairs has no usable order book. Pool swaps are unaffected. The pair contract bounds limit prices to `[MIN_LIMIT_PRICE, MAX_LIMIT_PRICE]` = `[1e-9, 1e9]` token1-per-token0 in **raw base units** (`limit_placement.rs`, from #467). UST1 has 6 decimals, USTR has 18, so the raw price carries a ×10¹² unit factor on top of the human price: - UST1/USTR: `78.76 × 10¹² ≈ 7.9e13` → **over MAX by ~78,760×** - USTR/UST1 (reversed): `≈ 1.3e-14` → **under MIN by ~78,760×** Every `place_limit_order_batch` reverts with `Invalid hybrid parameters` (e.g. `EF2AD63D440B9112E133FABCB97E63603E6CFACCF76E34955D9DD7D2DE66918F`, `0A4DFD172517B67E5BBA743CE449F58A1DD77131B8B4F3C2219D090096CAC28A`). Any pair with a large decimals gap will hit the same wall. ## Root cause #467's bounds guard against `Uint128` overflow in `fill × price` / `1/price` — a property of raw magnitudes — but "extreme" was implicitly calibrated for like-decimal pairs. A 6-vs-18 pair is *economically* ordinary (79 USTR per UST1) yet *numerically* extreme purely from unit scale. ## Recommended fix: validate a decimals-normalized price Apply the `[1e-9, 1e9]` bounds to `price_raw × 10^(dec0 − dec1)` (i.e. the human-scale price), keeping the raw value for execution. This preserves #467's intent — reject economically absurd prices — without penalizing legitimate decimal gaps. **Overflow check (so this doesn't reopen #467):** on UST1/USTR at 7.9e13 raw, `fill × price` for a 1,000,000-UST1 order is ≈7.9e25 vs `Uint128::MAX` ≈3.4e38 — **~4e12× headroom**; a 1-UST1 order sits at 7.9e19. The raw price also fits `Decimal` (max ≈3.4e20) comfortably. Normalizing the *validation* is therefore safe without touching the fill arithmetic for any realistic 6/18 pair. ## Alternatives considered - Widen the raw bounds to ±1e21 — one-line, but partially reopens #467 and still fails a 24-decimal token. - Pair 18-dec tokens only against 18-dec quotes — works, but constrains the venue, and USTR is yours. Both inferior to normalization. Happy to retest instantly once a fix is migrated — we have the book ready to go live on UST1/USTR.
PlasticDigits commented 2026-08-16 07:14:04 +00:00 (Migrated from gitlab.com)

mentioned in issue #530

mentioned in issue #530
PlasticDigits commented 2026-08-16 07:32:52 +00:00 (Migrated from gitlab.com)

mentioned in merge request !1060

mentioned in merge request !1060
PlasticDigits commented 2026-08-16 08:43:17 +00:00 (Migrated from gitlab.com)

mentioned in issue #532

mentioned in issue #532
PlasticDigits commented 2026-08-16 08:43:17 +00:00 (Migrated from gitlab.com)

marked as related to #532

marked as related to #532
PlasticDigits commented 2026-08-16 08:43:21 +00:00 (Migrated from gitlab.com)

Deploy/migrate tracker: #532 (pair wasm + factory pair_code_id + frontend ship). Code fix is !1060.

Ops note: pair CONTRACT_VERSION is still 1.12.0 in !1060 — same-version wasm migrate will be rejected. Bump to 1.13.0 before store/migrate. Details and sequence are on #532.

Deploy/migrate tracker: #532 (pair wasm + factory `pair_code_id` + frontend ship). Code fix is !1060. **Ops note:** pair `CONTRACT_VERSION` is still `1.12.0` in !1060 — same-version `wasm migrate` will be rejected. Bump to `1.13.0` before store/migrate. Details and sequence are on #532.
PlasticDigits commented 2026-08-16 08:44:21 +00:00 (Migrated from gitlab.com)

Merge sanity check for !1060 (before landing on main):

  • On-chain + dApp human-scale band looks correct (L529-1–L529-5). make verify-issue-529 was green on the MR SHA.
  • Pair wasm redeploy is required. Frontend-only deploy will not fix Invalid hybrid parameters on live 6/18 pairs.
  • No Playwright path for mixed-decimal place (~79 on UST1/USTR). Contract/unit coverage is strong; browser E2E is a follow-up.
  • If indexer decimals are missing, submit can skip scaling (raw vs human mismatch). Worth a place gate later.
  • Not blocking merge. MR pipeline failed immediately (duration 0) — not treated as a code verdict.
Merge sanity check for !1060 (before landing on main): - On-chain + dApp human-scale band looks correct (L529-1–L529-5). `make verify-issue-529` was green on the MR SHA. - Pair wasm redeploy is required. Frontend-only deploy will not fix `Invalid hybrid parameters` on live 6/18 pairs. - No Playwright path for mixed-decimal place (~79 on UST1/USTR). Contract/unit coverage is strong; browser E2E is a follow-up. - If indexer decimals are missing, submit can skip scaling (raw vs human mismatch). Worth a place gate later. - Not blocking merge. MR pipeline failed immediately (duration 0) — not treated as a code verdict.
PlasticDigits commented 2026-08-16 08:45:29 +00:00 (Migrated from gitlab.com)

mentioned in commit f77caabb5c

mentioned in commit f77caabb5c91d1041076190bc2767d416caabf75
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-08-16 08:45:29 +00:00
PlasticDigits commented 2026-08-16 08:46:22 +00:00 (Migrated from gitlab.com)

mentioned in commit 781f87c52d

mentioned in commit 781f87c52dd302fd4ba0ba0e132ed5aeaaeaa098
PlasticDigits commented 2026-08-16 08:47:44 +00:00 (Migrated from gitlab.com)

mentioned in commit 9ee15f75ec

mentioned in commit 9ee15f75ec998f5d78b2f64863055f8888d781e6
PlasticDigits commented 2026-08-16 08:49:51 +00:00 (Migrated from gitlab.com)

mentioned in commit 9fd8b4eadf

mentioned in commit 9fd8b4eadfbdbefcedfb3bc198ddaa814bf0e07d
leonardocolucci commented 2026-08-16 12:05:09 +00:00 (Migrated from gitlab.com)

mentioned in issue #534

mentioned in issue #534
PlasticDigits commented 2026-08-16 13:01:26 +00:00 (Migrated from gitlab.com)

mentioned in commit f7668ffefa

mentioned in commit f7668ffefac22288c0005e719cd7f34154b013ae
PlasticDigits commented 2026-08-16 13:18:42 +00:00 (Migrated from gitlab.com)

mentioned in commit 8ef35d4cf1

mentioned in commit 8ef35d4cf13399fe9fa3867c9246f2dcd2da2c8b
PlasticDigits commented 2026-08-16 13:20:29 +00:00 (Migrated from gitlab.com)

columbus-5 migrate + dApp deploy are live. Please smoke a UST1/USTR limit.

@leonardocolucci you offered to retest as soon as this was migrated — the book should place now.

On-chain

  • Pair wasm 1.13.0, code 11582 (data_hash b5f40d61fe98bec120bad5f078d274e26b1b51927a33ceeb6d95420ca53a2cd9)
  • Store: 76FF330225FC8323E0A0E8F5553B58F36DDBE14A555FA1C34C8CF7055F0CCDE5
  • All 13 factory pairs migrated (cw2 cl8y-dex-pair / 1.13.0), including UST1/USTR terra16vxrhpvpcucu05y0nr862vf9hnqeh274uaff4s7hz4n0ea74006qf5hgqy
  • UST1/USTR migrate: ABE79F5AE309BF4798617B2253E37C809FD8A9A318D6A42C363473C1F37ECEB9
  • Factory pair_code_id 11582: 8EFA819279F2BCBA99BE6B963E06AC46C2ADBB77720D57D9F4844373D6AA9AEB
  • Git: f7668ff (cw2 bump) + 40e614a (frontend tsc) + 8ef35d4 (optimizer checksums)

Placement now applies [1e-9, 1e9] to the human price raw × 10^(decimals0 − decimals1) (L20). Execution still stores raw. Typed ~79 USTR/UST1 is in-band.

dApp

https://dex.cl8y.com rebuilt from main (Coolify). UI stays human; submit scales human → raw.

Smoke

  1. Open https://dex.cl8y.com/trade on UST1/USTR
  2. Place a limit near the human mid (~79) in either orientation
  3. Confirm it lands (no Invalid hybrid parameters)
  4. Reply here with tx hash / result

Indexer was not redeployed (limit-book price strings stay raw).

**columbus-5 migrate + dApp deploy are live.** Please smoke a UST1/USTR limit. @leonardocolucci you offered to retest as soon as this was migrated — the book should place now. ### On-chain - Pair wasm **1.13.0**, code **11582** (`data_hash` `b5f40d61fe98bec120bad5f078d274e26b1b51927a33ceeb6d95420ca53a2cd9`) - Store: `76FF330225FC8323E0A0E8F5553B58F36DDBE14A555FA1C34C8CF7055F0CCDE5` - All **13** factory pairs migrated (cw2 `cl8y-dex-pair` / `1.13.0`), including UST1/USTR `terra16vxrhpvpcucu05y0nr862vf9hnqeh274uaff4s7hz4n0ea74006qf5hgqy` - UST1/USTR migrate: `ABE79F5AE309BF4798617B2253E37C809FD8A9A318D6A42C363473C1F37ECEB9` - Factory `pair_code_id` **11582**: `8EFA819279F2BCBA99BE6B963E06AC46C2ADBB77720D57D9F4844373D6AA9AEB` - Git: `f7668ff` (cw2 bump) + `40e614a` (frontend tsc) + `8ef35d4` (optimizer checksums) Placement now applies `[1e-9, 1e9]` to the **human** price `raw × 10^(decimals0 − decimals1)` (L20). Execution still stores **raw**. Typed ~79 USTR/UST1 is in-band. ### dApp `https://dex.cl8y.com` rebuilt from `main` (Coolify). UI stays human; submit scales human → raw. ### Smoke 1. Open https://dex.cl8y.com/trade on **UST1/USTR** 2. Place a limit near the human mid (~79) in either orientation 3. Confirm it lands (no `Invalid hybrid parameters`) 4. Reply here with tx hash / result Indexer was not redeployed (limit-book `price` strings stay raw).
leonardocolucci commented 2026-08-16 14:30:16 +00:00 (Migrated from gitlab.com)

Retested on mainnet after the migrate — book places now.

  • Ask 1 UST1 @ 79.196 USTR/UST1: 0944E85F3BD54EC9A75FC3A5E6A99A8BAC96DFA7E79D4BFAED3CF0914AB256EE (order #4)
  • Bid 1 UST1 @ 77.6278: 0ED3D8C2D5E4824FF6DAFD58607AFD982E0D0881F67DCBC210C45174227F56DD (order #5)

Both rested and cancelled cleanly (93585F9D352565BD94FBE52D70E71D8D7A792AC6D8D37953D9D25EE0F71EDE72). Thanks for the fast turnaround.

Side note from the smoke (separate issue incoming): effective_fee_bps came back 180 → the pair has no discount registry set (discount_registry raw state = null on all three economic pairs).

Retested on mainnet after the migrate — book places now. - Ask 1 UST1 @ 79.196 USTR/UST1: `0944E85F3BD54EC9A75FC3A5E6A99A8BAC96DFA7E79D4BFAED3CF0914AB256EE` (order #4) - Bid 1 UST1 @ 77.6278: `0ED3D8C2D5E4824FF6DAFD58607AFD982E0D0881F67DCBC210C45174227F56DD` (order #5) Both rested and cancelled cleanly (`93585F9D352565BD94FBE52D70E71D8D7A792AC6D8D37953D9D25EE0F71EDE72`). Thanks for the fast turnaround. Side note from the smoke (separate issue incoming): `effective_fee_bps` came back 180 → the pair has no discount registry set (`discount_registry` raw state = null on all three economic pairs).
PlasticDigits commented 2026-08-17 10:26:08 +00:00 (Migrated from gitlab.com)

mentioned in issue #546

mentioned in issue #546
PlasticDigits commented 2026-08-18 12:12:15 +00:00 (Migrated from gitlab.com)

mentioned in issue #564

mentioned in issue #564
PlasticDigits commented 2026-08-22 03:10:04 +00:00 (Migrated from gitlab.com)

mentioned in issue #589

mentioned in issue #589
leonardocolucci commented 2026-08-26 22:01:10 +00:00 (Migrated from gitlab.com)

mentioned in issue #676

mentioned in issue #676
PlasticDigits commented 2026-09-01 08:14:37 +00:00 (Migrated from gitlab.com)

mentioned in issue #717

mentioned in issue #717
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#529
No description provided.