ops: P0 order-enforcing upgrade-582 script, columbus-5 ContractInfo probe, freeze/unfreeze runbook #584

Closed
opened 2026-08-20 13:31:29 +00:00 by PlasticDigits · 23 comments
PlasticDigits commented 2026-08-20 13:31:29 +00:00 (Migrated from gitlab.com)

Gate

P0 — blocks columbus-5 rollout of factory 1.9.0 / pair 1.15.0 (invariant F6). Do not treat merge of #582 / MR 389 as production readiness. Do not close #581 with a go on 8266 / SpaceUSD listing until this issue is done and the migrate has actually run on the live factory.

Source: internal audit INTERNAL_KIMIK3_1787230030 P0 items 1–4 (findings 3.1, 3.2, 3.5, 3.7, 3.8, 3.16, F389-06, Chain A). Same ops class as prior-audit F-01/F-02 on scripts/upgrade-514-limit-discount.sh (#514) — documented order, no script enforcement, silent limit: 60 truncation.

This issue bundles the three P0 rollout controls (do not split):

  1. Order-enforcing upgrade-582 script with paginated migrate + post-migrate smoke.
  2. Columbus-5 WasmQuery::ContractInfo / LCD GET /cosmwasm/wasm/v1/contract/{addr} pre-flight probe.
  3. Documented exit-path policy + freeze/unfreeze incident runbook (incl. unfreeze-arb mitigation and batch-refresh skip).

Frontend humanization / indexer frozen-pair exclusion are P1 (before #581 relies on F6) and out of scope here — file separately if not already open.


Current codebase

What F6 already does (on fix/582-cw20-code-id-pin, not yet on main)

Factory 1.9.0 adds IsCodeIdWhitelisted and governance RefreshPairAssetCodeIds / RefreshPairAssetCodeIdsBatch. Pair 1.15.0 snapshots each listed asset's live ContractInfo.code_id into ASSET_CODE_IDS at instantiate (and backfills on migrate), then every fund-moving write path (swap, provide, withdraw, limit place/fill, cancel, claim) aborts unless live id equals the pin and the factory whitelist still contains it. Fail-closed on query errors (AssetCodeIdGuardUnavailable). Refresh refuses unlisted live ids. Same-tx MsgMigrateContract + swap is impossible (live query + tx atomicity).

main today is still factory 1.8.0 / pair 1.14.0 — no pin, no write-path re-check.

What is missing (ops only — docs exist, nothing executes them)

Gap Evidence
No scripts/upgrade-582-*.sh Prior upgrades: upgrade-514-limit-discount.sh, upgrade-518-lp-symbol.sh. F6 order is playbook-only (skills/AGENTS_CW20_CODE_ID_PIN.md step 5: factory 1.9.0 before pair 1.15.0).
Pair-first migrate = total write freeze Pair 1.15.0 queries factory IsCodeIdWhitelisted. Against factory ≤1.8.0 that smart query fails (unknown variant) → AssetCodeIdGuardUnavailable on every gated write on every upgraded pair until factory migrates. Fail-closed (no fund loss) but protocol liveness freeze. Nothing asserts factory cw2 ≥1.9.0 before broadcasting pair migrates.
Pagination / count reconcile not encoded Factory query_pairs clamps limit via calc_limit to MAX_LIMIT = 30 (dex-common/src/pagination.rs). PairsResponse has no has_more. upgrade-514 and rotate-fee-treasury.sh still query limit: 60 once and migrate that page — tail silently skipped (prior F-02). Live factory currently has 14 pairs (fits one page today; will not stay true). GetPairCount exists and is unused by those scripts' migrate loops.
Batch refresh all-or-nothing; skip undocumented RefreshPairAssetCodeIdsBatch sends one WasmMsg::Execute per pair in a single tx (factory execute_refresh_pair_asset_code_ids_batch). The incident pair whose token migrated to an unlisted id must fail refresh (pair refuses unlisted live ids) → whole batch reverts, blocking later-indexed pairs. Workaround (single refresh for good pairs, then batch with start_after past the bad index) is verified correct in code (start_idx = start_after+1, calc_limit clamps [1,30]) but not in any runbook.
No post-migrate smoke verify-issue-582.sh is contracts + docs greps only (pin suite ×2, version strings). No on-chain rung: no GetAssetCodeIds, no Simulation, no cw2 version assert, no GetPairCount reconcile. A green check does not mean the migrate ran or ran in order.
Columbus-5 ContractInfo assumed, not probed Pair migrate backfill and every gated write call query_wasm_contract_info. Factory CreatePair has used the same query since launch (factory/src/contract.rs ~220–231) and LocalTerra passed, but a columbus-5 LCD/chain miss would abort pair migrate (pair stuck on 1.14.0 — finding 3.9) or freeze writes. GET /cosmwasm/wasm/v1/contract/{addr} is used off-chain in add-ust1-secondary-pair.sh (contract_info.code_id) and handoff-cl8ydeploy-to-multisig.sh (admin). No F6 pre-flight records that this endpoint returns code_id for every listed asset on the target LCD before migrate.
Exit-path freeze is implicit gate_asset_code_ids also covers CancelLimitOrder(s), ClaimExpiredLimitOrder(s), and WithdrawLiquidity. A third-party token wasm admin (the actor F6 defends against) can freeze all user value in a pair (LP underlying, maker escrow, parked claims) until governance whitelists + refreshes. Accounting: exits could stay open under FoT (recipient bears outbound tax; pair-side PENDING_ESCROW/RESERVES stay consistent). Maximal freeze is a conservative choice, currently undocumented as a tradeoff, untested for cancel/claim/withdraw, and has no governance SLA.
Unfreeze is an arb During freeze, Simulation/HybridSimulation stay ungated and TWAP/OBSERVATIONS go stale (no swap → no observation write). First swap after refresh executes against stale reserves/TWAP (~5% × frozen TVL extractable, depth-limited). SetPairPaused through refresh + private corrective rebalance is not in emergency-commands.md or cw20-whitelist-policy.md. Governance timing of refresh is MEV-sensitive.
10184 de-whitelist is a protocol halt 13 of 14 live assets share mintable code 10184. RemoveWhitelistedCodeId(10184) freezes essentially every pair. Milder controls already exist: SetPairPaused / BlacklistPair / BlacklistToken. Incident runbook must say so.

Playbooks that describe F6 but do not enforce it: skills/AGENTS_CW20_CODE_ID_PIN.md, docs/runbooks/cw20-code-id-ops.md, docs/runbooks/wasm-admin-migration.md (lists #518 only), docs/runbooks/launch-checklist.md.


Why this is needed

Without a factory-first, count-reconciled upgrade script, an operator following the #514/#518 pattern (or a partial/retried run) can migrate pairs before factory 1.9.0 and freeze all gated writes on those pairs until a follow-up factory migrate. Without post-migrate smoke, a silent skip of the 31st pair leaves that pair on 1.14.0 (no pin; F6 hole remains for that market). Without a columbus-5 ContractInfo probe, pair migrate can abort mid-loop and leave a mixed version set. Without an exit-path + unfreeze runbook, the first real issuer-migrate freeze (permissionless 6036 listing, or 8266 SpaceUSD after #581) strands LP/escrow with no SLA, and the unfreeze hands stale-price arb to whoever is watching the mempool — including insiders who know refresh timing.

This is the production-readiness gap the #582 contract MR explicitly does not close.


Constraints / guardrails

  • No new fund-moving contract logic in this issue. F6 on-chain behavior stays as merged in #582. Do not add FoT / balance-delta swap math (H-01). Do not silently un-gate exits in the same MR as the ops script unless the documented policy decision is (a) open exits — that is a separate, explicit contract change with its own tests.
  • Factory 1.9.0 before any pair 1.15.0 migrate. Script must abort (non-zero) if factory cw2 < 1.9.0. Skip-flags must not allow pair migrate to proceed past a failed factory assert. Opposite of #514 (that one needed pairs first); do not copy that order.
  • Paginate pairs at limit: 30 using last page asset_infos as start_after. Never limit: 60 once. After the loop, migrated_count == GetPairCount.count. Fail the run on mismatch.
  • Do not Refresh onto an unlisted live id (pair rejects it). Batch skip procedure is operator-documented, not a contract change to make batch skip-on-error (all-or-nothing is intentional fail-closed).
  • verify-issue-582.sh green is not FoT-at-listing coverage. Pre-whitelist source audit (docs/runbooks/cw20-whitelist-policy.md + scripts/verify-cw20-code-ids.sh) remains the only control for FoT already behind a still-whitelisted template. Do not teach operators that F6 / this script substitutes for it.
  • Do not use RemoveWhitelistedCodeId(10184) as the default incident response (near-protocol-wide halt). Prefer SetPairPaused / BlacklistPair / BlacklistToken for single-market incidents.
  • Wasm admin key only. Script broadcasts wasm store / wasm migrate / factory execute as the contract admin / factory governance — same posture as #514/#518 (terrad-host.sh, DRY_RUN, UPGRADE582_LOCAL, SKIP_STORE). No eval of unfiltered .env.local (grep-allowlist Vite keys only, matching #518).
  • Do not bind-mount indexer/ into root Docker to run cargo (irrelevant here, but do not add a Docker cargo path to “verify” this). Host bash -n + DRY_RUN + LocalTerra rehearsal.
  • Frontend/indexer freeze visibility is P1, not this ticket. Script/runbook may note that quotes still succeed on frozen pairs (sim ungated).
  • Honest token upgrade path stays: whitelist new id → migrate instances → RefreshPairAssetCodeIds* → optional remove old id. Keep old id listed until Refresh finishes.

Relevant files

Path Role
scripts/upgrade-514-limit-discount.sh Pattern to follow and anti-pattern (order opposite; limit: 60 once; no GetPairCount)
scripts/upgrade-518-lp-symbol.sh Pattern: DRY_RUN, UPGRADE*_LOCAL, store/migrate/verify, terrad-host.sh
scripts/rotate-fee-treasury.sh Same silent limit: 60 enumerate; do not copy
scripts/lib/terrad-host.sh, terrad-tx-events.sh, lcd-smart-query.sh Broadcast + LCD smart query
scripts/add-ust1-secondary-pair.sh (contract_code_id) LCD GET /cosmwasm/wasm/v1/contract/{addr} → .contract_info.code_id
scripts/qa/verify-issue-582.sh Extend: must grep the new script + runbook + bash -n / DRY_RUN (keep pin tests)
scripts/qa/verify-issue-514.sh / verify-issue-518.sh Verify-script shape (bash -n + DRY_RUN + docs rg)
smartcontracts/packages/dex-common/src/pagination.rs MAX_LIMIT = 30, calc_limit clamps [1,30]
smartcontracts/packages/dex-common/src/factory.rs IsCodeIdWhitelisted, RefreshPairAssetCodeIds(Batch), GetPairCount, QueryMsg::Pairs
smartcontracts/packages/dex-common/src/pair.rs GetAssetCodeIds
smartcontracts/contracts/factory/src/contract.rs CreatePair query_wasm_contract_info; 1.9.0 handlers; batch refresh atomicity
smartcontracts/contracts/pair/src/asset_code_id_guard.rs Write-path gate + refresh refuse-unlisted
smartcontracts/contracts/pair/src/contract.rs Gate placement (incl. cancel/claim/withdraw); migrate backfill; GetAssetCodeIds hard-error on unmigrated
smartcontracts/tests/src/asset_code_id_pin_tests.rs Existing pin/FoT/refresh coverage (no ops-script tests)
skills/AGENTS_CW20_CODE_ID_PIN.md Operator sequence; add script + incident runbook links
docs/runbooks/cw20-code-id-ops.md, cw20-whitelist-policy.md Policy; exit-path + unfreeze sections belong here or a sibling runbook
docs/runbooks/wasm-admin-migration.md, launch-checklist.md, emergency-commands.md Cross-link #582 upgrade + pause-through-refresh
docs/runbooks/rebalance-mint-ust1-lp.md / scripts/rebalance-mint-ust1-lp.sh Pattern for private corrective re-seed at unfreeze
docs/contracts-terraclassic.md § Asset CW20 code_id pin Version migrate note (1.8.x → 1.9.0 before pair 1.15.0)
docs/contracts-security-audit.md invariant F6 Cross-link
audits/INTERNAL_KIMIK3_1787230030.md P0 list + Chain A
Makefile / AGENTS.md / docs/testing.md make verify-issue-* + new skill row
deployments/mainnet-soft-launch/deploy-trace.md Record probe + migrate txs

Live columbus-5 inventory (from #582, 2026-08-20): 14 pairs, 13 unique assets; UST1/USTR/cLUNC/cUSTC + CL8Y gems on 10184; PEARL/QUARTZ on 6036. LCD default in host scripts: https://terra-classic-lcd.publicnode.com.


1. scripts/upgrade-582-code-id-pin.sh

Mirror #514/#518 flags: DRY_RUN=1, UPGRADE582_LOCAL=1 (allowlisted VITE_FACTORY_ADDRESS / VITE_LCD_URL from frontend-dapp/.env.local only), UPGRADE582_SKIP_STORE=1 + explicit code ids, UPGRADE582_SKIP_PAIR_MIGRATE=1 (factory-only retry). Use set -euo pipefail.

Suggested steps (must be this order):

  1. Preflight
    • Require FACTORY_ADDRESS / LCD.
    • Columbus-5 (or target chain) ContractInfo probe: for factory + every listed pair asset (paginate pairs, unique CW20 addrs), GET {LCD}/cosmwasm/wasm/v1/contract/{addr} must return HTTP 2xx and a numeric .contract_info.code_id. Fail the script if any probe fails. Record LCD URL, chain-id, sample payload in the deploy trace. This is the 3.16 deploy gate.
    • Query factory cw2 (contract-state raw contract_info or equivalent). If already ≥1.9.0, skip factory migrate but still print the version.
  2. Store factory + pair optimized wasm (or reuse UPGRADE582_*_CODE_ID).
  3. Migrate factory to 1.9.0 wasm. Re-query cw2; abort unless version ≥ 1.9.0.
  4. Assert factory query live: {"is_code_id_whitelisted":{"code_id":10184}} (or first listed id) returns parseable CodeIdWhitelistedResponse. If this fails, do not migrate pairs.
  5. Paginated pair migrate to 1.15.0: loop {"pairs":{"start_after":...,"limit":30}} until a short page; migrate each contract_addr; migrated == GetPairCount.count or die. Per pair, if migrate tx fails, stop (do not continue and claim success).
  6. Optional paginated RefreshPairAssetCodeIdsBatch (default off for a clean upgrade — pins are backfilled from live ContractInfo at pair migrate, so refresh is a no-op unless something drifted mid-run). When enabled: loop until has_more=false; on batch revert, print the skip procedure (single RefreshPairAssetCodeIds for known-good, then start_after past the failing index) and exit non-zero with the failing index — do not swallow.
  7. Post-migrate smoke (required, not optional): for every pair: GetAssetCodeIds returns two u64s; each id IsCodeIdWhitelisted is true; one Simulation (tiny amount, either direction) returns a quote (queries are ungated — this checks the pair still answers, not that F6 would allow execute). Optionally query pair cw2 == 1.15.0. Fail the script on any miss. Print a table: pair, pin0, pin1, sim ok.

Idempotence: re-running after a full success must no-op or re-smoke only (SKIP_STORE + already-on-version). A partial run must be safely retryable without migrating factory twice into a broken state (ensure_from_older_version already rejects downgrade).

2. Columbus-5 probe in runbooks

Add a checklist item to launch-checklist.md (Phase 0 or a dedicated #582 F6 migrate subsection, same shape as the #518 LP-ticker BLOCK) and wasm-admin-migration.md step list: run the script's probe (or a tiny scripts/qa/probe-columbus5-contract-info.sh the upgrade script sources) before broadcasting pair 1.15.0. Paste output onto the deploy-trace / launch issue (#391).

3. Exit-path + unfreeze runbook

Extend docs/runbooks/cw20-code-id-ops.md (and skill) with an Incident: code-id drift / freeze section. Record an explicit policy decision (pick one, do not leave implicit):

  • (Keep — recommended default unless product overrides): maximal freeze (current code). Exits (cancel / claim / withdraw) stay gated. Document: issuer wasm-admin can hostage LP+escrow until governance SLA; users cannot self-rescue. Governance SLA target (e.g. pause within X, whitelist+refresh+unpause within Y).
  • (Change): open exits — that is a follow-up contract issue, not silently done here.

Unfreeze procedure (Chain A mitigation):

  1. Confirm drift: GetAssetCodeIds vs LCD ContractInfo.code_id per asset; IsCodeIdWhitelisted for live ids.
  2. SetPairPaused { paused: true } first (keep paused through refresh). Do not refresh while the pair is unpaused if external price moved.
  3. Source-review replacement wasm; AddWhitelistedCodeId if the new template is accepted (keep old id until done). If the new wasm is FoT/rebase → do not whitelist; use BlacklistToken / BlacklistPair; do not Refresh.
  4. RefreshPairAssetCodeIds (single) for the incident pair. For many pairs: batch with documented skip of the unrefreshable index (start_after past it; single refresh for neighbors).
  5. Corrective arb / re-seed while still paused (treasury/keeper; rebalance-mint-ust1-lp.sh pattern for hub pairs). Document that first-swap-after-unpause vs stale TWAP/reserves is otherwise extractable (~depth-limited).
  6. SetPairPaused { paused: false } only after rebalance/smoke. Refresh timing is MEV-sensitive — do not announce the unpause block in public chat before the txs land.
  7. Never lead with RemoveWhitelistedCodeId(10184).

Also document: GetAssetCodeIds hard-errors on pre-1.15.0 pairs (not null); CleanLimitBook still parks during freeze while claims stay gated (stranded parked escrow until unfreeze+claim); UpdateLimitOrderPrice is ungated (no funds).

4. Verification wiring

  • New scripts/qa/verify-issue-<this>.sh or extend verify-issue-582.sh so it cannot pass without the upgrade script existing, bash -n, a DRY_RUN factory-assert path (must refuse pair migrate when factory version is stubbed < 1.9.0), pagination/GetPairCount greps, and runbook strings (SetPairPaused, start_after, GetAssetCodeIds, ContractInfo).
  • make verify-issue-*, AGENTS.md playbook row, docs/testing.md table, skill AGENTS_CW20_CODE_ID_PIN.md operator sequence pointing at the script.

Acceptance criteria

  • scripts/upgrade-582-code-id-pin.sh exists, chmod +x, set -euo pipefail, uses terrad-host.sh / lcd-smart-query.sh.
  • Script refuses to broadcast any pair 1.15.0 migrate unless factory cw2 ≥ 1.9.0 and IsCodeIdWhitelisted smart-query succeeds. Covered by DRY_RUN or a LocalTerra negative test, not only a comment.
  • Pair enumerate pages at limit: 30 with start_after = last asset_infos; reconciles migrated count to GetPairCount.
  • Post-migrate smoke: every pair GetAssetCodeIds + one Simulation; script exits non-zero on failure.
  • Preflight probes GET /cosmwasm/wasm/v1/contract/{addr} for factory + every listed asset on the target LCD (columbus-5 for mainnet) and fails closed if code_id is missing.
  • Batch refresh skip-via-start_after is written in the runbook with a copy-pastable example (good pair, bad pair, start_after past bad index, has_more).
  • Exit-path policy is an explicit keep-vs-open decision in cw20-code-id-ops.md / cw20-whitelist-policy.md (not implied by gate placement).
  • Unfreeze runbook includes pause-through-refresh, private rebalance, MEV/timing warning, and “do not de-whitelist 10184 by default”.
  • launch-checklist.md + wasm-admin-migration.md + skill + AGENTS.md + docs/testing.md cross-link the script. #518-style BLOCK on economic listing / #581 until this migrate has run (not merely merged).
  • make verify-issue-582 (and/or this issue’s verify) greps the script + runbook and runs bash -n + DRY_RUN; cannot pass if the script is deleted.
  • Deploy-trace template / #391 comment fields listed for: LCD ContractInfo probe output, factory cw2 before/after, pair count, migrate tx hashes, smoke table.

Test plan (all paths)

Static / unit (no chain)

  1. bash -n scripts/upgrade-582-code-id-pin.sh.
  2. DRY_RUN=1 with dummy factory addr: prints order factory→pairs; does not require wasm if SKIP_STORE=1.
  3. DRY_RUN=1 with factory version stub / injected cw2 < 1.9.0: must exit non-zero before any pair migrate log. If DRY_RUN cannot simulate queries, add a UPGRADE582_FORCE_FACTORY_VERSION test hook used only by verify.
  4. Grep: no limit: 60 in the new script; has get_pair_count / GetPairCount / get_pair_count; has is_code_id_whitelisted; has get_asset_code_ids / GetAssetCodeIds; has /cosmwasm/wasm/v1/contract/.
  5. make verify-issue-582 (extended) twice (existing pin tests still pass).
  6. Docs rg: runbook contains SetPairPaused, RefreshPairAssetCodeIds, start_after, unfreeze, 10184 halt warning.

LocalTerra happy path (UPGRADE582_LOCAL=1 after make deploy-local)

  1. Factory starts 1.8.x / pair 1.14.x (current deploy). Run script for real (not DRY_RUN).
  2. After: factory cw2 1.9.0, every pair cw2 1.15.0, GetPairCount == number of migrated addrs == number of smoke rows.
  3. GetAssetCodeIds on a seeded pair matches LCD ContractInfo.code_id for both assets.
  4. IsCodeIdWhitelisted true for those ids.
  5. Simulation still returns a quote; an actual small swap succeeds (pins match).
  6. Re-run script: idempotent / skip already-migrated / smoke-only success.

Ordering / partial-failure paths (LocalTerra or mocked LCD)

  1. Pair-first forbidden: manually migrate one pair to 1.15.0 wasm before factory 1.9.0 (or run a broken copy). Confirm gated execute returns AssetCodeIdGuardUnavailable. Confirm the real script will not do this. Then factory-migrate recovers writes.
  2. Mid-loop pair migrate abort: kill after N pairs; retry; no double-count lie; remaining pairs get 1.15.0; GetPairCount still matches.
  3. Count mismatch: if a pair is created between page 1 and page 2, script must fail closed or re-page — document which.
  4. ContractInfo probe fail: point LCD at a URL that 404s /cosmwasm/wasm/v1/contract/{addr}; script exits before store/migrate.
  5. GetAssetCodeIds on unmigrated pair: smoke must treat query error as failure (L-05 hard-error shape), not as “empty pins ok”.
  6. Batch refresh + one unrefreshable pair: FoT-migrate one listed token off whitelist (existing pin test fixture); batch from start_after=null reverts; follow runbook skip; other pairs refresh; incident pair still frozen until policy (blacklist vs new whitelist).

Pagination correctness

  1. If LocalTerra has ≤30 pairs, add a unit/script test that mocks ≥31 pairs pages (two pages + short third) and asserts three queries and 31 migrates, or document a LocalTerra fixture that creates 31 pairs. Do not accept “we only have 14 on mainnet today”.

Docs / operator rehearsal

  1. Dry-run the unfreeze runbook against LocalTerra pause + refresh + unpause (make verify-issue-399 pause path + F6 refresh). Record that pause remains on through refresh.
  2. Confirm launch-checklist BLOCK language matches #518 style.

Test plan — attack, hack, and abuse vectors

Vector What to prove How
Ops mis-order (pair 1.15 before factory 1.9) Script cannot broadcast that sequence; if someone does it manually, writes freeze fail-closed (no theft) Test 13 + script guard
Silent tail skip (>30 pairs) 31st pair cannot remain on 1.14.0 after a “successful” run Test 19 + GetPairCount reconcile
Partial migrate claimed success Mid-loop crash ≠ exit 0; retry completes; mixed versions never reported OK Test 14–15, 17
Batch refresh grief (one bad pair blocks all) Operator can skip the bad index without changing fail-closed refresh Test 18 + runbook example
Issuer MsgMigrateContract to FoT / unlisted wasm Pair execute (incl. exits if policy=keep) reverts; refresh refuses unlisted; funds not stealable; Sweep still cannot take escrow Existing asset_code_id_pin_tests; runbook says do not Refresh onto FoT
Issuer freeze → ransom / LP hostage Documented SLA + pause/blacklist alternatives; 10184 de-whitelist not the default Runbook review; no script helper that removes 10184
Unfreeze arb / insider timing MEV Pause stays on through refresh; first public swap is after rebalance or accepted residual; no public “we unpause at height H” in the runbook Test 20; runbook wording
Same-tx migrate+swap Still impossible (tx atomicity) — do not regress #582 tests asset_code_id_pin_tests still in verify
Unauthorized Refresh Non-governance factory execute / non-factory pair RefreshAssetCodeIds still Unauthorized Existing tests; script only uses admin/gov key
Refresh onto attacker-chosen template Only if that id is already factory-whitelisted (source-reviewed). Script must not AddWhitelistedCodeId Guardrail; no whitelist mutation in the upgrade script
ContractInfo LCD lie / wrong LCD Probe uses operator-supplied LCD; wrong LCD fails probe or smoke (pins ≠ live). Do not proceed on empty code_id Test 16
.env.local eval injection Only allowlisted VITE_* keys, matching #518 Code review + grep
verify-issue-582 false-pass Deleting the upgrade script or runbook fails verify; green pin tests ≠ “FoT listing handled” Verify greps; runbook restates pre-whitelist audit as the only FoT-at-listing gate
Skip-flag abuse SKIP_PAIR_MIGRATE cannot be used to mark smoke OK for unmigrated pairs; SKIP_STORE still requires code ids Script tests
Governance key compromise using new levers Out of scope to fix (2-step transfer is P2); runbook must not add a one-click protocol halt 10184 warning only
Quote/execute divergence during freeze Smoke Simulation succeeding is not treated as “pair is tradable”. Runbook notes ungated queries Comment in smoke output + runbook

No public exploit PoC against columbus-5. LocalTerra / cw-multi-test only.


Verification criteria

Done when all of the following are true:

  1. make verify-issue-582 (extended) passes locally, including bash -n + DRY_RUN of upgrade-582-code-id-pin.sh and docs greps for the runbook/probe.
  2. LocalTerra rehearsal log attached (or #391 / this issue comment): factory 1.8→1.9 then pairs 1.14→1.15, GetPairCount match, per-pair GetAssetCodeIds + Simulation table, swap still works.
  3. Negative rehearsal: script abort when factory < 1.9.0 (log attached).
  4. Columbus-5 read-only probe executed against production LCD before any mainnet migrate: every currently listed asset ContractInfo.code_id recorded (expect 10184 / 6036 per #582 inventory). Output pasted here or on the deploy trace.
  5. Runbook sections exist and are linked from the skill, wasm-admin-migration.md, launch-checklist.md, and emergency-commands.md (pause-through-refresh pointer). Exit-path decision is a complete sentence, not “TBD”.
  6. Mainnet migrate itself is execution of this issue, not a separate unnamed Friday-night procedure: when operators run the script on columbus-5, they attach tx hashes + smoke table here / #391. Closing this issue before that run is allowed only if the script+docs+LocalTerra evidence are in main and launch-checklist BLOCK remains until the columbus-5 run completes (checkboxes above stay open for the live run, or a child comment tracks it).

Not done if: verify-issue-582 is still contracts-only greps; pairs are migrated with a one-shot limit: 60; factory version is a comment in a playbook; ContractInfo support is “LocalTerra passed”; unfreeze is “just Refresh”; or #581 is closed go because “F6 merged”.

## Gate **P0 — blocks columbus-5 rollout of factory 1.9.0 / pair 1.15.0 (invariant F6).** Do **not** treat merge of [#582](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/582) / [MR 389](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/389) as production readiness. Do **not** close [#581](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/581) with a **go** on 8266 / SpaceUSD listing until this issue is done **and** the migrate has actually run on the live factory. Source: internal audit `INTERNAL_KIMIK3_1787230030` P0 items 1–4 (findings 3.1, 3.2, 3.5, 3.7, 3.8, 3.16, F389-06, Chain A). Same ops class as prior-audit F-01/F-02 on [`scripts/upgrade-514-limit-discount.sh`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/scripts/upgrade-514-limit-discount.sh) ([#514](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/514)) — documented order, no script enforcement, silent `limit: 60` truncation. This issue **bundles** the three P0 rollout controls (do not split): 1. Order-enforcing `upgrade-582` script with paginated migrate + post-migrate smoke. 2. Columbus-5 `WasmQuery::ContractInfo` / LCD `GET /cosmwasm/wasm/v1/contract/{addr}` pre-flight probe. 3. Documented exit-path policy + freeze/unfreeze incident runbook (incl. unfreeze-arb mitigation and batch-refresh skip). Frontend humanization / indexer frozen-pair exclusion are **P1** (before #581 relies on F6) and **out of scope** here — file separately if not already open. --- ## Current codebase ### What F6 already does (on `fix/582-cw20-code-id-pin`, not yet on `main`) Factory **1.9.0** adds `IsCodeIdWhitelisted` and governance `RefreshPairAssetCodeIds` / `RefreshPairAssetCodeIdsBatch`. Pair **1.15.0** snapshots each listed asset's live `ContractInfo.code_id` into `ASSET_CODE_IDS` at instantiate (and backfills on migrate), then **every fund-moving write path** (swap, provide, withdraw, limit place/fill, **cancel**, **claim**) aborts unless live id **equals the pin** **and** the factory whitelist still contains it. Fail-closed on query errors (`AssetCodeIdGuardUnavailable`). Refresh refuses unlisted live ids. Same-tx `MsgMigrateContract` + swap is impossible (live query + tx atomicity). `main` today is still factory **1.8.0** / pair **1.14.0** — no pin, no write-path re-check. ### What is missing (ops only — docs exist, nothing executes them) | Gap | Evidence | |-----|----------| | No `scripts/upgrade-582-*.sh` | Prior upgrades: [`upgrade-514-limit-discount.sh`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/scripts/upgrade-514-limit-discount.sh), [`upgrade-518-lp-symbol.sh`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/scripts/upgrade-518-lp-symbol.sh). F6 order is playbook-only (`skills/AGENTS_CW20_CODE_ID_PIN.md` step 5: factory 1.9.0 **before** pair 1.15.0). | | Pair-first migrate = total write freeze | Pair 1.15.0 queries factory `IsCodeIdWhitelisted`. Against factory ≤1.8.0 that smart query fails (`unknown variant`) → `AssetCodeIdGuardUnavailable` on **every** gated write on **every** upgraded pair until factory migrates. Fail-closed (no fund loss) but protocol liveness freeze. Nothing asserts factory cw2 ≥1.9.0 before broadcasting pair migrates. | | Pagination / count reconcile not encoded | Factory `query_pairs` clamps `limit` via `calc_limit` to `MAX_LIMIT = 30` (`dex-common/src/pagination.rs`). `PairsResponse` has no `has_more`. `upgrade-514` and `rotate-fee-treasury.sh` still query `limit: 60` **once** and migrate that page — tail silently skipped (prior F-02). Live factory currently has **14** pairs (fits one page today; will not stay true). `GetPairCount` exists and is unused by those scripts' migrate loops. | | Batch refresh all-or-nothing; skip undocumented | `RefreshPairAssetCodeIdsBatch` sends one `WasmMsg::Execute` per pair in a **single tx** (factory `execute_refresh_pair_asset_code_ids_batch`). The incident pair whose token migrated to an **unlisted** id **must** fail refresh (pair refuses unlisted live ids) → whole batch reverts, blocking later-indexed pairs. Workaround (single refresh for good pairs, then batch with `start_after` past the bad index) is verified correct in code (`start_idx = start_after+1`, `calc_limit` clamps `[1,30]`) but **not in any runbook**. | | No post-migrate smoke | `verify-issue-582.sh` is contracts + docs greps only (pin suite ×2, version strings). No on-chain rung: no `GetAssetCodeIds`, no `Simulation`, no cw2 version assert, no `GetPairCount` reconcile. A green check does **not** mean the migrate ran or ran in order. | | Columbus-5 `ContractInfo` assumed, not probed | Pair migrate backfill and every gated write call `query_wasm_contract_info`. Factory `CreatePair` has used the same query since launch (`factory/src/contract.rs` ~220–231) and LocalTerra passed, but a columbus-5 LCD/chain miss would abort pair migrate (pair stuck on 1.14.0 — finding 3.9) or freeze writes. `GET /cosmwasm/wasm/v1/contract/{addr}` is used off-chain in [`add-ust1-secondary-pair.sh`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/scripts/add-ust1-secondary-pair.sh) (`contract_info.code_id`) and [`handoff-cl8ydeploy-to-multisig.sh`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/scripts/handoff-cl8ydeploy-to-multisig.sh) (admin). **No F6 pre-flight** records that this endpoint returns `code_id` for **every listed asset** on the target LCD before migrate. | | Exit-path freeze is implicit | `gate_asset_code_ids` also covers `CancelLimitOrder(s)`, `ClaimExpiredLimitOrder(s)`, and `WithdrawLiquidity`. A third-party token wasm admin (the actor F6 defends against) can freeze **all** user value in a pair (LP underlying, maker escrow, parked claims) until governance whitelists + refreshes. Accounting: exits could stay open under FoT (recipient bears outbound tax; pair-side `PENDING_ESCROW`/`RESERVES` stay consistent). Maximal freeze is a conservative choice, currently **undocumented as a tradeoff**, **untested** for cancel/claim/withdraw, and has **no governance SLA**. | | Unfreeze is an arb | During freeze, `Simulation`/`HybridSimulation` stay ungated and TWAP/`OBSERVATIONS` go stale (no swap → no observation write). First swap after refresh executes against stale reserves/TWAP (~5% × frozen TVL extractable, depth-limited). `SetPairPaused` through refresh + private corrective rebalance is **not** in [`emergency-commands.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/docs/runbooks/emergency-commands.md) or [`cw20-whitelist-policy.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/docs/runbooks/cw20-whitelist-policy.md). Governance timing of refresh is MEV-sensitive. | | 10184 de-whitelist is a protocol halt | 13 of 14 live assets share mintable code **10184**. `RemoveWhitelistedCodeId(10184)` freezes essentially every pair. Milder controls already exist: `SetPairPaused` / `BlacklistPair` / `BlacklistToken`. Incident runbook must say so. | Playbooks that **describe** F6 but do not enforce it: [`skills/AGENTS_CW20_CODE_ID_PIN.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/fix/582-cw20-code-id-pin/skills/AGENTS_CW20_CODE_ID_PIN.md), [`docs/runbooks/cw20-code-id-ops.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/fix/582-cw20-code-id-pin/docs/runbooks/cw20-code-id-ops.md), [`docs/runbooks/wasm-admin-migration.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/docs/runbooks/wasm-admin-migration.md) (lists #518 only), [`docs/runbooks/launch-checklist.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/docs/runbooks/launch-checklist.md). --- ## Why this is needed Without a factory-first, count-reconciled upgrade script, an operator following the #514/#518 pattern (or a partial/retried run) can migrate pairs before factory 1.9.0 and freeze **all** gated writes on those pairs until a follow-up factory migrate. Without post-migrate smoke, a silent skip of the 31st pair leaves that pair on 1.14.0 (no pin; F6 hole remains for that market). Without a columbus-5 `ContractInfo` probe, pair migrate can abort mid-loop and leave a mixed version set. Without an exit-path + unfreeze runbook, the first real issuer-migrate freeze (permissionless 6036 listing, or 8266 SpaceUSD after #581) strands LP/escrow with no SLA, and the unfreeze hands stale-price arb to whoever is watching the mempool — including insiders who know refresh timing. This is the production-readiness gap the #582 contract MR explicitly does **not** close. --- ## Constraints / guardrails - **No new fund-moving contract logic in this issue.** F6 on-chain behavior stays as merged in #582. Do **not** add FoT / balance-delta swap math (H-01). Do **not** silently un-gate exits in the same MR as the ops script unless the **documented policy decision** is (a) open exits — that is a separate, explicit contract change with its own tests. - **Factory 1.9.0 before any pair 1.15.0 migrate.** Script must **abort** (non-zero) if factory cw2 `< 1.9.0`. Skip-flags must not allow pair migrate to proceed past a failed factory assert. Opposite of #514 (that one needed **pairs first**); do not copy that order. - **Paginate `pairs` at `limit: 30`** using last page `asset_infos` as `start_after`. **Never** `limit: 60` once. After the loop, `migrated_count == GetPairCount.count`. Fail the run on mismatch. - **Do not Refresh onto an unlisted live id** (pair rejects it). Batch skip procedure is operator-documented, not a contract change to make batch skip-on-error (all-or-nothing is intentional fail-closed). - **`verify-issue-582.sh` green is not FoT-at-listing coverage.** Pre-whitelist source audit ([`docs/runbooks/cw20-whitelist-policy.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/docs/runbooks/cw20-whitelist-policy.md) + [`scripts/verify-cw20-code-ids.sh`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/scripts/verify-cw20-code-ids.sh)) remains the **only** control for FoT already behind a still-whitelisted template. Do not teach operators that F6 / this script substitutes for it. - **Do not use `RemoveWhitelistedCodeId(10184)` as the default incident response** (near-protocol-wide halt). Prefer `SetPairPaused` / `BlacklistPair` / `BlacklistToken` for single-market incidents. - **Wasm admin key only.** Script broadcasts `wasm store` / `wasm migrate` / factory execute as the contract admin / factory governance — same posture as #514/#518 (`terrad-host.sh`, `DRY_RUN`, `UPGRADE582_LOCAL`, `SKIP_STORE`). No `eval` of unfiltered `.env.local` (grep-allowlist Vite keys only, matching #518). - **Do not bind-mount `indexer/` into root Docker to run cargo** (irrelevant here, but do not add a Docker cargo path to “verify” this). Host `bash -n` + `DRY_RUN` + LocalTerra rehearsal. - **Frontend/indexer freeze visibility is P1**, not this ticket. Script/runbook may note that quotes still succeed on frozen pairs (sim ungated). - **Honest token upgrade path stays:** whitelist new id → migrate instances → `RefreshPairAssetCodeIds*` → optional remove old id. Keep old id listed until Refresh finishes. --- ## Relevant files | Path | Role | |------|------| | `scripts/upgrade-514-limit-discount.sh` | Pattern to follow **and** anti-pattern (order opposite; `limit: 60` once; no `GetPairCount`) | | `scripts/upgrade-518-lp-symbol.sh` | Pattern: `DRY_RUN`, `UPGRADE*_LOCAL`, store/migrate/verify, `terrad-host.sh` | | `scripts/rotate-fee-treasury.sh` | Same silent `limit: 60` enumerate; do not copy | | `scripts/lib/terrad-host.sh`, `terrad-tx-events.sh`, `lcd-smart-query.sh` | Broadcast + LCD smart query | | `scripts/add-ust1-secondary-pair.sh` (`contract_code_id`) | LCD `GET /cosmwasm/wasm/v1/contract/{addr}` → `.contract_info.code_id` | | `scripts/qa/verify-issue-582.sh` | Extend: must grep the new script + runbook + `bash -n` / `DRY_RUN` (keep pin tests) | | `scripts/qa/verify-issue-514.sh` / `verify-issue-518.sh` | Verify-script shape (`bash -n` + `DRY_RUN` + docs rg) | | `smartcontracts/packages/dex-common/src/pagination.rs` | `MAX_LIMIT = 30`, `calc_limit` clamps `[1,30]` | | `smartcontracts/packages/dex-common/src/factory.rs` | `IsCodeIdWhitelisted`, `RefreshPairAssetCodeIds(Batch)`, `GetPairCount`, `QueryMsg::Pairs` | | `smartcontracts/packages/dex-common/src/pair.rs` | `GetAssetCodeIds` | | `smartcontracts/contracts/factory/src/contract.rs` | CreatePair `query_wasm_contract_info`; 1.9.0 handlers; batch refresh atomicity | | `smartcontracts/contracts/pair/src/asset_code_id_guard.rs` | Write-path gate + refresh refuse-unlisted | | `smartcontracts/contracts/pair/src/contract.rs` | Gate placement (incl. cancel/claim/withdraw); migrate backfill; `GetAssetCodeIds` hard-error on unmigrated | | `smartcontracts/tests/src/asset_code_id_pin_tests.rs` | Existing pin/FoT/refresh coverage (no ops-script tests) | | `skills/AGENTS_CW20_CODE_ID_PIN.md` | Operator sequence; add script + incident runbook links | | `docs/runbooks/cw20-code-id-ops.md`, `cw20-whitelist-policy.md` | Policy; exit-path + unfreeze sections belong here or a sibling runbook | | `docs/runbooks/wasm-admin-migration.md`, `launch-checklist.md`, `emergency-commands.md` | Cross-link #582 upgrade + pause-through-refresh | | `docs/runbooks/rebalance-mint-ust1-lp.md` / `scripts/rebalance-mint-ust1-lp.sh` | Pattern for private corrective re-seed at unfreeze | | `docs/contracts-terraclassic.md` § Asset CW20 code_id pin | Version migrate note (1.8.x → 1.9.0 before pair 1.15.0) | | `docs/contracts-security-audit.md` invariant **F6** | Cross-link | | `audits/INTERNAL_KIMIK3_1787230030.md` | P0 list + Chain A | | `Makefile` / `AGENTS.md` / `docs/testing.md` | `make verify-issue-*` + new skill row | | `deployments/mainnet-soft-launch/deploy-trace.md` | Record probe + migrate txs | Live columbus-5 inventory (from #582, 2026-08-20): **14** pairs, **13** unique assets; UST1/USTR/cLUNC/cUSTC + CL8Y gems on **10184**; PEARL/QUARTZ on **6036**. LCD default in host scripts: `https://terra-classic-lcd.publicnode.com`. --- ## Recommended direction ### 1. `scripts/upgrade-582-code-id-pin.sh` Mirror #514/#518 flags: `DRY_RUN=1`, `UPGRADE582_LOCAL=1` (allowlisted `VITE_FACTORY_ADDRESS` / `VITE_LCD_URL` from `frontend-dapp/.env.local` only), `UPGRADE582_SKIP_STORE=1` + explicit code ids, `UPGRADE582_SKIP_PAIR_MIGRATE=1` (factory-only retry). Use `set -euo pipefail`. Suggested steps (must be this order): 1. **Preflight** - Require `FACTORY_ADDRESS` / LCD. - **Columbus-5 (or target chain) `ContractInfo` probe:** for factory + every listed pair asset (paginate pairs, unique CW20 addrs), `GET {LCD}/cosmwasm/wasm/v1/contract/{addr}` must return HTTP 2xx and a numeric `.contract_info.code_id`. Fail the script if any probe fails. Record LCD URL, chain-id, sample payload in the deploy trace. This is the 3.16 deploy gate. - Query factory cw2 (`contract-state raw` `contract_info` or equivalent). If already ≥1.9.0, skip factory migrate but still print the version. 2. **Store** factory + pair optimized wasm (or reuse `UPGRADE582_*_CODE_ID`). 3. **Migrate factory** to 1.9.0 wasm. Re-query cw2; **abort unless version ≥ 1.9.0**. 4. **Assert factory query live:** `{"is_code_id_whitelisted":{"code_id":10184}}` (or first listed id) returns parseable `CodeIdWhitelistedResponse`. If this fails, **do not** migrate pairs. 5. **Paginated pair migrate** to 1.15.0: loop `{"pairs":{"start_after":...,"limit":30}}` until a short page; migrate each `contract_addr`; `migrated == GetPairCount.count` or die. Per pair, if migrate tx fails, stop (do not continue and claim success). 6. **Optional paginated `RefreshPairAssetCodeIdsBatch`** (default off for a clean upgrade — pins are backfilled from live `ContractInfo` at pair migrate, so refresh is a no-op unless something drifted mid-run). When enabled: loop until `has_more=false`; on batch revert, print the skip procedure (single `RefreshPairAssetCodeIds` for known-good, then `start_after` past the failing index) and exit non-zero with the failing index — do not swallow. 7. **Post-migrate smoke (required, not optional):** for **every** pair: `GetAssetCodeIds` returns two u64s; each id `IsCodeIdWhitelisted` is true; one `Simulation` (tiny amount, either direction) returns a quote (queries are ungated — this checks the pair still answers, not that F6 would allow execute). Optionally query pair cw2 == 1.15.0. Fail the script on any miss. Print a table: pair, pin0, pin1, sim ok. Idempotence: re-running after a full success must no-op or re-smoke only (`SKIP_STORE` + already-on-version). A partial run must be safely retryable without migrating factory twice into a broken state (`ensure_from_older_version` already rejects downgrade). ### 2. Columbus-5 probe in runbooks Add a checklist item to `launch-checklist.md` (Phase 0 or a dedicated **#582 F6 migrate** subsection, same shape as the #518 LP-ticker BLOCK) and `wasm-admin-migration.md` step list: run the script's probe (or a tiny `scripts/qa/probe-columbus5-contract-info.sh` the upgrade script sources) **before** broadcasting pair 1.15.0. Paste output onto the deploy-trace / launch issue (#391). ### 3. Exit-path + unfreeze runbook Extend `docs/runbooks/cw20-code-id-ops.md` (and skill) with an **Incident: code-id drift / freeze** section. Record an explicit policy decision (pick one, do not leave implicit): - **(Keep — recommended default unless product overrides):** maximal freeze (current code). Exits (cancel / claim / withdraw) stay gated. Document: issuer wasm-admin can hostage LP+escrow until governance SLA; users cannot self-rescue. Governance SLA target (e.g. pause within X, whitelist+refresh+unpause within Y). - **(Change):** open exits — that is a **follow-up contract issue**, not silently done here. Unfreeze procedure (Chain A mitigation): 1. Confirm drift: `GetAssetCodeIds` vs LCD `ContractInfo.code_id` per asset; `IsCodeIdWhitelisted` for live ids. 2. **`SetPairPaused { paused: true }` first** (keep paused through refresh). Do **not** refresh while the pair is unpaused if external price moved. 3. Source-review replacement wasm; `AddWhitelistedCodeId` if the new template is accepted (keep old id until done). If the new wasm is FoT/rebase → **do not whitelist**; use `BlacklistToken` / `BlacklistPair`; do **not** Refresh. 4. `RefreshPairAssetCodeIds` (single) for the incident pair. For many pairs: batch with **documented skip** of the unrefreshable index (`start_after` past it; single refresh for neighbors). 5. **Corrective arb / re-seed while still paused** (treasury/keeper; `rebalance-mint-ust1-lp.sh` pattern for hub pairs). Document that first-swap-after-unpause vs stale TWAP/reserves is otherwise extractable (~depth-limited). 6. `SetPairPaused { paused: false }` only after rebalance/smoke. Refresh timing is MEV-sensitive — do not announce the unpause block in public chat before the txs land. 7. Never lead with `RemoveWhitelistedCodeId(10184)`. Also document: `GetAssetCodeIds` **hard-errors** on pre-1.15.0 pairs (not `null`); `CleanLimitBook` still parks during freeze while claims stay gated (stranded parked escrow until unfreeze+claim); `UpdateLimitOrderPrice` is ungated (no funds). ### 4. Verification wiring - New `scripts/qa/verify-issue-<this>.sh` **or** extend `verify-issue-582.sh` so it cannot pass without the upgrade script existing, `bash -n`, a `DRY_RUN` factory-assert path (must refuse pair migrate when factory version is stubbed `< 1.9.0`), pagination/`GetPairCount` greps, and runbook strings (`SetPairPaused`, `start_after`, `GetAssetCodeIds`, `ContractInfo`). - `make verify-issue-*`, `AGENTS.md` playbook row, `docs/testing.md` table, skill `AGENTS_CW20_CODE_ID_PIN.md` operator sequence pointing at the script. --- ## Acceptance criteria - [ ] `scripts/upgrade-582-code-id-pin.sh` exists, `chmod +x`, `set -euo pipefail`, uses `terrad-host.sh` / `lcd-smart-query.sh`. - [ ] Script **refuses** to broadcast any pair 1.15.0 migrate unless factory cw2 ≥ 1.9.0 **and** `IsCodeIdWhitelisted` smart-query succeeds. Covered by `DRY_RUN` or a LocalTerra negative test, not only a comment. - [ ] Pair enumerate pages at `limit: 30` with `start_after` = last `asset_infos`; **reconciles** migrated count to `GetPairCount`. - [ ] Post-migrate smoke: every pair `GetAssetCodeIds` + one `Simulation`; script exits non-zero on failure. - [ ] Preflight probes `GET /cosmwasm/wasm/v1/contract/{addr}` for factory + every listed asset on the **target** LCD (columbus-5 for mainnet) and fails closed if `code_id` is missing. - [ ] Batch refresh skip-via-`start_after` is written in the runbook with a copy-pastable example (good pair, bad pair, `start_after` past bad index, `has_more`). - [ ] Exit-path policy is an explicit keep-vs-open decision in `cw20-code-id-ops.md` / `cw20-whitelist-policy.md` (not implied by gate placement). - [ ] Unfreeze runbook includes pause-through-refresh, private rebalance, MEV/timing warning, and “do not de-whitelist 10184 by default”. - [ ] `launch-checklist.md` + `wasm-admin-migration.md` + skill + `AGENTS.md` + `docs/testing.md` cross-link the script. #518-style **BLOCK** on economic listing / #581 until this migrate has **run** (not merely merged). - [ ] `make verify-issue-582` (and/or this issue’s verify) greps the script + runbook and runs `bash -n` + `DRY_RUN`; **cannot** pass if the script is deleted. - [ ] Deploy-trace template / #391 comment fields listed for: LCD ContractInfo probe output, factory cw2 before/after, pair count, migrate tx hashes, smoke table. --- ## Test plan (all paths) ### Static / unit (no chain) 1. `bash -n scripts/upgrade-582-code-id-pin.sh`. 2. `DRY_RUN=1` with dummy factory addr: prints order factory→pairs; does not require wasm if `SKIP_STORE=1`. 3. `DRY_RUN=1` with factory version stub / injected cw2 `< 1.9.0`: **must exit non-zero before any pair migrate log**. If DRY_RUN cannot simulate queries, add a `UPGRADE582_FORCE_FACTORY_VERSION` test hook used only by verify. 4. Grep: no `limit: 60` in the new script; has `get_pair_count` / `GetPairCount` / `get_pair_count`; has `is_code_id_whitelisted`; has `get_asset_code_ids` / `GetAssetCodeIds`; has `/cosmwasm/wasm/v1/contract/`. 5. `make verify-issue-582` (extended) twice (existing pin tests still pass). 6. Docs rg: runbook contains `SetPairPaused`, `RefreshPairAssetCodeIds`, `start_after`, unfreeze, 10184 halt warning. ### LocalTerra happy path (`UPGRADE582_LOCAL=1` after `make deploy-local`) 7. Factory starts 1.8.x / pair 1.14.x (current deploy). Run script for real (not DRY_RUN). 8. After: factory cw2 `1.9.0`, every pair cw2 `1.15.0`, `GetPairCount` == number of migrated addrs == number of smoke rows. 9. `GetAssetCodeIds` on a seeded pair matches LCD `ContractInfo.code_id` for both assets. 10. `IsCodeIdWhitelisted` true for those ids. 11. `Simulation` still returns a quote; an actual small swap **succeeds** (pins match). 12. Re-run script: idempotent / skip already-migrated / smoke-only success. ### Ordering / partial-failure paths (LocalTerra or mocked LCD) 13. **Pair-first forbidden:** manually migrate one pair to 1.15.0 wasm **before** factory 1.9.0 (or run a broken copy). Confirm gated execute returns `AssetCodeIdGuardUnavailable`. Confirm the **real** script will not do this. Then factory-migrate recovers writes. 14. **Mid-loop pair migrate abort:** kill after N pairs; retry; no double-count lie; remaining pairs get 1.15.0; `GetPairCount` still matches. 15. **Count mismatch:** if a pair is created between page 1 and page 2, script must fail closed or re-page — document which. 16. **ContractInfo probe fail:** point LCD at a URL that 404s `/cosmwasm/wasm/v1/contract/{addr}`; script exits before store/migrate. 17. **`GetAssetCodeIds` on unmigrated pair:** smoke must treat query error as failure (L-05 hard-error shape), not as “empty pins ok”. 18. **Batch refresh + one unrefreshable pair:** FoT-migrate one listed token off whitelist (existing pin test fixture); batch from `start_after=null` reverts; follow runbook skip; other pairs refresh; incident pair still frozen until policy (blacklist vs new whitelist). ### Pagination correctness 19. If LocalTerra has ≤30 pairs, add a **unit/script test** that mocks ≥31 `pairs` pages (two pages + short third) and asserts three queries and 31 migrates, **or** document a LocalTerra fixture that creates 31 pairs. Do not accept “we only have 14 on mainnet today”. ### Docs / operator rehearsal 20. Dry-run the unfreeze runbook against LocalTerra pause + refresh + unpause (`make verify-issue-399` pause path + F6 refresh). Record that pause remains on through refresh. 21. Confirm `launch-checklist` BLOCK language matches #518 style. --- ## Test plan — attack, hack, and abuse vectors | Vector | What to prove | How | |--------|----------------|-----| | **Ops mis-order (pair 1.15 before factory 1.9)** | Script cannot broadcast that sequence; if someone does it manually, writes freeze fail-closed (no theft) | Test 13 + script guard | | **Silent tail skip (>30 pairs)** | 31st pair cannot remain on 1.14.0 after a “successful” run | Test 19 + `GetPairCount` reconcile | | **Partial migrate claimed success** | Mid-loop crash ≠ exit 0; retry completes; mixed versions never reported OK | Test 14–15, 17 | | **Batch refresh grief (one bad pair blocks all)** | Operator can skip the bad index without changing fail-closed refresh | Test 18 + runbook example | | **Issuer `MsgMigrateContract` to FoT / unlisted wasm** | Pair execute (incl. **exits** if policy=keep) reverts; refresh refuses unlisted; funds not stealable; Sweep still cannot take escrow | Existing `asset_code_id_pin_tests`; runbook says do not Refresh onto FoT | | **Issuer freeze → ransom / LP hostage** | Documented SLA + pause/blacklist alternatives; 10184 de-whitelist **not** the default | Runbook review; no script helper that removes 10184 | | **Unfreeze arb / insider timing MEV** | Pause stays on through refresh; first public swap is after rebalance or accepted residual; no public “we unpause at height H” in the runbook | Test 20; runbook wording | | **Same-tx migrate+swap** | Still impossible (tx atomicity) — do not regress #582 tests | `asset_code_id_pin_tests` still in verify | | **Unauthorized Refresh** | Non-governance factory execute / non-factory pair `RefreshAssetCodeIds` still `Unauthorized` | Existing tests; script only uses admin/gov key | | **Refresh onto attacker-chosen template** | Only if that id is already factory-whitelisted (source-reviewed). Script must not `AddWhitelistedCodeId` | Guardrail; no whitelist mutation in the upgrade script | | **ContractInfo LCD lie / wrong LCD** | Probe uses operator-supplied LCD; wrong LCD fails probe or smoke (pins ≠ live). Do not proceed on empty `code_id` | Test 16 | | **`.env.local` `eval` injection** | Only allowlisted `VITE_*` keys, matching #518 | Code review + grep | | **`verify-issue-582` false-pass** | Deleting the upgrade script or runbook fails verify; green pin tests ≠ “FoT listing handled” | Verify greps; runbook restates pre-whitelist audit as the only FoT-at-listing gate | | **Skip-flag abuse** | `SKIP_PAIR_MIGRATE` cannot be used to mark smoke OK for unmigrated pairs; `SKIP_STORE` still requires code ids | Script tests | | **Governance key compromise using new levers** | Out of scope to fix (2-step transfer is P2); runbook must not add a one-click protocol halt | 10184 warning only | | **Quote/execute divergence during freeze** | Smoke `Simulation` succeeding is **not** treated as “pair is tradable”. Runbook notes ungated queries | Comment in smoke output + runbook | No public exploit PoC against columbus-5. LocalTerra / `cw-multi-test` only. --- ## Verification criteria **Done when all of the following are true:** 1. `make verify-issue-582` (extended) passes locally, including `bash -n` + `DRY_RUN` of `upgrade-582-code-id-pin.sh` and docs greps for the runbook/probe. 2. LocalTerra rehearsal log attached (or `#391` / this issue comment): factory 1.8→1.9 then pairs 1.14→1.15, `GetPairCount` match, per-pair `GetAssetCodeIds` + `Simulation` table, swap still works. 3. Negative rehearsal: script abort when factory `< 1.9.0` (log attached). 4. Columbus-5 **read-only** probe executed against production LCD **before** any mainnet migrate: every currently listed asset `ContractInfo.code_id` recorded (expect 10184 / 6036 per #582 inventory). Output pasted here or on the deploy trace. 5. Runbook sections exist and are linked from the skill, `wasm-admin-migration.md`, `launch-checklist.md`, and `emergency-commands.md` (pause-through-refresh pointer). Exit-path decision is a complete sentence, not “TBD”. 6. Mainnet migrate itself is **execution of this issue**, not a separate unnamed Friday-night procedure: when operators run the script on columbus-5, they attach tx hashes + smoke table here / #391. Closing this issue **before** that run is allowed only if the script+docs+LocalTerra evidence are in `main` **and** launch-checklist BLOCK remains until the columbus-5 run completes (checkboxes above stay open for the live run, or a child comment tracks it). **Not done if:** `verify-issue-582` is still contracts-only greps; pairs are migrated with a one-shot `limit: 60`; factory version is a comment in a playbook; ContractInfo support is “LocalTerra passed”; unfreeze is “just Refresh”; or #581 is closed go because “F6 merged”.
PlasticDigits commented 2026-08-20 13:31:31 +00:00 (Migrated from gitlab.com)

marked as related to #582

marked as related to #582
PlasticDigits commented 2026-08-20 13:31:32 +00:00 (Migrated from gitlab.com)

marked as related to #581

marked as related to #581
PlasticDigits commented 2026-08-20 13:31:34 +00:00 (Migrated from gitlab.com)

marked as related to #514

marked as related to #514
PlasticDigits commented 2026-08-20 14:42:40 +00:00 (Migrated from gitlab.com)

mentioned in commit b2b8b52af2

mentioned in commit b2b8b52af255d716d855b32062c4ca7c8d68d73c
PlasticDigits commented 2026-08-20 14:43:29 +00:00 (Migrated from gitlab.com)

mentioned in merge request !1108

mentioned in merge request !1108
PlasticDigits commented 2026-08-20 14:44:14 +00:00 (Migrated from gitlab.com)

Implementation MR: !1108 (stacked on !1107 / fix/582-cw20-code-id-pin).

Static verify passed (make verify-issue-584 ×2, make verify-issue-582 ×2 including pin tests). Columbus-5 read-only ContractInfo probe (no migrate):

  • LCD: https://terra-classic-lcd.publicnode.com
  • Factory terra1ejpgvv7g3hj0u6fpcnxhflqp84g0w3cnaskqkg5733ygwlmf963sfchsea code_id 11585
  • Enumerated 14 pairs = GetPairCount 14
  • Listed assets: 10184 (mintable hub/CL8Y) and 6036 (PEARL/QUARTZ)

Still open on this issue: LocalTerra UPGRADE582_LOCAL=1 rehearsal log, and the live columbus-5 migrate (tx hashes + smoke table). Launch-checklist BLOCK stays until that run.

Implementation MR: !1108 (stacked on !1107 / `fix/582-cw20-code-id-pin`). Static verify passed (`make verify-issue-584` ×2, `make verify-issue-582` ×2 including pin tests). Columbus-5 **read-only** ContractInfo probe (no migrate): - LCD: `https://terra-classic-lcd.publicnode.com` - Factory `terra1ejpgvv7g3hj0u6fpcnxhflqp84g0w3cnaskqkg5733ygwlmf963sfchsea` code_id **11585** - Enumerated **14** pairs = `GetPairCount` 14 - Listed assets: **10184** (mintable hub/CL8Y) and **6036** (PEARL/QUARTZ) Still open on this issue: LocalTerra `UPGRADE582_LOCAL=1` rehearsal log, and the live columbus-5 migrate (tx hashes + smoke table). Launch-checklist **BLOCK** stays until that run.
PlasticDigits commented 2026-08-21 00:16:14 +00:00 (Migrated from gitlab.com)

mentioned in issue #582

mentioned in issue #582
PlasticDigits commented 2026-08-21 00:16:15 +00:00 (Migrated from gitlab.com)

Merge of !1108 (2026-08-21)

!1108 is merged to main (70e79508) after:

  1. GitLab source 584-ops-p0-… had zero unique commits (SHA matched !1107). Real work was on fix/584-upgrade-582-ops @ 363b17f9.
  2. That SHA was pushed onto the MR source branch and the MR was retargeted main (was stacked on fix/582-cw20-code-id-pin, which !1107 deleted).
  3. Conflicts were GitLab source==target, not file conflicts. git merge-tree --write-tree vs main was clean.

Local sanity: make verify-issue-584 12/12, make verify-issue-582 5/5 (pin tests + ops greps). CI was not waited on.

Acceptance vs this merge

Script + runbook + verify wiring are on main. Remaining execution checkboxes stay open:

  • LocalTerra UPGRADE582_LOCAL=1 after make deploy-local (happy path + factory-first abort)
  • Columbus-5 live migrate (tx hashes + smoke table here / #391) — launch BLOCK until RUN
  • Live LCD re-inventory of listed-asset admins (issue #582 table is 2026-08-20)

PARTIAL (non-blocking): IsCodeIdWhitelisted is enforced on the live path, but DRY_RUN skips that query when factory cw2 is already ≥1.9.0. Factory-version refuse is covered (UPGRADE582_FORCE_FACTORY_VERSION=1.8.0). Optional UPGRADE582_REFRESH=1 batch loop does not parse wasm has_more (default off; runbook has the skip procedure).

Do not close this issue on merge. Do not close #581 go on 8266 because F6 code landed.

## Merge of !1108 (2026-08-21) !1108 is **merged** to `main` (`70e79508`) after: 1. GitLab source `584-ops-p0-…` had **zero unique commits** (SHA matched !1107). Real work was on `fix/584-upgrade-582-ops` @ `363b17f9`. 2. That SHA was pushed onto the MR source branch and the MR was retargeted **main** (was stacked on `fix/582-cw20-code-id-pin`, which !1107 deleted). 3. Conflicts were GitLab `source==target`, not file conflicts. `git merge-tree --write-tree` vs `main` was clean. Local sanity: `make verify-issue-584` **12/12**, `make verify-issue-582` **5/5** (pin tests + ops greps). CI was not waited on. ### Acceptance vs this merge Script + runbook + verify wiring are on `main`. Remaining **execution** checkboxes stay open: - [ ] LocalTerra `UPGRADE582_LOCAL=1` after `make deploy-local` (happy path + factory-first abort) - [ ] Columbus-5 live migrate (tx hashes + smoke table here / #391) — **launch BLOCK until RUN** - [ ] Live LCD re-inventory of listed-asset admins (issue #582 table is 2026-08-20) **PARTIAL (non-blocking):** `IsCodeIdWhitelisted` is enforced on the live path, but DRY_RUN skips that query when factory cw2 is already ≥1.9.0. Factory-version refuse is covered (`UPGRADE582_FORCE_FACTORY_VERSION=1.8.0`). Optional `UPGRADE582_REFRESH=1` batch loop does not parse wasm `has_more` (default off; runbook has the skip procedure). Do **not** close this issue on merge. Do **not** close #581 go on 8266 because F6 code landed.
PlasticDigits commented 2026-08-21 00:16:25 +00:00 (Migrated from gitlab.com)

mentioned in issue #585

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

marked as related to #585

marked as related to #585
PlasticDigits commented 2026-08-21 00:17:22 +00:00 (Migrated from gitlab.com)

P1 freeze UX (indexer route/solve + dApp humanize) filed as #585. Not required to close this issue; this issue remains the columbus-5 / LocalTerra execution tracker.

P1 freeze UX (indexer route/solve + dApp humanize) filed as #585. Not required to close this issue; this issue remains the columbus-5 / LocalTerra execution tracker.
PlasticDigits commented 2026-08-21 00:46:44 +00:00 (Migrated from gitlab.com)

Columbus-5 F6 migrate — PARTIAL (do not close)

Factory-first order was followed. 11 / 14 pairs are on pair 1.15.0. Three RPC resets left a mixed version set. Launch BLOCK stays. Do not treat smoke pin1=…/null as an unlisted pin — that pair is 10184/10184; publicnode dropped the second IsCodeIdWhitelisted read.

Store (cl8ydeploy)

Wasm checksum tx height code_id
pair e38ed8441d105e0a02d9424a3b80d7e2221c071abd76eb3628e93cf7b18fe992 6C44832EBE2E81B5FA0158F5203E9618332C3FD1C4ABCC32B3B3212DCE0F66F9 30039698 11601
factory 0422927baf44992848442f62065cd42685b5b1391e8e966b79687cda05c9a8b3 9ABEF0B2207646188BEFD6F702ED23ADE3795A592227B2A9263F2E6BAA82BF54 30039700 11602

Factory migrate (2-of-3 multisig1+multisig2)

  • tx 794AAB9E3AEBF32E7D0050F260EC907722CEC8CA955B00CAA1608B235D0600DB
  • cw2 1.8.0 → 1.9.0, live ContractInfo.code_id 11602
  • IsCodeIdWhitelisted 10184 → true, 6036 → true
  • config.pair_code_id is still 11586 (1.14.0). New CreatePair would not pin until UpdateConfig { pair_code_id: 11601 }.

Pair migrates → 11601 (cw2 1.15.0) — 11 OK, 3 still 11586 / 1.14.0

Pair tx live
terra1klwuxas6x7p6fjde60kq70t0hu86wvt3fvyr2vgs0nn32fnv0q4qwznwp4 893779ACD3BEE9CB08FB0FBB7A901B8BE07A4BDCD758E552DEE591ECB0AB6AC4 11601 / 1.15.0 pins 10184/10184
terra1y5xxv980jn0qu7n7y3slhtjehta6nlpqjkgcxl80uetdx84dxa4qegjhtx 5C1A17D71788659A102216D0253F0BEF08E6DDE4B842B610A5ED1894D7EC0892 11601 / 1.15.0 pins 10184/10184
terra16827w2c7zcvetck9xz8d6ds3379v77gelwra6jdafqkx9q9u0r8qvkluu0 6DBD464BEE1E9D3C478D65C238CDA8655092AF961B1A0ECEC9F834C1688D7B32 11601 / 1.15.0 pins 10184/10184
terra1ra7cugjhchr45kdupxe2al5fna0zxu6syl8xhpanfk8dsvkq9lksf6fm9l 385B41C50136C7085071C547928E0054658BD0631CAFC44330EB52E6BBB2E721 11601 / 1.15.0 pins 10184/10184
terra1nqjvd2xatac5ydcs6nstw7zp2yjc20p632ycxtevtf3rr2554fqswstx0n D4EE5EBC43BD1AB26FDD396D1161065F1F952BCAF73BC8B504DBF00010B96E03 11601 / 1.15.0 pins 10184/10184
terra1havxdjfyphjazc342r3cj2n3kslsptac2eunvw8uzayusywg9t4shtuz7v — publicnode RST mid tx sign still 11586 / 1.14.0 (no pins)
terra1p0sd0t2ggm9ye43gp0ryadx3wwkz5hzn99hnz93ve99397xvuufsvsmw73 — publicnode RST mid tx sign still 11586 / 1.14.0
terra1mp72n97rzwmqwudzycjj0e4jveetjnp622gnprv6ugqt3hfxg60sr5gkjm — generate-only RST still 11586 / 1.14.0
terra16k6huf87gzvnlgpvf85f8xfgawl6y2l5d4d5qdpyhknqaran9s5qx63c3r F6870AF7D121CF7B945785ED4CD770CC017C66B5FC663C5F088811D6B97B64BE 11601 / 1.15.0 pins 10184/6036
terra1pc7dvcucrl9sr4r2nhr2rv3ywhthskqqtvtvaerx9cff7l8gesdskjgmn6 8C34C10DF9C779B76F3DD25A48F4CB176DC0225C40E289F97F6D10694E2220CA 11601 / 1.15.0 pins 10184/10184
terra1ceprjsxp86ggftf5e38wwt34l83e5gq7penkdnv4wsatkwcs8v6qccw55f 80DD60D228C82389450EB2ED1040573DEBEBF6F14BA465C18B5BEB0613408C13 11601 / 1.15.0 pins 10184/10184
terra16vxrhpvpcucu05y0nr862vf9hnqeh274uaff4s7hz4n0ea74006qf5hgqy 24400E8B637B9DF96AC7D4CC52E838DEE924968292D740C76948335EAD36655E 11601 / 1.15.0 pins 10184/10184
terra1su5363453fj326u4t0kqar30f35cm3n0dc9yksg379u6875z350s4mm7h4 BED1CC34831E79ED2A618C1F1ACB6165DE0B2807DDCCAC435BBA8F92A15FADC8 11601 / 1.15.0 pins 10184/10184
terra1q5karg99p5hc0jvplgn2fd8akg39r4ss00cu5rwkm600yqvmssgqhwqvq0 ED97DCBBB158E6D1E486AB4A8E8AECDF207786F7267D07632D9008E33979F2C0 11601 / 1.15.0 pins 10184/10184

GetPairCount = 14. Listed-asset ContractInfo: 10184 (hub/CL8Y/gems) + 6036 (PEARL/QUARTZ) only.

Smoke

upgrade-582-code-id-pin.sh with SKIP_STORE + skip migrates aborted on pair 3 (pin1=10184/null). Independent LCD re-query: that pair pins 10184/10184 and both ids are whitelisted. Script never reached the three unmigrated pairs (those would hard-fail GetAssetCodeIds on 1.14.0 — correct).

Remaining to close this issue

  1. Retry the three leftover pair migrates (2-of-3), then full smoke until 14/14 rows.
  2. UpdateConfig { pair_code_id: 11601 } so new listings instantiate 1.15.0.
  3. Optional: LocalTerra UPGRADE582_LOCAL=1 log (script already on main).
  4. Do not AddWhitelistedCodeId 8266 / close #581 go from this partial run.
## Columbus-5 F6 migrate — PARTIAL (do not close) Factory-first order was followed. **11 / 14** pairs are on pair **1.15.0**. Three RPC resets left a mixed version set. Launch **BLOCK** stays. Do not treat smoke `pin1=…/null` as an unlisted pin — that pair is 10184/10184; publicnode dropped the second `IsCodeIdWhitelisted` read. ### Store (`cl8ydeploy`) | Wasm | checksum | tx | height | code_id | |------|----------|----|--------|---------| | pair | `e38ed8441d105e0a02d9424a3b80d7e2221c071abd76eb3628e93cf7b18fe992` | `6C44832EBE2E81B5FA0158F5203E9618332C3FD1C4ABCC32B3B3212DCE0F66F9` | 30039698 | **11601** | | factory | `0422927baf44992848442f62065cd42685b5b1391e8e966b79687cda05c9a8b3` | `9ABEF0B2207646188BEFD6F702ED23ADE3795A592227B2A9263F2E6BAA82BF54` | 30039700 | **11602** | ### Factory migrate (2-of-3 `multisig1`+`multisig2`) - tx `794AAB9E3AEBF32E7D0050F260EC907722CEC8CA955B00CAA1608B235D0600DB` - cw2 **1.8.0 → 1.9.0**, live `ContractInfo.code_id` **11602** - `IsCodeIdWhitelisted` 10184 → true, 6036 → true - `config.pair_code_id` is still **11586** (1.14.0). New `CreatePair` would not pin until `UpdateConfig { pair_code_id: 11601 }`. ### Pair migrates → 11601 (cw2 1.15.0) — 11 OK, 3 still 11586 / 1.14.0 | Pair | tx | live | |------|----|------| | `terra1klwuxas6x7p6fjde60kq70t0hu86wvt3fvyr2vgs0nn32fnv0q4qwznwp4` | `893779ACD3BEE9CB08FB0FBB7A901B8BE07A4BDCD758E552DEE591ECB0AB6AC4` | 11601 / 1.15.0 pins 10184/10184 | | `terra1y5xxv980jn0qu7n7y3slhtjehta6nlpqjkgcxl80uetdx84dxa4qegjhtx` | `5C1A17D71788659A102216D0253F0BEF08E6DDE4B842B610A5ED1894D7EC0892` | 11601 / 1.15.0 pins 10184/10184 | | `terra16827w2c7zcvetck9xz8d6ds3379v77gelwra6jdafqkx9q9u0r8qvkluu0` | `6DBD464BEE1E9D3C478D65C238CDA8655092AF961B1A0ECEC9F834C1688D7B32` | 11601 / 1.15.0 pins 10184/10184 | | `terra1ra7cugjhchr45kdupxe2al5fna0zxu6syl8xhpanfk8dsvkq9lksf6fm9l` | `385B41C50136C7085071C547928E0054658BD0631CAFC44330EB52E6BBB2E721` | 11601 / 1.15.0 pins 10184/10184 | | `terra1nqjvd2xatac5ydcs6nstw7zp2yjc20p632ycxtevtf3rr2554fqswstx0n` | `D4EE5EBC43BD1AB26FDD396D1161065F1F952BCAF73BC8B504DBF00010B96E03` | 11601 / 1.15.0 pins 10184/10184 | | `terra1havxdjfyphjazc342r3cj2n3kslsptac2eunvw8uzayusywg9t4shtuz7v` | — publicnode RST mid `tx sign` | **still 11586 / 1.14.0** (no pins) | | `terra1p0sd0t2ggm9ye43gp0ryadx3wwkz5hzn99hnz93ve99397xvuufsvsmw73` | — publicnode RST mid `tx sign` | **still 11586 / 1.14.0** | | `terra1mp72n97rzwmqwudzycjj0e4jveetjnp622gnprv6ugqt3hfxg60sr5gkjm` | — generate-only RST | **still 11586 / 1.14.0** | | `terra16k6huf87gzvnlgpvf85f8xfgawl6y2l5d4d5qdpyhknqaran9s5qx63c3r` | `F6870AF7D121CF7B945785ED4CD770CC017C66B5FC663C5F088811D6B97B64BE` | 11601 / 1.15.0 pins 10184/6036 | | `terra1pc7dvcucrl9sr4r2nhr2rv3ywhthskqqtvtvaerx9cff7l8gesdskjgmn6` | `8C34C10DF9C779B76F3DD25A48F4CB176DC0225C40E289F97F6D10694E2220CA` | 11601 / 1.15.0 pins 10184/10184 | | `terra1ceprjsxp86ggftf5e38wwt34l83e5gq7penkdnv4wsatkwcs8v6qccw55f` | `80DD60D228C82389450EB2ED1040573DEBEBF6F14BA465C18B5BEB0613408C13` | 11601 / 1.15.0 pins 10184/10184 | | `terra16vxrhpvpcucu05y0nr862vf9hnqeh274uaff4s7hz4n0ea74006qf5hgqy` | `24400E8B637B9DF96AC7D4CC52E838DEE924968292D740C76948335EAD36655E` | 11601 / 1.15.0 pins 10184/10184 | | `terra1su5363453fj326u4t0kqar30f35cm3n0dc9yksg379u6875z350s4mm7h4` | `BED1CC34831E79ED2A618C1F1ACB6165DE0B2807DDCCAC435BBA8F92A15FADC8` | 11601 / 1.15.0 pins 10184/10184 | | `terra1q5karg99p5hc0jvplgn2fd8akg39r4ss00cu5rwkm600yqvmssgqhwqvq0` | `ED97DCBBB158E6D1E486AB4A8E8AECDF207786F7267D07632D9008E33979F2C0` | 11601 / 1.15.0 pins 10184/10184 | `GetPairCount` = 14. Listed-asset ContractInfo: 10184 (hub/CL8Y/gems) + 6036 (PEARL/QUARTZ) only. ### Smoke `upgrade-582-code-id-pin.sh` with `SKIP_STORE` + skip migrates aborted on pair 3 (`pin1=10184/null`). Independent LCD re-query: that pair pins **10184/10184** and both ids are whitelisted. Script never reached the three unmigrated pairs (those would hard-fail `GetAssetCodeIds` on 1.14.0 — correct). ### Remaining to close this issue 1. Retry the three leftover pair migrates (2-of-3), then full smoke until 14/14 rows. 2. `UpdateConfig { pair_code_id: 11601 }` so new listings instantiate 1.15.0. 3. Optional: LocalTerra `UPGRADE582_LOCAL=1` log (script already on `main`). 4. Do **not** `AddWhitelistedCodeId 8266` / close #581 go from this partial run.
PlasticDigits commented 2026-08-21 00:46:46 +00:00 (Migrated from gitlab.com)

mentioned in issue #391

mentioned in issue #391
PlasticDigits commented 2026-08-21 00:46:48 +00:00 (Migrated from gitlab.com)

mentioned in issue #581

mentioned in issue #581
PlasticDigits commented 2026-08-21 00:50:23 +00:00 (Migrated from gitlab.com)

Columbus-5 F6 migrate complete — closing

Factory-first order held. Remaining three pairs migrated; smoke 14/14; config.pair_code_id now 11601. Independent LCD re-query matches.

Leftover pair migrates → 11601 / 1.15.0

Pair tx height
terra1havxdjfyphjazc342r3cj2n3kslsptac2eunvw8uzayusywg9t4shtuz7v 8BF125B308063FE3089BAE422E7A1354DD3C0DAE89B312922DB6D6C44F84E5A6 30039810
terra1p0sd0t2ggm9ye43gp0ryadx3wwkz5hzn99hnz93ve99397xvuufsvsmw73 2C51DB5D237D3BB072A427685EE2F695BFF704D362DD5D27544B0E1F769B8E32 30039812
terra1mp72n97rzwmqwudzycjj0e4jveetjnp622gnprv6ugqt3hfxg60sr5gkjm 6E145EF310A658C4F6485EB099747E96AB68BEA27391FEA814900E47BAAC7815 30039814

Pins on those three: 10184 / 6036 (HybridSimulation ok).

Factory UpdateConfig { pair_code_id: 11601 }

  • tx F8F97C113758DB50B180BEA76E994CA8D1AB567A5969273161B3FAA7EE8E1787 height 30039822
  • live config.pair_code_id 11601 (was 11586). New CreatePair instantiates pair 1.15.0.

Smoke (all 14, GetPairCount=14)

pair pin0 pin1 sim
terra1klwux…znwp4 10184 10184 ok
terra1y5xxv…gjhtx 10184 10184 ok
terra16827w…kluu0 10184 10184 ok
terra1ra7cu…6fm9l 10184 10184 ok
terra1nqjvd…stx0n 10184 10184 ok
terra1havxd…tuz7v 10184 6036 ok
terra1p0sd0…smw73 10184 6036 ok
terra1mp72n…5gkjm 10184 6036 ok
terra16k6hu…63c3r 10184 6036 ok
terra1pc7dv…jgmn6 10184 10184 ok
terra1ceprj…cw55f 10184 10184 ok
terra16vxrh…5hgqy 10184 10184 ok
terra1su536…mm7h4 10184 10184 ok
terra1q5kar…wqvq0 10184 10184 ok

Live: factory 11602 / 1.9.0, every pair 11601 / 1.15.0. Quotes remaining ungated by design.

Not unblocked by this close

  • #581 / 8266 SpaceUSD still no-go until source review (or admin cleared / wrap-to-10184). Do not AddWhitelistedCodeId 8266.
  • #585 P1 freeze UX (dApp + indexer) still open.
## Columbus-5 F6 migrate complete — closing Factory-first order held. Remaining three pairs migrated; smoke **14/14**; `config.pair_code_id` now **11601**. Independent LCD re-query matches. ### Leftover pair migrates → 11601 / 1.15.0 | Pair | tx | height | |------|----|--------| | `terra1havxdjfyphjazc342r3cj2n3kslsptac2eunvw8uzayusywg9t4shtuz7v` | `8BF125B308063FE3089BAE422E7A1354DD3C0DAE89B312922DB6D6C44F84E5A6` | 30039810 | | `terra1p0sd0t2ggm9ye43gp0ryadx3wwkz5hzn99hnz93ve99397xvuufsvsmw73` | `2C51DB5D237D3BB072A427685EE2F695BFF704D362DD5D27544B0E1F769B8E32` | 30039812 | | `terra1mp72n97rzwmqwudzycjj0e4jveetjnp622gnprv6ugqt3hfxg60sr5gkjm` | `6E145EF310A658C4F6485EB099747E96AB68BEA27391FEA814900E47BAAC7815` | 30039814 | Pins on those three: **10184 / 6036** (HybridSimulation ok). ### Factory `UpdateConfig { pair_code_id: 11601 }` - tx `F8F97C113758DB50B180BEA76E994CA8D1AB567A5969273161B3FAA7EE8E1787` height 30039822 - live `config.pair_code_id` **11601** (was 11586). New `CreatePair` instantiates pair **1.15.0**. ### Smoke (all 14, GetPairCount=14) | pair | pin0 | pin1 | sim | |------|------|------|-----| | `terra1klwux…znwp4` | 10184 | 10184 | ok | | `terra1y5xxv…gjhtx` | 10184 | 10184 | ok | | `terra16827w…kluu0` | 10184 | 10184 | ok | | `terra1ra7cu…6fm9l` | 10184 | 10184 | ok | | `terra1nqjvd…stx0n` | 10184 | 10184 | ok | | `terra1havxd…tuz7v` | 10184 | 6036 | ok | | `terra1p0sd0…smw73` | 10184 | 6036 | ok | | `terra1mp72n…5gkjm` | 10184 | 6036 | ok | | `terra16k6hu…63c3r` | 10184 | 6036 | ok | | `terra1pc7dv…jgmn6` | 10184 | 10184 | ok | | `terra1ceprj…cw55f` | 10184 | 10184 | ok | | `terra16vxrh…5hgqy` | 10184 | 10184 | ok | | `terra1su536…mm7h4` | 10184 | 10184 | ok | | `terra1q5kar…wqvq0` | 10184 | 10184 | ok | Live: factory **11602 / 1.9.0**, every pair **11601 / 1.15.0**. Quotes remaining ungated by design. ### Not unblocked by this close - **#581 / 8266 SpaceUSD** still no-go until source review (or admin cleared / wrap-to-10184). Do not `AddWhitelistedCodeId 8266`. - **#585** P1 freeze UX (dApp + indexer) still open.
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-08-21 00:50:28 +00:00
PlasticDigits commented 2026-08-21 01:15:13 +00:00 (Migrated from gitlab.com)

mentioned in merge request !1109

mentioned in merge request !1109
PlasticDigits commented 2026-08-21 01:15:48 +00:00 (Migrated from gitlab.com)

mentioned in merge request !1110

mentioned in merge request !1110
PlasticDigits commented 2026-08-21 01:23:50 +00:00 (Migrated from gitlab.com)

!1110 merge review (hardening follow-up)

Merged !1110 (fix/584-upgrade-582-retry-harden) onto main without automerge / without waiting for CI.

Conflicts: none vs current main (MR already contained 70e79508; git merge-tree clean).

Local sanity: make verify-issue-584 17/17 (syntax, greps, pagination 31/61, refresh-event parser, DRY_RUN factory-first + UpdateConfig, refuse factory 1.8.0, refuse unparseable IsCodeIdWhitelisted, probe fail-closed).

CI: pipeline 2778207657 failed on gitleaks with ci_quota_exceeded (job never started). Not a script defect; project does not require a green pipeline to merge. Quota is a GitLab account/runner issue, not this diff.

What this MR adds vs the columbus-5 run that closed #584: LCD IsCodeIdWhitelisted retries (the pin1=10184/null smoke abort), skip pairs already on the target code id, UpdateConfig { pair_code_id } before pair migrate, UPGRADE582_REFRESH=1 parses wasm has_more / next_start_after. Invariant F6 unchanged. No AddWhitelistedCodeId 8266.

Remaining (not this MR)

  • #581 / 8266 still BLOCK / no-go. This merge does not whitelist SpaceUSD.
  • #585 still open — P1 freeze UX (dApp + indexer route/solve).
  • Optional LocalTerra UPGRADE582_LOCAL=1 rehearsal was never attached (MR test plan left unchecked; #584 closed on the live columbus-5 run). Not blocking; no new issue filed — next F6 wasm upgrade should use this script on LocalTerra first if operators want a dry rehearsal.

No new tracking issue: #581 and #585 already cover the leftover product/ops gates.

## !1110 merge review (hardening follow-up) Merged [!1110](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/392) (`fix/584-upgrade-582-retry-harden`) onto `main` without automerge / without waiting for CI. **Conflicts:** none vs current `main` (MR already contained `70e79508`; `git merge-tree` clean). **Local sanity:** `make verify-issue-584` **17/17** (syntax, greps, pagination 31/61, refresh-event parser, DRY_RUN factory-first + UpdateConfig, refuse factory 1.8.0, refuse unparseable `IsCodeIdWhitelisted`, probe fail-closed). **CI:** pipeline [2778207657](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/pipelines/2778207657) **failed** on `gitleaks` with `ci_quota_exceeded` (job never started). Not a script defect; project does not require a green pipeline to merge. Quota is a GitLab account/runner issue, not this diff. **What this MR adds vs the columbus-5 run that closed #584:** LCD `IsCodeIdWhitelisted` retries (the `pin1=10184/null` smoke abort), skip pairs already on the target code id, `UpdateConfig { pair_code_id }` before pair migrate, `UPGRADE582_REFRESH=1` parses wasm `has_more` / `next_start_after`. Invariant **F6** unchanged. No `AddWhitelistedCodeId 8266`. ### Remaining (not this MR) - **#581 / 8266** still **BLOCK** / no-go. This merge does not whitelist SpaceUSD. - **#585** still open — P1 freeze UX (dApp + indexer `route/solve`). - Optional LocalTerra `UPGRADE582_LOCAL=1` rehearsal was never attached (MR test plan left unchecked; #584 closed on the live columbus-5 run). Not blocking; no new issue filed — next F6 wasm upgrade should use this script on LocalTerra first if operators want a dry rehearsal. No new tracking issue: #581 and #585 already cover the leftover product/ops gates.
PlasticDigits commented 2026-08-21 01:23:55 +00:00 (Migrated from gitlab.com)

mentioned in commit 86c5c0566b

mentioned in commit 86c5c0566b1fe942afc6f7006cd735bf7ecee9f5
PlasticDigits commented 2026-08-21 03:42:24 +00:00 (Migrated from gitlab.com)

mentioned in merge request !1111

mentioned in merge request !1111
PlasticDigits commented 2026-08-21 03:43:21 +00:00 (Migrated from gitlab.com)

#585 (dApp banners + route/solve hop exclude), which this issue deferred, is in !1111. Visibility only — does not replace on-chain F6 or change the columbus-5 migrate/runbook.

#585 (dApp banners + `route/solve` hop exclude), which this issue deferred, is in !1111. Visibility only — does not replace on-chain F6 or change the columbus-5 migrate/runbook.
PlasticDigits commented 2026-08-22 10:59:04 +00:00 (Migrated from gitlab.com)

mentioned in issue #592

mentioned in issue #592
PlasticDigits commented 2026-08-25 02:08:33 +00:00 (Migrated from gitlab.com)

mentioned in issue #634

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