fix(pair): migrate must backfill DISCOUNT_REGISTRY and ORACLE_STATE #1232

Open
opened 2026-09-11 08:02:25 +00:00 by PlasticDigits · 1 comment

Summary

Pair migrate already backfills later-added Items (ORDER_NEXT_ID, escrow, limit configs, ASSET_CODE_IDS) when they are absent. It does not backfill DISCOUNT_REGISTRY or ORACLE_STATE. Instantiation writes both. Runtime execute, simulation, and several queries then call hard .load() on those keys.

A pair whose storage predates those items (older wasm, or a migrate test fixture that only seeds PAIR_INFO / RESERVES) therefore migrates “successfully” (cw2 version bump + action=migrate) and then bricks:

  • Every reserve-mutating execute (Swap, ProvideLiquidity, WithdrawLiquidity) calls oracle_update → ORACLE_STATE.load.
  • Swap / hybrid simulate / reverse-sim / limit place call DISCOUNT_REGISTRY.load.
  • Observe and OracleInfo also ORACLE_STATE.load.

GetDiscountRegistry is the exception: it uses may_load and reports None. That hides the execute-path panic. PAUSED already uses may_load and is out of scope.

This is not #535 / #536 / #538 (factory pointer snapshot and wiring addresses into existing listings). Those tickets assume the DISCOUNT_REGISTRY key exists. Do not auto-wire a registry address here.

This is not #465 / #1224 / #1231 (TWAP arithmetic). Those paths assume ORACLE_STATE already exists.

