fix(pair): Observe query panics on Decimal::from_ratio after #465 #1231

Closed
opened 2026-09-11 07:57:12 +00:00 by PlasticDigits · 5 comments

Summary

QueryMsg::Observe still panics on an extreme reserve ratio. Closed #465 replaced panicking Decimal::from_ratio with Decimal::checked_from_ratio in oracle_update only (swap / provide / withdraw). The query extrapolation helper oracle_observe_single still uses the unwrapping constructor at smartcontracts/contracts/pair/src/contract.rs:424–425.

This is not a second LP brick. Observe is read-only; a panic cannot freeze RESERVES or lock withdrawals. It is a CosmWasm VM abort on a public query: LCD returns a runtime error instead of TWAP cumulatives. After #465, a pair can keep executing with a lopsided ratio (observation skipped) while every live Observe poll that extrapolates from current reserves panics.

Related but not this ticket:

  • Closed #465 — execute-path from_ratio panic / pair brick. AC verified; do not reopen.
  • Open #1224 — price_times_dt overflow on execute after a representable ratio. That issue already marks query-only oracle_observe_single as out of scope. Do not fold this into #1224.

Bundle (do not split): checked ratio on the Observe extrapolation branch, non-panic degrade, unit tests next to oracle_overflow_tests, no execute-path rewrite.

Current codebase

oracle_observe_single (called from query_observe for each seconds_ago):

if seconds_ago == 0 || target >= latest_obs.timestamp {
    if target == latest_obs.timestamp {
        return Ok((latest_obs.price_a_cumulative, latest_obs.price_b_cumulative));
    }
    if reserve_a.is_zero() || reserve_b.is_zero() {
        return Ok((latest_obs.price_a_cumulative, latest_obs.price_b_cumulative));
    }
    let dt = target - latest_obs.timestamp;
    let price_a = Decimal::from_ratio(reserve_b, reserve_a);
    let price_b = Decimal::from_ratio(reserve_a, reserve_b);
    // then price_times_dt + checked_add
}
  • Decimal::from_ratio is checked_from_ratio(...).unwrap(). Ratio above Decimal::MAX (u128::MAX / 1e18) panics.
  • Zero-reserve is already skipped. Overflow is not.
  • QueryMsg::Observe { seconds_ago } maps ContractError to StdError::generic_err. A panic never becomes that error; the VM aborts.
  • Historical ring-buffer interpolation (window older than latest_obs) does not call from_ratio. The bug is the forward extrapolation used when seconds_ago == 0 or target is after the last stored observation — the path indexers use for “now.”

oracle_update (execute) already:

Decimal::checked_from_ratio(reserve_b, reserve_a),
Decimal::checked_from_ratio(reserve_a, reserve_b),

On Err, it return Ok(()) (skip sample). oracle_overflow_tests cover only oracle_update.

Why this is needed

A pair that hit the #465 skip still has live, extreme RESERVES. The next Observe with a target after latest_obs.timestamp recomputes spot from those reserves and panics. Indexer TWAP / candles / listing adapters that poll Observe then look like an LCD outage for that pair, while swap/LP execute may still succeed.

Expected vs actual

