fix(pair): widen TWAP cumulative to Uint256 (#1224 #1322) #1323

Merged
PlasticDigits merged 2 commits from fix/oracle-cumulative-u256 into main 2026-09-22 17:06:58 +00:00

Summary

  • Pair TWAP price × dt and both running cumulatives are Uint256. A stored u128 decimal string loads as the same integer (zero-extend). Sums past 2^128 stay that integer.
  • Swap, provide, and withdraw no longer abort when a representable spot's product does not fit in u128, or when the cumulative add would have. Observe returns every seconds_ago point in that case.
  • #465 / #1231 still skip an unrepresentable ratio. Pair cw2 is 1.18.0. Migrate does not rewrite OBSERVATIONS.

Founder direction on #1224 (256-bit) and #1322 (zero-extend u128 into u256) replaces the earlier wrapping-add note.

Invariants O1322-1–O1322-8: skills/AGENTS_TWAP_CUMULATIVE_U256.md, docs/twap-oracle.md, docs/contracts-security-audit.md. Ratio skip stays skills/AGENTS_TWAP_OBSERVE_RATIO.md.

Issue criteria

#1224

  • AC1. price_times_dt returns the full product. A Decimal × u64 fits in 192 bits, so execute does not abort.
  • AC2. After a representable ratio whose atomics × dt does not fit in u128, oracle_update returns Ok and stores that product. Swap, provide, and withdraw stay live once the cumulative add is also wide.
  • AC3. #465 skip remains. extreme_ratio_degrades_gracefully_instead_of_panicking is green.
  • AC4. Balanced pools still record (normal_ratio_still_records_observation).
  • AC5. cargo test in smartcontracts passed, including the oracle modules.

#1322

  • Near-max price_a and price_b plus a delta that fits in u128 but whose sum does not: oracle_update stores the full Uint256 sum and advances the ring. The exact live cumulative 340144359629112943994362291128760055446 is that unit fixture.
  • Observe with seconds_ago [0, historical] returns every offset. The "now" value is the wide sum. Storage is unchanged by the query.
  • Swap, provide, and withdraw on a cumulative within one sample of u128::MAX move reserves (multitest).
  • A window that crosses 2^128 returns (end − start) / elapsed from compute_twap_price and computeTwapPriceDecimalString.
  • Interpolation between observations that straddle 2^128 matches that integral.
  • #465 and #1231 tests stay green. JSON keys stay price_a_cumulatives / price_b_cumulatives.
  • Same-block, zero reserves, and the first zero seed are unchanged.
  • Docs no longer say this overflow is handled by returning an error. O1231 is not weakened.

Not in this PR

  • No wrapping modulo 2^128. The later comment asked for zero-extend instead. end < start stays a corrupt window.
  • No Columbus-5 store or migrate, and no mainnet broadcast. The live pair stays on the old wasm until a pair migrate to 1.18.0 that leaves OBSERVATIONS in place.
  • The multitest seeds u128::MAX − 500 rather than the live cumulative string. Block time is nanoseconds in a u64, so a ~1:1 pool cannot accrue the ~1.38e35 headroom under that seed. The live string is covered by the direct oracle_update test, which is the function swap / provide / withdraw call.
  • Limit place, cancel, claim, and reprice still do not write the oracle. No new test makes them start doing so.

Test plan

  • make verify-issue-1322 (also make verify-issue-1224) — 17/17, including the retest pass
  • cd smartcontracts && cargo test
  • cargo fmt --check and cargo clippy -- -D warnings -A clippy::uninlined_format_args
  • After merge: pair wasm migrate to cw2 1.18.0 on columbus-5, preserving OBSERVATIONS, then Observe and a small swap on the previously bricked pair
## Summary - Pair TWAP `price × dt` and both running cumulatives are `Uint256`. A stored `u128` decimal string loads as the same integer (zero-extend). Sums past `2^128` stay that integer. - Swap, provide, and withdraw no longer abort when a representable spot's product does not fit in `u128`, or when the cumulative add would have. Observe returns every `seconds_ago` point in that case. - `#465` / `#1231` still skip an unrepresentable ratio. Pair cw2 is **1.18.0**. Migrate does not rewrite `OBSERVATIONS`. Founder direction on [#1224](https://git.cl8y.com/code/cl8y-dex-terraclassic/issues/1224) (256-bit) and [#1322](https://git.cl8y.com/code/cl8y-dex-terraclassic/issues/1322) (zero-extend `u128` into `u256`) replaces the earlier wrapping-add note. Invariants **O1322-1–O1322-8**: [`skills/AGENTS_TWAP_CUMULATIVE_U256.md`](skills/AGENTS_TWAP_CUMULATIVE_U256.md), [`docs/twap-oracle.md`](docs/twap-oracle.md), [`docs/contracts-security-audit.md`](docs/contracts-security-audit.md). Ratio skip stays [`skills/AGENTS_TWAP_OBSERVE_RATIO.md`](skills/AGENTS_TWAP_OBSERVE_RATIO.md). ## Issue criteria ### #1224 - [x] AC1. `price_times_dt` returns the full product. A `Decimal` × `u64` fits in 192 bits, so execute does not abort. - [x] AC2. After a representable ratio whose atomics × dt does not fit in `u128`, `oracle_update` returns Ok and stores that product. Swap, provide, and withdraw stay live once the cumulative add is also wide. - [x] AC3. `#465` skip remains. `extreme_ratio_degrades_gracefully_instead_of_panicking` is green. - [x] AC4. Balanced pools still record (`normal_ratio_still_records_observation`). - [x] AC5. `cargo test` in `smartcontracts` passed, including the oracle modules. ### #1322 - [x] Near-max `price_a` and `price_b` plus a delta that fits in `u128` but whose sum does not: `oracle_update` stores the full `Uint256` sum and advances the ring. The exact live cumulative `340144359629112943994362291128760055446` is that unit fixture. - [x] `Observe` with `seconds_ago` `[0, historical]` returns every offset. The "now" value is the wide sum. Storage is unchanged by the query. - [x] Swap, provide, and withdraw on a cumulative within one sample of `u128::MAX` move reserves (multitest). - [x] A window that crosses `2^128` returns `(end − start) / elapsed` from `compute_twap_price` and `computeTwapPriceDecimalString`. - [x] Interpolation between observations that straddle `2^128` matches that integral. - [x] `#465` and `#1231` tests stay green. JSON keys stay `price_a_cumulatives` / `price_b_cumulatives`. - [x] Same-block, zero reserves, and the first zero seed are unchanged. - [x] Docs no longer say this overflow is handled by returning an error. **O1231** is not weakened. ## Not in this PR - No wrapping modulo `2^128`. The later comment asked for zero-extend instead. `end < start` stays a corrupt window. - No Columbus-5 store or migrate, and no mainnet broadcast. The live pair stays on the old wasm until a pair migrate to **1.18.0** that leaves `OBSERVATIONS` in place. - The multitest seeds `u128::MAX − 500` rather than the live cumulative string. Block time is nanoseconds in a `u64`, so a ~1:1 pool cannot accrue the ~1.38e35 headroom under that seed. The live string is covered by the direct `oracle_update` test, which is the function swap / provide / withdraw call. - Limit place, cancel, claim, and reprice still do not write the oracle. No new test makes them start doing so. ## Test plan - [x] `make verify-issue-1322` (also `make verify-issue-1224`) — 17/17, including the retest pass - [x] `cd smartcontracts && cargo test` - [x] `cargo fmt --check` and `cargo clippy -- -D warnings -A clippy::uninlined_format_args` - [ ] After merge: pair wasm migrate to cw2 1.18.0 on columbus-5, preserving `OBSERVATIONS`, then `Observe` and a small swap on the previously bricked pair
fix(pair): widen TWAP cumulative to Uint256
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
30de00d69b
A representable spot whose price times dt passes 2^128, and a cumulative already near that ceiling, aborted swap, provide, withdraw, and Observe. Store the full product and the running sum as Uint256 so existing u128 decimal strings zero-extend and the pair stays live.
Merge branch 'main' into fix/oracle-cumulative-u256
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
b445b77436
PlasticDigits deleted branch fix/oracle-cumulative-u256 2026-09-22 17:06:58 +00:00
Sign in to join this conversation.
No reviewers
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!1323
No description provided.