Factory already has the right pattern: migrate_from_1_0_0_backfills_pair_addr_registered_and_pair_key_index in smartcontracts/contracts/factory/src/contract.rs. Pair ASSET_CODE_IDS backfill (commit 43173579, #582) is the local precedent that these two keys missed.

Missing keys vs instantiate defaults

Item Instantiate migrate today Hard .load()
DISCOUNT_REGISTRY msg.discount_registry or None not written swap, hybrid sim / reverse-sim, limit place
ORACLE_STATE { cardinality: DEFAULT_OBSERVATION_CARDINALITY (360), index: 0, cardinality_initialized: 0 } not written oracle_update, IncreaseObservationCardinality, Observe, OracleInfo
ASSET_CODE_IDS snapshot backfilled (already handled)

cw-storage-plus Item::load is StdError when the key is absent (not a typed ContractError). Callers use ?, so the tx/query aborts.

IncreaseObservationCardinality cannot rescue a missing oracle item: it also .load()s first. There is no execute path that creates ORACLE_STATE except instantiate (and this migrate, once fixed). SetDiscountRegistry can create the discount key (factory-only save), but it does not run on migrate and does not create oracle state.

Why the new implementation is needed

  1. Migrate is the only upgrade writer. Governance store+migrate of pre-item pair wasm currently leaves two required keys empty. New CreatePair is fine (instantiate writes them). Upgrade of old instances is not.
  2. Hard load is intentional on the hot path. Fee and TWAP code should not may_load into silent wrong fees or a missing ring. The fix is to initialize storage in migrate, matching instantiate, not to weaken every .load().
  3. Idempotent defaults only. Missing DISCOUNT_REGISTRY → None (full pair fee, unwired). Missing ORACLE_STATE → empty ring with default cardinality; first later oracle_update seeds OBSERVATIONS the same way instantiate+first swap does. Do not invent TWAP samples. Do not copy factory config.discount_registry (F5 / #535: existing pairs are not retroactively wired).
  4. Already-populated pairs must be untouched. A second migrate, or a 1.14+ pair that already has Some(registry) and a live observation ring, must keep both.

Constraints / guardrails

  • CosmWasm pair storage. Founder-required. No community autoland. Do not add ready.
  • Backfill only when may_load is None. Never overwrite Some(Addr) or a written OracleState.
  • Do not write OBSERVATIONS in migrate. Empty ring + cardinality_initialized: 0 is correct; oracle_update already seeds the first slot when may_load(index) is None.
  • Do not call SetDiscountRegistry / factory All/Batch from pair migrate. Unwired (None) is the documented post-migrate default for listings that never had the key (#535).
  • Do not change fee math, I10 fail-closed registry errors, TWAP skip-on-overflow (#465 / #1224), or GetDiscountRegistry JSON.
  • Bump pair CONTRACT_VERSION (today 1.15.0 in smartcontracts/contracts/pair/src/contract.rs) only if this repo’s wasm-admin convention requires a new cw2 version for the backfill to run on already-1.15.0 instances. If live instances are already 1.15.0 without these keys, a version bump is required for ensure_from_older_version to accept a second migrate. Confirm against docs/runbooks/wasm-admin-migration.md / existing #582 pin flow — do not invent a mainnet schedule in this ticket.
  • MigrateMsg stays empty unless a documented extra field is required. Prefer zero-arg migrate like current pair::migrate.
  • No frontend, indexer, or factory CreatePair changes in this issue.

Relevant files

Path Why
smartcontracts/contracts/pair/src/contract.rs (migrate, instantiate oracle/discount saves, oracle_update, execute/sim .load() sites) Add the two may_load → save blocks; keep existing backfills
smartcontracts/contracts/pair/src/state.rs DISCOUNT_REGISTRY, ORACLE_STATE Item keys
smartcontracts/contracts/pair/src/limit_placement.rs DISCOUNT_REGISTRY.load on place
smartcontracts/packages/dex-common/src/oracle.rs DEFAULT_OBSERVATION_CARDINALITY (360) — use the same default, do not fork
Pair unit tests next to oracle_overflow_tests / factory migrate tests Missing-key migrate fixture + idempotence
docs/contracts-terraclassic.md / skills/AGENTS_FACTORY_DISCOUNT_REGISTRY.md (one sentence) Document that pair migrate initializes the key to None, not the factory pointer
  1. In pair::migrate, after the existing ASSET_CODE_IDS backfill:

    • If DISCOUNT_REGISTRY.may_load is None, save(&None).
    • If ORACLE_STATE.may_load is None, save the instantiate default (cardinality: DEFAULT_OBSERVATION_CARDINALITY, index: 0, cardinality_initialized: 0).
  2. Unit test (mock storage, no chain): set cw2 to an older pair version, seed PAIR_INFO + RESERVES (and whatever ensure_from_older_version needs), omit the two keys, call migrate. Assert both keys exist with the defaults above. Then oracle_update with non-zero reserves returns Ok. query_discount_registry / GetDiscountRegistry returns registry: null. Hybrid simulate / DISCOUNT_REGISTRY.load succeeds.

  3. Idempotence: seed DISCOUNT_REGISTRY = Some(addr) and a non-default OracleState (e.g. cardinality_initialized: 1, index: 3). Migrate. Both unchanged.

  4. Optional: one integration test that instantiates an old-layout fixture or runs migrate then Swap / ProvideLiquidity / WithdrawLiquidity without not found / type: oracle_state StdError. Do not require LocalTerra for AC.

Acceptance criteria

  • AC1. migrate on storage that lacks discount_registry writes DISCOUNT_REGISTRY = None. Pairs that already have Some(addr) or explicit None are unchanged.
  • AC2. migrate on storage that lacks oracle_state writes { cardinality: 360, index: 0, cardinality_initialized: 0 } (or DEFAULT_OBSERVATION_CARDINALITY if that constant changes in the same PR). Existing OracleState and OBSERVATIONS are not rewritten.
  • AC3. After AC1/AC2, oracle_update / Swap / ProvideLiquidity / WithdrawLiquidity / hybrid simulate / reverse-sim / limit place do not fail with missing-key StdError on those two items. First oracle write may seed observation slot 0.
  • AC4. GetDiscountRegistry still returns stored Option<Addr>. Unwired after migrate remains None (full fee_bps). Factory SetDiscountRegistry remains the only wiring path.
  • AC5. Existing migrate backfills (ORDER_NEXT_ID, escrow, limit configs, ASSET_CODE_IDS) stay. make test-contracts (or cargo test -p cl8y-dex-pair + documented pair integration) passes. #465 oracle_overflow_tests stay green.

Test plan (functional paths)

# Path Expect
T1 Mock storage: old cw2, PAIR_INFO+RESERVES, no discount/oracle keys → migrate Both keys saved with defaults; action=migrate
T2 After T1, GetDiscountRegistry registry: null
T3 After T1, OracleInfo cardinality 360, initialized 0 (or equivalent empty-ring response already implemented)
T4 After T1, oracle_update then Observe Ok; first observation seeded; no missing-key error
T5 After T1, hybrid simulate / reverse-sim DISCOUNT_REGISTRY.load succeeds; full fee
T6 Storage already DISCOUNT_REGISTRY = Some(factory-set addr) → migrate Same addr
T7 Storage already has OracleState + observation at index → migrate State and map unchanged
T8 Second migrate after T1 (if cw2 bump allows) No overwrite; still None + empty ring
T9 Instantiate-only new pair (no migrate) Unchanged vs current tests
T10 #582 ASSET_CODE_IDS backfill still runs when that key is also missing Pin saved; orthogonal to AC1/AC2

Test plan (attack, hack, and abuse)

# Vector Expect
A1 Migrate silently writes a registry Addr (fee discount without factory Set) Forbidden; must be None
A2 Migrate overwrites a live Some(registry) with None Forbidden
A3 Migrate fabricates TWAP cumulatives / timestamps Forbidden; empty ring only
A4 Migrate resets cardinality / index on a live ring Forbidden
A5 Unprivileged execute used as “init” instead of migrate (IncreaseObservationCardinality on missing key) Still fails until migrate; do not add a public init execute
A6 Missing-key .load StdError swallowed into wrong fee (zero bps) Unwired = full fee_bps only after None save
A7 Query GetDiscountRegistry may_load used as proof execute is safe Test execute/sim .load(), not only the query
A8 Replay migrate as a way to clear pause / hooks / reserves Those keys must not be rewritten

Verification criteria

  • New pair unit tests for T1–T8. Prefer cargo test -p cl8y-dex-pair migrate (name the module migrate_backfill_tests or similar).
  • cargo test -p cl8y-dex-pair oracle_overflow_tests still passes.
  • make test-contracts or the repo’s documented contract suite.
  • No on-chain attack tx. No live migrate in this ticket (ops runbook only after the wasm is reviewed).

Out of scope

  • Wiring existing listings to the factory discount pointer (#535, closed).
  • Factory CreatePair inherit (#536 / #538, closed).
  • TWAP from_ratio / price_times_dt overflow (#465, #1224, #1231).
  • Changing GetDiscountRegistry from may_load to load (optional later hardening after this backfill; not required).
  • Frontend fee chrome (I14).
  • Columbus/mainnet store schedule (ops, not this issue).

First-pass model recommendation

Recommendation: grok-high

Rationale: CosmWasm pair migrate writes persisted contract state. Founder-required surface (contracts, wasm, migrate). Cross-cutting: two Items used from execute, limit place, hybrid simulate, and oracle queries. Composer is disallowed for wasm/migrations even if the production edit is a few may_load/save lines plus tests. Verify with the missing-key fixture and idempotence tests above, plus existing #465 oracle overflow tests — not a live-chain migrate.

Related (do not retarget): #535, #536, #538, #582 (ASSET_CODE_IDS backfill precedent), #465, #1224.

## Summary Pair `migrate` already backfills later-added `Item`s (`ORDER_NEXT_ID`, escrow, limit configs, `ASSET_CODE_IDS`) when they are absent. It does **not** backfill `DISCOUNT_REGISTRY` or `ORACLE_STATE`. Instantiation writes both. Runtime execute, simulation, and several queries then call **hard** `.load()` on those keys. A pair whose storage predates those items (older wasm, or a migrate test fixture that only seeds `PAIR_INFO` / `RESERVES`) therefore migrates “successfully” (`cw2` version bump + `action=migrate`) and then bricks: - Every reserve-mutating execute (`Swap`, `ProvideLiquidity`, `WithdrawLiquidity`) calls `oracle_update` → `ORACLE_STATE.load`. - Swap / hybrid simulate / reverse-sim / limit place call `DISCOUNT_REGISTRY.load`. - `Observe` and `OracleInfo` also `ORACLE_STATE.load`. `GetDiscountRegistry` is the exception: it uses `may_load` and reports `None`. That hides the execute-path panic. `PAUSED` already uses `may_load` and is out of scope. This is not #535 / #536 / #538 (factory pointer snapshot and wiring **addresses** into existing listings). Those tickets assume the `DISCOUNT_REGISTRY` **key exists**. Do not auto-wire a registry address here. This is not #465 / #1224 / #1231 (TWAP arithmetic). Those paths assume `ORACLE_STATE` already exists. Factory already has the right pattern: `migrate_from_1_0_0_backfills_pair_addr_registered_and_pair_key_index` in `smartcontracts/contracts/factory/src/contract.rs`. Pair `ASSET_CODE_IDS` backfill (commit `43173579`, #582) is the local precedent that these two keys missed. ### Missing keys vs instantiate defaults | Item | Instantiate | `migrate` today | Hard `.load()` | | --- | --- | --- | --- | | `DISCOUNT_REGISTRY` | `msg.discount_registry` or `None` | not written | swap, hybrid sim / reverse-sim, limit place | | `ORACLE_STATE` | `{ cardinality: DEFAULT_OBSERVATION_CARDINALITY (360), index: 0, cardinality_initialized: 0 }` | not written | `oracle_update`, `IncreaseObservationCardinality`, `Observe`, `OracleInfo` | | `ASSET_CODE_IDS` | snapshot | backfilled | (already handled) | `cw-storage-plus` `Item::load` is `StdError` when the key is absent (not a typed `ContractError`). Callers use `?`, so the tx/query aborts. `IncreaseObservationCardinality` cannot rescue a missing oracle item: it also `.load()`s first. There is no execute path that creates `ORACLE_STATE` except instantiate (and this migrate, once fixed). `SetDiscountRegistry` **can** create the discount key (factory-only `save`), but it does not run on migrate and does not create oracle state. ## Why the new implementation is needed 1. **Migrate is the only upgrade writer.** Governance store+migrate of pre-item pair wasm currently leaves two required keys empty. New `CreatePair` is fine (instantiate writes them). Upgrade of old instances is not. 2. **Hard load is intentional on the hot path.** Fee and TWAP code should not `may_load` into silent wrong fees or a missing ring. The fix is to initialize storage in `migrate`, matching instantiate, not to weaken every `.load()`. 3. **Idempotent defaults only.** Missing `DISCOUNT_REGISTRY` → `None` (full pair fee, unwired). Missing `ORACLE_STATE` → empty ring with default cardinality; first later `oracle_update` seeds `OBSERVATIONS` the same way instantiate+first swap does. Do not invent TWAP samples. Do not copy factory `config.discount_registry` (F5 / #535: existing pairs are not retroactively wired). 4. **Already-populated pairs must be untouched.** A second migrate, or a 1.14+ pair that already has `Some(registry)` and a live observation ring, must keep both. ## Constraints / guardrails - CosmWasm pair storage. Founder-required. No community autoland. Do not add `ready`. - Backfill only when `may_load` is `None`. Never overwrite `Some(Addr)` or a written `OracleState`. - Do not write `OBSERVATIONS` in migrate. Empty ring + `cardinality_initialized: 0` is correct; `oracle_update` already seeds the first slot when `may_load(index)` is `None`. - Do not call `SetDiscountRegistry` / factory All/Batch from pair migrate. Unwired (`None`) is the documented post-migrate default for listings that never had the key (#535). - Do not change fee math, I10 fail-closed registry errors, TWAP skip-on-overflow (#465 / #1224), or `GetDiscountRegistry` JSON. - Bump pair `CONTRACT_VERSION` (today `1.15.0` in `smartcontracts/contracts/pair/src/contract.rs`) only if this repo’s wasm-admin convention requires a new cw2 version for the backfill to run on already-1.15.0 instances. If live instances are already 1.15.0 **without** these keys, a version bump is required for `ensure_from_older_version` to accept a second migrate. Confirm against `docs/runbooks/wasm-admin-migration.md` / existing #582 pin flow — do not invent a mainnet schedule in this ticket. - `MigrateMsg` stays empty unless a documented extra field is required. Prefer zero-arg migrate like current `pair::migrate`. - No frontend, indexer, or factory `CreatePair` changes in this issue. ## Relevant files | Path | Why | | --- | --- | | `smartcontracts/contracts/pair/src/contract.rs` (`migrate`, instantiate oracle/discount saves, `oracle_update`, execute/sim `.load()` sites) | Add the two `may_load` → `save` blocks; keep existing backfills | | `smartcontracts/contracts/pair/src/state.rs` | `DISCOUNT_REGISTRY`, `ORACLE_STATE` `Item` keys | | `smartcontracts/contracts/pair/src/limit_placement.rs` | `DISCOUNT_REGISTRY.load` on place | | `smartcontracts/packages/dex-common/src/oracle.rs` | `DEFAULT_OBSERVATION_CARDINALITY` (360) — use the same default, do not fork | | Pair unit tests next to `oracle_overflow_tests` / factory migrate tests | Missing-key migrate fixture + idempotence | | `docs/contracts-terraclassic.md` / `skills/AGENTS_FACTORY_DISCOUNT_REGISTRY.md` (one sentence) | Document that pair migrate initializes the **key** to `None`, not the factory pointer | ## Recommended direction 1. In `pair::migrate`, after the existing `ASSET_CODE_IDS` backfill: - If `DISCOUNT_REGISTRY.may_load` is `None`, `save(&None)`. - If `ORACLE_STATE.may_load` is `None`, `save` the instantiate default (`cardinality: DEFAULT_OBSERVATION_CARDINALITY`, `index: 0`, `cardinality_initialized: 0`). 2. Unit test (mock storage, no chain): set `cw2` to an older pair version, seed `PAIR_INFO` + `RESERVES` (and whatever `ensure_from_older_version` needs), **omit** the two keys, call `migrate`. Assert both keys exist with the defaults above. Then `oracle_update` with non-zero reserves returns `Ok`. `query_discount_registry` / `GetDiscountRegistry` returns `registry: null`. Hybrid simulate / `DISCOUNT_REGISTRY.load` succeeds. 3. Idempotence: seed `DISCOUNT_REGISTRY = Some(addr)` and a non-default `OracleState` (e.g. `cardinality_initialized: 1`, `index: 3`). Migrate. Both unchanged. 4. Optional: one integration test that instantiates an old-layout fixture or runs migrate then `Swap` / `ProvideLiquidity` / `WithdrawLiquidity` without `not found` / `type: oracle_state` StdError. Do not require LocalTerra for AC. ## Acceptance criteria - **AC1.** `migrate` on storage that lacks `discount_registry` writes `DISCOUNT_REGISTRY = None`. Pairs that already have `Some(addr)` or explicit `None` are unchanged. - **AC2.** `migrate` on storage that lacks `oracle_state` writes `{ cardinality: 360, index: 0, cardinality_initialized: 0 }` (or `DEFAULT_OBSERVATION_CARDINALITY` if that constant changes in the same PR). Existing `OracleState` and `OBSERVATIONS` are not rewritten. - **AC3.** After AC1/AC2, `oracle_update` / `Swap` / `ProvideLiquidity` / `WithdrawLiquidity` / hybrid simulate / reverse-sim / limit place do not fail with missing-key `StdError` on those two items. First oracle write may seed observation slot 0. - **AC4.** `GetDiscountRegistry` still returns stored `Option<Addr>`. Unwired after migrate remains `None` (full `fee_bps`). Factory `SetDiscountRegistry` remains the only wiring path. - **AC5.** Existing migrate backfills (`ORDER_NEXT_ID`, escrow, limit configs, `ASSET_CODE_IDS`) stay. `make test-contracts` (or `cargo test -p cl8y-dex-pair` + documented pair integration) passes. `#465` `oracle_overflow_tests` stay green. ## Test plan (functional paths) | # | Path | Expect | | --- | --- | --- | | T1 | Mock storage: old cw2, `PAIR_INFO`+`RESERVES`, no discount/oracle keys → `migrate` | Both keys saved with defaults; `action=migrate` | | T2 | After T1, `GetDiscountRegistry` | `registry: null` | | T3 | After T1, `OracleInfo` | cardinality 360, initialized 0 (or equivalent empty-ring response already implemented) | | T4 | After T1, `oracle_update` then `Observe` | `Ok`; first observation seeded; no missing-key error | | T5 | After T1, hybrid simulate / reverse-sim | `DISCOUNT_REGISTRY.load` succeeds; full fee | | T6 | Storage already `DISCOUNT_REGISTRY = Some(factory-set addr)` → `migrate` | Same addr | | T7 | Storage already has `OracleState` + observation at `index` → `migrate` | State and map unchanged | | T8 | Second `migrate` after T1 (if cw2 bump allows) | No overwrite; still `None` + empty ring | | T9 | Instantiate-only new pair (no migrate) | Unchanged vs current tests | | T10 | `#582` `ASSET_CODE_IDS` backfill still runs when that key is also missing | Pin saved; orthogonal to AC1/AC2 | ## Test plan (attack, hack, and abuse) | # | Vector | Expect | | --- | --- | --- | | A1 | Migrate silently writes a registry Addr (fee discount without factory Set) | Forbidden; must be `None` | | A2 | Migrate overwrites a live `Some(registry)` with `None` | Forbidden | | A3 | Migrate fabricates TWAP cumulatives / timestamps | Forbidden; empty ring only | | A4 | Migrate resets `cardinality` / `index` on a live ring | Forbidden | | A5 | Unprivileged execute used as “init” instead of migrate (`IncreaseObservationCardinality` on missing key) | Still fails until migrate; do not add a public init execute | | A6 | Missing-key `.load` StdError swallowed into wrong fee (zero bps) | Unwired = full `fee_bps` only after `None` save | | A7 | Query `GetDiscountRegistry` `may_load` used as proof execute is safe | Test execute/sim `.load()`, not only the query | | A8 | Replay migrate as a way to clear pause / hooks / reserves | Those keys must not be rewritten | ## Verification criteria - New pair unit tests for T1–T8. Prefer `cargo test -p cl8y-dex-pair migrate` (name the module `migrate_backfill_tests` or similar). - `cargo test -p cl8y-dex-pair oracle_overflow_tests` still passes. - `make test-contracts` or the repo’s documented contract suite. - No on-chain attack tx. No live migrate in this ticket (ops runbook only after the wasm is reviewed). ## Out of scope - Wiring existing listings to the factory discount pointer (#535, closed). - Factory `CreatePair` inherit (#536 / #538, closed). - TWAP `from_ratio` / `price_times_dt` overflow (#465, #1224, #1231). - Changing `GetDiscountRegistry` from `may_load` to `load` (optional later hardening **after** this backfill; not required). - Frontend fee chrome (I14). - Columbus/mainnet store schedule (ops, not this issue). ## First-pass model recommendation Recommendation: grok-high Rationale: CosmWasm pair `migrate` writes persisted contract state. Founder-required surface (contracts, wasm, migrate). Cross-cutting: two `Item`s used from execute, limit place, hybrid simulate, and oracle queries. Composer is disallowed for wasm/migrations even if the production edit is a few `may_load`/`save` lines plus tests. Verify with the missing-key fixture and idempotence tests above, plus existing `#465` oracle overflow tests — not a live-chain migrate. Related (do not retarget): #535, #536, #538, #582 (ASSET_CODE_IDS backfill precedent), #465, #1224.
Author
Owner

Verification on origin/main at 54c4868e: #1232 remains open and cannot close. pair::migrate backfills order, escrow, limit configuration, and asset-code items, but omits missing DISCOUNT_REGISTRY and ORACLE_STATE; current hard .load() paths can still fail on an older layout. Existing migration_tests::pair_migration_preserves_fee_registry_lp_admin_and_limit_book passes, but covers populated-state preservation only, not missing keys.

Remaining before closure:

  • Backfill only absent keys: DISCOUNT_REGISTRY = None and the default empty ORACLE_STATE; preserve existing values and OBSERVATIONS.
  • Add missing-key, idempotence, and post-migrate hard-load regression coverage; run pair migration/oracle regressions and make test-contracts.

Related: #1324 is the ops migration and explicitly excludes this fix; #582 is the prior asset-key backfill precedent. #535/#536/#538 cover registry wiring, while #465/#1224/#1231 cover oracle arithmetic/ratio behavior. No newer issue tracks the #1232 backfill verification.

Verification on origin/main at 54c4868e: #1232 remains open and cannot close. pair::migrate backfills order, escrow, limit configuration, and asset-code items, but omits missing DISCOUNT_REGISTRY and ORACLE_STATE; current hard .load() paths can still fail on an older layout. Existing migration_tests::pair_migration_preserves_fee_registry_lp_admin_and_limit_book passes, but covers populated-state preservation only, not missing keys. Remaining before closure: - Backfill only absent keys: DISCOUNT_REGISTRY = None and the default empty ORACLE_STATE; preserve existing values and OBSERVATIONS. - Add missing-key, idempotence, and post-migrate hard-load regression coverage; run pair migration/oracle regressions and make test-contracts. Related: #1324 is the ops migration and explicitly excludes this fix; #582 is the prior asset-key backfill precedent. #535/#536/#538 cover registry wiring, while #465/#1224/#1231 cover oracle arithmetic/ratio behavior. No newer issue tracks the #1232 backfill verification.
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#1232
No description provided.