Case Expected Actual today
Observe extrapolate, ratio > Decimal::MAX No VM panic. Skip extrapolate (return last cumulatives) or typed ContractError::Oracle Panic in from_ratio
Observe extrapolate, balanced reserves Unchanged cumulatives / interpolation Unchanged
Observe historical window ( interpolation, no spot ratio) Unchanged Unchanged
oracle_update extreme ratio Still skip / Ok (#465) Already fixed
Execute price_times_dt overflow #1224 Out of scope here

Constraints / guardrails

  • Do not reopen or retarget #465 ACs. Keep execute checked_from_ratio skip.
  • Do not implement #1224 (price_times_dt execute brick) in this ticket. If Observe’s existing price_times_dt(...).map_err(ContractError::Oracle) already returns Err instead of panicking, leave that as a typed query error unless a one-line checked path is required to avoid panic. Do not widen the execute helper “while here.”
  • Prefer the same policy as #465: missed sample / no extrapolate over clamping to Decimal::MAX (clamp can bias TWAP). For Observe, returning the last stored cumulatives (same as the zero-reserve branch) is the consistent fail-open for query liveness. A typed Oracle error is acceptable if docs say Observe may error on unrepresentable spot; a panic is not.
  • Do not change observation cardinality, ring index, query JSON field names, or seconds_ago semantics for representable ratios.
  • Do not gate this on #464 k-widening or MAX_PAIR_ASSET_DECIMALS.
  • Integer / Decimal only. No floats. No unwrap / expect on ratio construction.
  • No public on-chain attack transaction. Tests are in-tree unit/integration.
  • Founder-required CosmWasm pair query path. Do not add ready via labels.
  • Wasm migrate / code-id bump is ops after merge, not this ticket.

Relevant files

Path Why
smartcontracts/contracts/pair/src/contract.rs (oracle_observe_single) Lines 424–425 panicking from_ratio; zero-reserve sister branch
smartcontracts/contracts/pair/src/contract.rs (query_observe, QueryMsg::Observe) Public query entry; maps ContractError to StdError
smartcontracts/contracts/pair/src/contract.rs (oracle_overflow_tests) Extend with Observe/extrapolate extreme-ratio case; keep #465 tests green
smartcontracts/tests pair oracle query tests (if present) Prove Observe { seconds_ago: [0] } does not VM-panic on lopsided reserves
Indexer Observe / TWAP callers (docs only if an error vs last-cumulative choice is user-visible) Do not change indexer math in this ticket unless a panic was the only failure mode
  1. In oracle_observe_single’s forward-extrapolation branch, replace both Decimal::from_ratio calls with Decimal::checked_from_ratio (same operand order as oracle_update: price_a = reserve_b/reserve_a, price_b = reserve_a/reserve_b).
  2. On either Err: do not panic. Prefer return Ok((latest_obs.price_a_cumulative, latest_obs.price_b_cumulative)) (skip extrapolate). Alternative: Err(ContractError::Oracle { reason: ... }) so LCD gets a contract error. Pick one and test it; skip-extrapolate matches #465 “missed sample.”
  3. Keep price_times_dt + checked_add as typed Oracle errors (already non-panic). Do not switch them to unwrap.
  4. Add tests beside oracle_overflow_tests (or a sibling oracle_observe_overflow_tests):
    • Extreme reserve_a = 1, reserve_b = Uint128::MAX, seconds_ago = 0, block_time > latest_obs.timestamp → no panic; skip or typed error.
    • Reciprocal extreme (reserve_a = MAX, reserve_b = 1) → same.
    • Balanced reserves still extrapolate (cumulatives move with dt, not frozen at last obs).
  5. Leave oracle_update tests unchanged and green.

Acceptance criteria

  • AC1. Given non-zero reserves whose ratio cannot be a Decimal. When oracle_observe_single / QueryMsg::Observe extrapolates (seconds_ago == 0 or target after last observation). Then the call does not panic. It returns last cumulatives or a typed ContractError::Oracle / StdError.
  • AC2. Given the same extreme reserves. When oracle_update runs. Then still Ok with skip (#465). Observe hardening must not reintroduce execute panics.
  • AC3. Given balanced reserves and dt > 0. When Observe extrapolates. Then cumulatives still advance (not always equal to latest_obs).
  • AC4. Given a historical seconds_ago that interpolates two stored observations (no spot ratio). When Observe runs. Then behavior unchanged.
  • AC5. Existing extreme_ratio_degrades_gracefully_instead_of_panicking and normal_ratio_still_records_observation stay green.
  • AC6. No change to Observe JSON shape (price_a_cumulatives / price_b_cumulatives) for the success path.

Test plan (functional paths)

# Path Expect
T1 oracle_observe_single, reserve_a=1, reserve_b=Uint128::MAX, seconds_ago=0, block_time > last_ts No panic; skip or typed Oracle error
T2 Reciprocal reserve_a=MAX, reserve_b=1 Same as T1
T3 Zero reserve (already guarded) Last cumulatives, no panic
T4 Balanced 1e6/1e6, dt > 0, seconds_ago=0 Cumulatives ≠ last (extrapolate still works)
T5 target == latest_obs.timestamp Last cumulatives, no ratio math
T6 Historical window with cardinality_initialized >= 2 Unchanged interpolation
T7 QueryMsg::Observe { seconds_ago: [0] } through query StdError or success JSON; never VM panic
T8 oracle_update extreme ratio Still Ok / skip

Test plan (attack, hack, and abuse)

# Vector Expect
A1 After #465 skip, poll Observe every block on a lopsided pair (indexer TWAP) No VM abort loop; skip or typed error
A2 seconds_ago list with mixed 0 and in-window values, extreme reserves No panic on the 0 element; other elements follow existing interpolation/error rules
A3 Repeated Observe (query) must not be able to abort execute; execute stays on oracle_update Observe fail does not write RESERVES / OBSERVATIONS
A4 Hostile overlong seconds_ago vec (existing gas/limits) Unchanged; this ticket does not add a new DoS via vec length
A5 Clamp-to-Decimal::MAX “fix” Rejected unless spec owner documents TWAP bias; prefer skip
A6 Reintroduce from_ratio on execute “for consistency” Forbidden; execute stays checked_from_ratio

Do not publish a mainnet or CW20-hook recipe. Unit tests with Uint128::MAX / 1 are enough.

Verification criteria

  • cd smartcontracts && cargo test -p cl8y-dex-pair oracle_overflow (existing #465 + new observe cases).
  • cd smartcontracts && cargo test -p cl8y-dex-pair oracle_observe (if new module name).
  • cd smartcontracts && cargo test -p cl8y-dex-tests oracle if that suite covers Observe queries.
  • make test-contracts (or the repo’s documented contract gate) green.
  • Grep pair contract.rs: no Decimal::from_ratio on the Observe extrapolation path; execute comments for #465 remain accurate.

Out of scope

  • #1224 price_times_dt execute overflow / LP lock.
  • Reopening #465.
  • AMM k widening (#464).
  • Indexer candle math, frontend charts UI, wasm migrate.

First-pass model recommendation

Recommendation: grok-high

Rationale: Production CosmWasm pair TWAP query in oracle_observe_single (contract.rs) plus oracle overflow tests. Founder-required contracts / wasm (model-policy invariant 5) even though the edit is local to one helper. Wrong degrade (clamp vs skip vs panic) changes integrator TWAP. Not Composer-eligible. Verify with pair unit tests for extreme-ratio Observe plus existing #465 oracle_overflow_tests.

## Summary `QueryMsg::Observe` still panics on an extreme reserve ratio. Closed [#465](https://git.cl8y.com/code/cl8y-dex-terraclassic/issues/465) replaced panicking `Decimal::from_ratio` with `Decimal::checked_from_ratio` in **`oracle_update` only** (swap / provide / withdraw). The query extrapolation helper `oracle_observe_single` still uses the unwrapping constructor at `smartcontracts/contracts/pair/src/contract.rs:424–425`. This is **not** a second LP brick. Observe is read-only; a panic cannot freeze `RESERVES` or lock withdrawals. It **is** a CosmWasm VM abort on a public query: LCD returns a runtime error instead of TWAP cumulatives. After #465, a pair can keep executing with a lopsided ratio (observation skipped) while every live Observe poll that extrapolates from current reserves panics. Related but **not** this ticket: - Closed #465 — execute-path `from_ratio` panic / pair brick. AC verified; do not reopen. - Open [#1224](https://git.cl8y.com/code/cl8y-dex-terraclassic/issues/1224) — `price_times_dt` overflow on **execute** after a representable ratio. That issue already marks query-only `oracle_observe_single` as out of scope. Do not fold this into #1224. Bundle (do not split): checked ratio on the Observe extrapolation branch, non-panic degrade, unit tests next to `oracle_overflow_tests`, no execute-path rewrite. ## Current codebase `oracle_observe_single` (called from `query_observe` for each `seconds_ago`): ```rust if seconds_ago == 0 || target >= latest_obs.timestamp { if target == latest_obs.timestamp { return Ok((latest_obs.price_a_cumulative, latest_obs.price_b_cumulative)); } if reserve_a.is_zero() || reserve_b.is_zero() { return Ok((latest_obs.price_a_cumulative, latest_obs.price_b_cumulative)); } let dt = target - latest_obs.timestamp; let price_a = Decimal::from_ratio(reserve_b, reserve_a); let price_b = Decimal::from_ratio(reserve_a, reserve_b); // then price_times_dt + checked_add } ``` - `Decimal::from_ratio` is `checked_from_ratio(...).unwrap()`. Ratio above `Decimal::MAX` (`u128::MAX / 1e18`) panics. - Zero-reserve is already skipped. Overflow is not. - `QueryMsg::Observe { seconds_ago }` maps `ContractError` to `StdError::generic_err`. A **panic** never becomes that error; the VM aborts. - Historical ring-buffer interpolation (window older than `latest_obs`) does not call `from_ratio`. The bug is the **forward extrapolation** used when `seconds_ago == 0` or `target` is after the last stored observation — the path indexers use for “now.” `oracle_update` (execute) already: ```rust Decimal::checked_from_ratio(reserve_b, reserve_a), Decimal::checked_from_ratio(reserve_a, reserve_b), ``` On `Err`, it `return Ok(())` (skip sample). `oracle_overflow_tests` cover **only** `oracle_update`. ## Why this is needed A pair that hit the #465 skip still has live, extreme `RESERVES`. The next Observe with a target after `latest_obs.timestamp` recomputes spot from those reserves and panics. Indexer TWAP / candles / listing adapters that poll Observe then look like an LCD outage for that pair, while swap/LP execute may still succeed. Expected vs actual | Case | Expected | Actual today | | --- | --- | --- | | Observe extrapolate, ratio > `Decimal::MAX` | No VM panic. Skip extrapolate (return last cumulatives) or typed `ContractError::Oracle` | Panic in `from_ratio` | | Observe extrapolate, balanced reserves | Unchanged cumulatives / interpolation | Unchanged | | Observe historical window ( interpolation, no spot ratio) | Unchanged | Unchanged | | `oracle_update` extreme ratio | Still skip / Ok (#465) | Already fixed | | Execute `price_times_dt` overflow | #1224 | Out of scope here | ## Constraints / guardrails - Do **not** reopen or retarget #465 ACs. Keep execute `checked_from_ratio` skip. - Do **not** implement #1224 (`price_times_dt` execute brick) in this ticket. If Observe’s existing `price_times_dt(...).map_err(ContractError::Oracle)` already returns `Err` instead of panicking, leave that as a typed query error unless a one-line checked path is required to avoid panic. Do not widen the execute helper “while here.” - Prefer the same policy as #465: **missed sample / no extrapolate** over clamping to `Decimal::MAX` (clamp can bias TWAP). For Observe, returning the last stored cumulatives (same as the zero-reserve branch) is the consistent fail-open for **query liveness**. A typed `Oracle` error is acceptable if docs say Observe may error on unrepresentable spot; a panic is not. - Do not change observation cardinality, ring index, query JSON field names, or `seconds_ago` semantics for representable ratios. - Do not gate this on #464 k-widening or `MAX_PAIR_ASSET_DECIMALS`. - Integer / `Decimal` only. No floats. No `unwrap` / `expect` on ratio construction. - No public on-chain attack transaction. Tests are in-tree unit/integration. - Founder-required CosmWasm pair query path. Do not add `ready` via labels. - Wasm migrate / code-id bump is ops after merge, not this ticket. ## Relevant files | Path | Why | | --- | --- | | `smartcontracts/contracts/pair/src/contract.rs` (`oracle_observe_single`) | Lines 424–425 panicking `from_ratio`; zero-reserve sister branch | | `smartcontracts/contracts/pair/src/contract.rs` (`query_observe`, `QueryMsg::Observe`) | Public query entry; maps `ContractError` to `StdError` | | `smartcontracts/contracts/pair/src/contract.rs` (`oracle_overflow_tests`) | Extend with Observe/extrapolate extreme-ratio case; keep #465 tests green | | `smartcontracts/tests` pair oracle query tests (if present) | Prove `Observe { seconds_ago: [0] }` does not VM-panic on lopsided reserves | | Indexer Observe / TWAP callers (docs only if an error vs last-cumulative choice is user-visible) | Do not change indexer math in this ticket unless a panic was the only failure mode | ## Recommended direction 1. In `oracle_observe_single`’s forward-extrapolation branch, replace both `Decimal::from_ratio` calls with `Decimal::checked_from_ratio` (same operand order as `oracle_update`: `price_a = reserve_b/reserve_a`, `price_b = reserve_a/reserve_b`). 2. On either `Err`: do **not** panic. Prefer `return Ok((latest_obs.price_a_cumulative, latest_obs.price_b_cumulative))` (skip extrapolate). Alternative: `Err(ContractError::Oracle { reason: ... })` so LCD gets a contract error. Pick one and test it; skip-extrapolate matches #465 “missed sample.” 3. Keep `price_times_dt` + `checked_add` as typed `Oracle` errors (already non-panic). Do not switch them to unwrap. 4. Add tests beside `oracle_overflow_tests` (or a sibling `oracle_observe_overflow_tests`): - Extreme `reserve_a = 1`, `reserve_b = Uint128::MAX`, `seconds_ago = 0`, `block_time > latest_obs.timestamp` → no panic; skip or typed error. - Reciprocal extreme (`reserve_a = MAX`, `reserve_b = 1`) → same. - Balanced reserves still extrapolate (cumulatives move with `dt`, not frozen at last obs). 5. Leave `oracle_update` tests unchanged and green. ## Acceptance criteria - AC1. **Given** non-zero reserves whose ratio cannot be a `Decimal`. **When** `oracle_observe_single` / `QueryMsg::Observe` extrapolates (`seconds_ago == 0` or target after last observation). **Then** the call does not panic. It returns last cumulatives or a typed `ContractError::Oracle` / `StdError`. - AC2. **Given** the same extreme reserves. **When** `oracle_update` runs. **Then** still `Ok` with skip (#465). Observe hardening must not reintroduce execute panics. - AC3. **Given** balanced reserves and `dt > 0`. **When** Observe extrapolates. **Then** cumulatives still advance (not always equal to `latest_obs`). - AC4. **Given** a historical `seconds_ago` that interpolates two stored observations (no spot ratio). **When** Observe runs. **Then** behavior unchanged. - AC5. Existing `extreme_ratio_degrades_gracefully_instead_of_panicking` and `normal_ratio_still_records_observation` stay green. - AC6. No change to Observe JSON shape (`price_a_cumulatives` / `price_b_cumulatives`) for the success path. ## Test plan (functional paths) | # | Path | Expect | | --- | --- | --- | | T1 | `oracle_observe_single`, `reserve_a=1`, `reserve_b=Uint128::MAX`, `seconds_ago=0`, `block_time > last_ts` | No panic; skip or typed Oracle error | | T2 | Reciprocal `reserve_a=MAX`, `reserve_b=1` | Same as T1 | | T3 | Zero reserve (already guarded) | Last cumulatives, no panic | | T4 | Balanced 1e6/1e6, `dt > 0`, `seconds_ago=0` | Cumulatives ≠ last (extrapolate still works) | | T5 | `target == latest_obs.timestamp` | Last cumulatives, no ratio math | | T6 | Historical window with `cardinality_initialized >= 2` | Unchanged interpolation | | T7 | `QueryMsg::Observe { seconds_ago: [0] }` through `query` | `StdError` or success JSON; never VM panic | | T8 | `oracle_update` extreme ratio | Still Ok / skip | ## Test plan (attack, hack, and abuse) | # | Vector | Expect | | --- | --- | --- | | A1 | After #465 skip, poll Observe every block on a lopsided pair (indexer TWAP) | No VM abort loop; skip or typed error | | A2 | `seconds_ago` list with mixed 0 and in-window values, extreme reserves | No panic on the 0 element; other elements follow existing interpolation/error rules | | A3 | Repeated Observe (query) must not be able to abort execute; execute stays on `oracle_update` | Observe fail does not write `RESERVES` / `OBSERVATIONS` | | A4 | Hostile overlong `seconds_ago` vec (existing gas/limits) | Unchanged; this ticket does not add a new DoS via vec length | | A5 | Clamp-to-`Decimal::MAX` “fix” | Rejected unless spec owner documents TWAP bias; prefer skip | | A6 | Reintroduce `from_ratio` on execute “for consistency” | Forbidden; execute stays `checked_from_ratio` | Do not publish a mainnet or CW20-hook recipe. Unit tests with `Uint128::MAX` / `1` are enough. ## Verification criteria - `cd smartcontracts && cargo test -p cl8y-dex-pair oracle_overflow` (existing #465 + new observe cases). - `cd smartcontracts && cargo test -p cl8y-dex-pair oracle_observe` (if new module name). - `cd smartcontracts && cargo test -p cl8y-dex-tests oracle` if that suite covers Observe queries. - `make test-contracts` (or the repo’s documented contract gate) green. - Grep pair `contract.rs`: no `Decimal::from_ratio` on the Observe extrapolation path; execute comments for #465 remain accurate. ## Out of scope - #1224 `price_times_dt` execute overflow / LP lock. - Reopening #465. - AMM `k` widening (#464). - Indexer candle math, frontend charts UI, wasm migrate. ## First-pass model recommendation Recommendation: grok-high Rationale: Production CosmWasm pair TWAP query in `oracle_observe_single` (`contract.rs`) plus oracle overflow tests. Founder-required **contracts / wasm** (model-policy invariant 5) even though the edit is local to one helper. Wrong degrade (clamp vs skip vs panic) changes integrator TWAP. Not Composer-eligible. Verify with pair unit tests for extreme-ratio Observe plus existing `#465` `oracle_overflow_tests`.
Author
Owner

cl8y-agent-control: queued implement job 9e0548ce-fa2d-4688-9974-9c764475d7da (not executed; no Hetzner VM).

cl8y-agent-control: queued `implement` job `9e0548ce-fa2d-4688-9974-9c764475d7da` (not executed; no Hetzner VM).
Author
Owner

Merged onto origin/main via #1236. make verify-issue-1231 was 14/14. Observe uses checked_from_ratio and skips (no clamp).

Leftover: pair wasm store+migrate so listed columbus-5 pairs actually skip on overflow instead of panicking. Bundled with #1227 / #1230 in a new ops issue. Not #1232 (missing-key backfill).

This PR also landed .woodpecker.yaml (gitleaks). Live Woodpecker still did not post ci/woodpecker/pr/woodpecker; merges used a local gitleaks scan plus a Forgejo status. Follow-up CI issue filed separately.

Merged onto `origin/main` via #1236. `make verify-issue-1231` was 14/14. Observe uses `checked_from_ratio` and skips (no clamp). Leftover: pair wasm store+migrate so listed columbus-5 pairs actually skip on overflow instead of panicking. Bundled with #1227 / #1230 in a new ops issue. Not #1232 (missing-key backfill). This PR also landed `.woodpecker.yaml` (gitleaks). Live Woodpecker still did not post `ci/woodpecker/pr/woodpecker`; merges used a local gitleaks scan plus a Forgejo status. Follow-up CI issue filed separately.
Author
Owner

Follow-up ops ticket: #1246. Woodpecker enablement: #1247.

Follow-up ops ticket: #1246. Woodpecker enablement: #1247.
Author
Owner

columbus-5 wasm for this ticket is not live. Ops tracker: #1246.

Live pairs are 11639 / 1.16.0 (#712). LCD HybridSimulation belief_price: "0" still 200 (same output as omitted belief) — #1230 / #1227 / #1231 execute/query wasm still needs a 1.17.0 store+migrate (git CONTRACT_VERSION is still 1.15.0). Tax listed pin is 11630 (not 11611/11619); ALPHA terra1x6e64… is 1.0.0 and needs a tax cw2 bump + CMM migrate for #1228 / #1237.

columbus-5 wasm for this ticket is **not** live. Ops tracker: [#1246](https://git.cl8y.com/code/cl8y-dex-terraclassic/issues/1246). Live pairs are **11639 / 1.16.0** (#712). LCD `HybridSimulation` `belief_price: "0"` still **200** (same output as omitted belief) — #1230 / #1227 / #1231 execute/query wasm still needs a **1.17.0** store+migrate (git `CONTRACT_VERSION` is still 1.15.0). Tax listed pin is **11630** (not 11611/11619); ALPHA `terra1x6e64…` is 1.0.0 and needs a tax cw2 bump + CMM migrate for #1228 / #1237.
Author
Owner

columbus-5 pair wasm is live: 11664 / cw2 1.17.0 (store DB35943A4925059E770A63C9ADDF35622696088A4DFFC8A91110466034995961). Factory pair_code_id=11664, GetPairCount=20. LCD UST1/cUSTC HybridSimulation belief_price:"0" → Invalid belief_price 0. Ops #1246.

columbus-5 pair wasm is live: **11664 / cw2 1.17.0** (store `DB35943A4925059E770A63C9ADDF35622696088A4DFFC8A91110466034995961`). Factory `pair_code_id=11664`, `GetPairCount=20`. LCD UST1/cUSTC `HybridSimulation` `belief_price:"0"` → `Invalid belief_price 0`. Ops [#1246](https://git.cl8y.com/code/cl8y-dex-terraclassic/issues/1246).
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#1231
No description provided.