feat(wrap-mapper): split fee_wrap_bps / fee_unwrap_bps for ≈2% unwrap all-in (no gross-up) #9

Closed
opened 2026-08-11 01:32:00 +00:00 by PlasticDigits · 15 comments
PlasticDigits commented 2026-08-11 01:32:00 +00:00 (Migrated from gitlab.com)

Summary

Split wrap-mapper fee_bps into fee_wrap_bps and fee_unwrap_bps, migrate mainnet, and set fee_wrap_bps=200 / fee_unwrap_bps=51 so user unwrap all-in ≈ 2% under today’s 1.5% Classic burn tax without InstantWithdraw gross-up. Document the retune rule for future tax changes.

Product decision (approved): prefer asymmetric fees + existing tax incidence over on-chain gross-up.

Companion (DEX consumer): track UI/docs/ops in PlasticDigits/cl8y-dex-terraclassic (linked after create).

Related: cl8y-dex #512 (UI disclosure of stacked tax; closed mitigation), mainnet wrap-mapper terra1xuuuhpmyd5t29ry7mydg7ra2q2phrwhx7j28nx7x9sjw6zznkumsz0nmd2.


Current codebase

On-chain (columbus-5, verified)

Param Value
wrap-mapper config.fee_bps 200 (single fee for wrap and unwrap)
burn_tax_rate 0.015 (1.5%)
Governance terra1xsecn4snv94ezcez0z3vq8an9j4h4kxxcydp8l

Behavior today

  1. Wrap: fee = amount × fee_bps / 10_000; MsgExecuteContract wrap_deposit is untaxed → user nets post-fee CW20 only.
  2. Unwrap: fee skimmed with same fee_bps; treasury InstantWithdraw → BankMsg::Send of post-fee amount; receiver pays burn tax.
  3. With fee_bps=200 + 1.5% tax: 10 000 → fee 200 → withdraw 9 800 → user 9 653 (~3.47% all-in), not 2%.

Fee was raised 100→200 after Prop #12223 so a future gross-up could fund tax from fee residual (docs/DEPLOYMENT.md “Wrap fee vs burn tax”). Gross-up was not implemented; users still eat tax on top of the full 2% fee.

Relevant files (ustr-cmm)

Path Role
contracts/contracts/wrap-mapper/src/state.rs Config.fee_bps, MIN_FEE_BPS, MAX_FEE_BPS
contracts/contracts/wrap-mapper/src/msg.rs InstantiateMsg.fee_bps, ExecuteMsg::SetFeeBps, ConfigResponse.fee_bps
contracts/contracts/wrap-mapper/src/contract.rs wrap + unwrap both call calculate_fee(..., config.fee_bps); migrate/query
contracts/contracts/wrap-mapper/src/error.rs fee bound errors
docs/DEPLOYMENT.md fee vs burn tax policy + set_fee_bps gov example
docs/CONTRACTS.md wrap-mapper mainnet config narrative
plans/NATIVE_TOKEN_WRAPPING.md tax-event architecture

Audit note: MIN_FEE_BPS / “fee must cover tax” assumptions (audits/INTERNAL_KIMIK3_*.md M-4) assume a single fee intended to subsidize unwrap tax under gross-up — must be revised for asymmetric fees.


Why this is needed

  • Documented / marketed unwrap cost is ~2%; live all-in is ~3.47% (#512 evidence tx C282C337…).
  • Approved alternative to gross-up: keep wrap at 200 bps, lower unwrap fee only so stacked fee+tax ≈ 2% all-in.
  • Economics of fee_unwrap_bps=51 (no gross-up) match gross-up+200 for treasury surplus (~+51 bps per unwrap) while avoiding tax-oracle / gross-up contract complexity.
  • Single fee_bps cannot express “expensive wrap, tax-tuned unwrap”.

Target math (burn_tax = 0.015)

Want user receive ≈ 0.98 × A with no gross-up:

(1 - fee_unwrap) × (1 - burn_tax_rate) = 0.98
fee_unwrap ≈ 1 - 0.98/(1-0.015) ≈ 0.5076% → 51 bps
Setting Wrap user cost Unwrap user all-in
Today fee_bps=200 2% ~3.47%
Target wrap=200, unwrap=51 2% ≈2.00% (51 → slightly ≤2%; 50 → slightly >2%)

Prefer 51 if goal is ≤ 2% all-in.


Constraints / guardrails

  1. No InstantWithdraw gross-up in this issue — tax incidence stays on the user; unwrap fee is tuned so all-in ≈ 2%.
  2. Do not lower wrap fee to 51 — wrap path is untaxed; product keeps fee_wrap_bps=200.
  3. No CosmWasm LCD tax oracle required — retune is an off-chain / gov param process.
  4. Solvency invariant unchanged: unwrap burns A CW20 and withdraws A − fee_unwrap; treasury surplus Δ ≈ +fee_unwrap (user-paid tax does not erode native ≥ supply). Document explicitly — old “fee ≥ tax” floor does not apply to unwrap under this policy.
  5. MIN_FEE_BPS / bounds: allow fee_unwrap_bps=51 (and potentially lower after retune). Do not force unwrap fee ≥ burn tax. Keep a sane MAX_FEE_BPS for both. Zero fee: follow existing product policy (reject or allow only if explicitly decided — default keep reject-zero if that is current wrap-mapper behavior).
  6. Migration safety: existing mainnet state has fee_bps=200 → migrate to fee_wrap_bps=200 and fee_unwrap_bps=200 or directly to 200/51 in the same gov window; never leave unwrap at 200 after advertising the fix without setting 51.
  7. Governance only for fee changes (terra1xsecn…); not DEX factory multisig.
  8. Pause / rate-limit / mapping behavior must remain unchanged.
  9. JSON / clients: breaking ConfigResponse shape is OK if versioned migrate + coordinated DEX release; prefer additive fields + deprecate fee_bps only if a short dual-read window is required — document chosen compat strategy in the MR.
  10. Agents must not broadcast mainnet gov txs; provide exact msgs for human operators.

  1. State: replace fee_bps with fee_wrap_bps + fee_unwrap_bps on Config.
  2. Msgs:
    • Instantiate: accept both (sensible defaults; LocalTerra may keep low test values).
    • Execute: SetFeeWrapBps / SetFeeUnwrapBps and/or SetFees { fee_wrap_bps, fee_unwrap_bps } (governance-gated).
    • Query Config: return both fields; if temporary compat needed, mirror fee_bps → unwrap or omit with clear changelog.
  3. Apply fees: wrap/mint path → fee_wrap_bps; unwrap path → fee_unwrap_bps only.
  4. Migrate: Cw2 version bump; map old fee_bps → both fields; then gov set 200 / 51 on columbus-5.
  5. Docs: replace “single fee covers tax” narrative with asymmetric policy + retune rule (below).
  6. Coordinate DEX issue for consumers before declaring production done.

Retune rule (must document)

When chain burn_tax_rate changes, recompute:

fee_unwrap_bps = round(10000 - 9800 / (1 - burn_tax_rate))

aimed at 2% user all-in (receive/A = 0.98). Worked example: burn_tax_rate=0.015 → 51. Prefer rounding so all-in is ≤ 2% when ambiguous.

Also document: if tax rises above ~2%, “2% all-in with no gross-up” becomes impossible without paying users a subsidy — escalate to product (gross-up or higher all-in target).


Acceptance criteria

  • wrap-mapper state/msgs/query expose fee_wrap_bps and fee_unwrap_bps (no reliance on a single fee for both paths).
  • Wrap uses only fee_wrap_bps; unwrap uses only fee_unwrap_bps.
  • Migrate from single fee_bps succeeds on upgrade tests; mainnet migrate plan written.
  • Mainnet (post-gov): fee_wrap_bps=200, fee_unwrap_bps=51 (or documented equivalent from retune rule at ship time).
  • Unwrap 10 000 raw units → user native receive ≈ 9 800 under 1.5% tax (integer tolerance documented); not 9 653.
  • Wrap 10 000 → CW20 net 9 800 at 200 bps (unchanged product wrap fee).
  • docs/DEPLOYMENT.md + docs/CONTRACTS.md (+ wrapping plan if needed) document asymmetric fees, solvency argument, and retune rule.
  • Audit/MIN_FEE comments updated so reviewers do not reintroduce “unwrap fee ≥ tax”.
  • No gross-up code added to treasury InstantWithdraw for this issue.
  • Operator runbook includes exact wasm execute JSON for SetFees / setters (no secrets).

Test plan (all paths)

Unit / contract

  • Instantiate with distinct wrap/unwrap fees; config query matches.
  • Wrap fee math uses fee_wrap_bps only (unwrap fee ignored).
  • Unwrap fee math uses fee_unwrap_bps only (wrap fee ignored).
  • SetFeeWrapBps / SetFeeUnwrapBps / SetFees: auth (gov only), bounds, events/attrs.
  • Migrate: old fee_bps=200 → expected new fields; Cw2 version.
  • Pause still blocks wrap + unwrap; rate limits unchanged.
  • Fee truncation / dust amounts (1–2 units) behave safely.

Integration / LocalTerra (or multi-test)

  • Wrap then unwrap round-trip with 200/51; balances match fee formulas (simulate burn tax in test harness if chain tax not present).
  • Mapping + minter permissions unchanged after migrate.

Mainnet verify (read-only then tiny live)

  • Query config shows 200/51 after gov.
  • Tiny unwrap: received ≈ post-fee_unwrap × (1 − tax); record tx hash.
  • Tiny wrap: received = post-fee_wrap; record tx hash.
  • Treasury uluna/uusd ≥ cLUNC/cUSTC supply after probes.

Test plan (attack / hack / abuse)

  • Unauthorized fee change — non-gov SetFees* rejected.
  • Fee extreme: unwrap/wrap at MAX_FEE_BPS; at minimum allowed; reject out-of-bounds / zero if policy rejects zero.
  • Asymmetric griefing: attacker cannot set wrap=1 unwrap=MAX via non-gov; gov key compromise still in threat model (document; out of scope to add timelock unless separately approved).
  • Solvency drain myth: with fee_unwrap << tax, prove native ≥ supply does not regress across unwraps (surplus += unwrap fee); add regression test/comment.
  • Wrong path fee: ensure wrap cannot be charged unwrap fee and vice versa (message crafting / hook confusion).
  • Migrate double-apply / corrupt state — migrate idempotence or clear error; no fee field zeroing.
  • Paused unwrap still fail-closed; fee split must not bypass pause.
  • Recipient / exchange-deposit abuse unchanged (UI warning remains DEX-side); contract still sends to specified recipient.

Verification criteria

Check Pass
Config query fee_wrap_bps=200, fee_unwrap_bps=51 on columbus-5
Unwrap all-in Empirical receive/A ∈ [0.979, 0.981] for sizeable test amount under tax 1.5%
Wrap fee receive/A = 0.98 at 200 bps
Docs Retune formula + worked 1.5%→51 example published
Gross-up Absent from unwrap/treasury send path
DEX companion Linked issue updated / unblocked for consumer release

Retune verification: given a hypothetical burn_tax_rate, docs alone suffice for an operator to compute the next fee_unwrap_bps without reading this issue.

## Summary Split wrap-mapper `fee_bps` into **`fee_wrap_bps`** and **`fee_unwrap_bps`**, migrate mainnet, and set **`fee_wrap_bps=200` / `fee_unwrap_bps=51`** so **user unwrap all-in ≈ 2%** under today’s 1.5% Classic burn tax **without InstantWithdraw gross-up**. Document the retune rule for future tax changes. **Product decision (approved):** prefer asymmetric fees + existing tax incidence over on-chain gross-up. **Companion (DEX consumer):** track UI/docs/ops in `PlasticDigits/cl8y-dex-terraclassic` (linked after create). Related: [cl8y-dex #512](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/512) (UI disclosure of stacked tax; closed mitigation), mainnet wrap-mapper `terra1xuuuhpmyd5t29ry7mydg7ra2q2phrwhx7j28nx7x9sjw6zznkumsz0nmd2`. --- ## Current codebase ### On-chain (columbus-5, verified) | Param | Value | |-------|--------| | wrap-mapper `config.fee_bps` | **200** (single fee for wrap **and** unwrap) | | `burn_tax_rate` | **0.015** (1.5%) | | Governance | `terra1xsecn4snv94ezcez0z3vq8an9j4h4kxxcydp8l` | ### Behavior today 1. **Wrap:** fee = `amount × fee_bps / 10_000`; `MsgExecuteContract` wrap_deposit is **untaxed** → user nets post-fee CW20 only. 2. **Unwrap:** fee skimmed with same `fee_bps`; treasury `InstantWithdraw` → `BankMsg::Send` of post-fee amount; **receiver pays burn tax**. 3. With `fee_bps=200` + 1.5% tax: 10 000 → fee 200 → withdraw 9 800 → user **9 653** (**~3.47%** all-in), not 2%. Fee was raised 100→200 after [Prop #12223](https://station.terraclassic.community/proposal/columbus-5/12223) so a **future** gross-up could fund tax from fee residual (`docs/DEPLOYMENT.md` “Wrap fee vs burn tax”). **Gross-up was not implemented**; users still eat tax on top of the full 2% fee. ### Relevant files (ustr-cmm) | Path | Role | |------|------| | `contracts/contracts/wrap-mapper/src/state.rs` | `Config.fee_bps`, `MIN_FEE_BPS`, `MAX_FEE_BPS` | | `contracts/contracts/wrap-mapper/src/msg.rs` | `InstantiateMsg.fee_bps`, `ExecuteMsg::SetFeeBps`, `ConfigResponse.fee_bps` | | `contracts/contracts/wrap-mapper/src/contract.rs` | wrap + unwrap both call `calculate_fee(..., config.fee_bps)`; migrate/query | | `contracts/contracts/wrap-mapper/src/error.rs` | fee bound errors | | `docs/DEPLOYMENT.md` | fee vs burn tax policy + `set_fee_bps` gov example | | `docs/CONTRACTS.md` | wrap-mapper mainnet config narrative | | `plans/NATIVE_TOKEN_WRAPPING.md` | tax-event architecture | Audit note: `MIN_FEE_BPS` / “fee must cover tax” assumptions (`audits/INTERNAL_KIMIK3_*.md` M-4) assume a **single** fee intended to subsidize unwrap tax under gross-up — must be revised for asymmetric fees. --- ## Why this is needed - Documented / marketed unwrap cost is **~2%**; live all-in is **~3.47%** (#512 evidence tx `C282C337…`). - **Approved alternative to gross-up:** keep wrap at **200 bps**, lower **unwrap fee only** so stacked fee+tax ≈ 2% all-in. - Economics of `fee_unwrap_bps=51` (no gross-up) match gross-up+200 for treasury surplus (~+51 bps per unwrap) while avoiding tax-oracle / gross-up contract complexity. - Single `fee_bps` cannot express “expensive wrap, tax-tuned unwrap”. ### Target math (burn_tax = 0.015) Want user receive ≈ `0.98 × A` with no gross-up: ```text (1 - fee_unwrap) × (1 - burn_tax_rate) = 0.98 fee_unwrap ≈ 1 - 0.98/(1-0.015) ≈ 0.5076% → 51 bps ``` | Setting | Wrap user cost | Unwrap user all-in | |---------|----------------|--------------------| | Today `fee_bps=200` | 2% | ~3.47% | | **Target** `wrap=200`, `unwrap=51` | 2% | **≈2.00%** (51 → slightly ≤2%; 50 → slightly >2%) | Prefer **51** if goal is **≤ 2%** all-in. --- ## Constraints / guardrails 1. **No InstantWithdraw gross-up** in this issue — tax incidence stays on the user; unwrap fee is tuned so all-in ≈ 2%. 2. **Do not lower wrap fee** to 51 — wrap path is untaxed; product keeps **`fee_wrap_bps=200`**. 3. **No CosmWasm LCD tax oracle** required — retune is an off-chain / gov param process. 4. **Solvency invariant unchanged:** unwrap burns `A` CW20 and withdraws `A − fee_unwrap`; treasury surplus Δ ≈ `+fee_unwrap` (user-paid tax does not erode `native ≥ supply`). Document explicitly — old “fee ≥ tax” floor does **not** apply to unwrap under this policy. 5. **`MIN_FEE_BPS` / bounds:** allow `fee_unwrap_bps=51` (and potentially lower after retune). Do not force unwrap fee ≥ burn tax. Keep a sane `MAX_FEE_BPS` for both. Zero fee: follow existing product policy (reject or allow only if explicitly decided — default keep reject-zero if that is current wrap-mapper behavior). 6. **Migration safety:** existing mainnet state has `fee_bps=200` → migrate to `fee_wrap_bps=200` and `fee_unwrap_bps=200` **or** directly to `200/51` in the same gov window; never leave unwrap at 200 after advertising the fix without setting 51. 7. **Governance only** for fee changes (`terra1xsecn…`); not DEX factory multisig. 8. **Pause / rate-limit / mapping** behavior must remain unchanged. 9. **JSON / clients:** breaking `ConfigResponse` shape is OK if versioned migrate + coordinated DEX release; prefer additive fields + deprecate `fee_bps` only if a short dual-read window is required — document chosen compat strategy in the MR. 10. **Agents must not broadcast** mainnet gov txs; provide exact msgs for human operators. --- ## Recommended direction 1. **State:** replace `fee_bps` with `fee_wrap_bps` + `fee_unwrap_bps` on `Config`. 2. **Msgs:** - Instantiate: accept both (sensible defaults; LocalTerra may keep low test values). - Execute: `SetFeeWrapBps` / `SetFeeUnwrapBps` and/or `SetFees { fee_wrap_bps, fee_unwrap_bps }` (governance-gated). - Query `Config`: return both fields; if temporary compat needed, mirror `fee_bps` → unwrap or omit with clear changelog. 3. **Apply fees:** wrap/mint path → `fee_wrap_bps`; unwrap path → `fee_unwrap_bps` only. 4. **Migrate:** Cw2 version bump; map old `fee_bps` → both fields; then gov set **200 / 51** on columbus-5. 5. **Docs:** replace “single fee covers tax” narrative with asymmetric policy + **retune rule** (below). 6. **Coordinate** DEX issue for consumers before declaring production done. ### Retune rule (must document) When chain `burn_tax_rate` changes, recompute: ```text fee_unwrap_bps = round(10000 - 9800 / (1 - burn_tax_rate)) ``` aimed at **2% user all-in** (`receive/A = 0.98`). Worked example: `burn_tax_rate=0.015` → **51**. Prefer rounding so all-in is **≤ 2%** when ambiguous. Also document: if tax rises above ~2%, “2% all-in with no gross-up” becomes impossible without paying users a subsidy — escalate to product (gross-up or higher all-in target). --- ## Acceptance criteria - [ ] wrap-mapper state/msgs/query expose `fee_wrap_bps` and `fee_unwrap_bps` (no reliance on a single fee for both paths). - [ ] Wrap uses only `fee_wrap_bps`; unwrap uses only `fee_unwrap_bps`. - [ ] Migrate from single `fee_bps` succeeds on upgrade tests; mainnet migrate plan written. - [ ] Mainnet (post-gov): `fee_wrap_bps=200`, `fee_unwrap_bps=51` (or documented equivalent from retune rule at ship time). - [ ] Unwrap 10 000 raw units → user native receive ≈ **9 800** under 1.5% tax (integer tolerance documented); **not** 9 653. - [ ] Wrap 10 000 → CW20 net **9 800** at 200 bps (unchanged product wrap fee). - [ ] `docs/DEPLOYMENT.md` + `docs/CONTRACTS.md` (+ wrapping plan if needed) document asymmetric fees, solvency argument, and **retune rule**. - [ ] Audit/MIN_FEE comments updated so reviewers do not reintroduce “unwrap fee ≥ tax”. - [ ] No gross-up code added to treasury InstantWithdraw for this issue. - [ ] Operator runbook includes exact `wasm execute` JSON for `SetFees` / setters (no secrets). --- ## Test plan (all paths) ### Unit / contract - [ ] Instantiate with distinct wrap/unwrap fees; config query matches. - [ ] Wrap fee math uses `fee_wrap_bps` only (unwrap fee ignored). - [ ] Unwrap fee math uses `fee_unwrap_bps` only (wrap fee ignored). - [ ] `SetFeeWrapBps` / `SetFeeUnwrapBps` / `SetFees`: auth (gov only), bounds, events/attrs. - [ ] Migrate: old `fee_bps=200` → expected new fields; Cw2 version. - [ ] Pause still blocks wrap + unwrap; rate limits unchanged. - [ ] Fee truncation / dust amounts (1–2 units) behave safely. ### Integration / LocalTerra (or multi-test) - [ ] Wrap then unwrap round-trip with 200/51; balances match fee formulas (simulate burn tax in test harness if chain tax not present). - [ ] Mapping + minter permissions unchanged after migrate. ### Mainnet verify (read-only then tiny live) - [ ] Query config shows 200/51 after gov. - [ ] Tiny unwrap: received ≈ post-`fee_unwrap` × (1 − tax); record tx hash. - [ ] Tiny wrap: received = post-`fee_wrap`; record tx hash. - [ ] Treasury `uluna`/`uusd` ≥ cLUNC/cUSTC supply after probes. --- ## Test plan (attack / hack / abuse) - [ ] **Unauthorized fee change** — non-gov `SetFees*` rejected. - [ ] **Fee extreme:** unwrap/wrap at `MAX_FEE_BPS`; at minimum allowed; reject out-of-bounds / zero if policy rejects zero. - [ ] **Asymmetric griefing:** attacker cannot set wrap=1 unwrap=MAX via non-gov; gov key compromise still in threat model (document; out of scope to add timelock unless separately approved). - [ ] **Solvency drain myth:** with `fee_unwrap << tax`, prove `native ≥ supply` does not regress across unwraps (surplus += unwrap fee); add regression test/comment. - [ ] **Wrong path fee:** ensure wrap cannot be charged unwrap fee and vice versa (message crafting / hook confusion). - [ ] **Migrate double-apply / corrupt state** — migrate idempotence or clear error; no fee field zeroing. - [ ] **Paused unwrap** still fail-closed; fee split must not bypass pause. - [ ] **Recipient / exchange-deposit** abuse unchanged (UI warning remains DEX-side); contract still sends to specified recipient. --- ## Verification criteria | Check | Pass | |-------|------| | Config query | `fee_wrap_bps=200`, `fee_unwrap_bps=51` on columbus-5 | | Unwrap all-in | Empirical receive/A ∈ [0.979, 0.981] for sizeable test amount under tax 1.5% | | Wrap fee | receive/A = 0.98 at 200 bps | | Docs | Retune formula + worked 1.5%→51 example published | | Gross-up | Absent from unwrap/treasury send path | | DEX companion | Linked issue updated / unblocked for consumer release | **Retune verification:** given a hypothetical `burn_tax_rate`, docs alone suffice for an operator to compute the next `fee_unwrap_bps` without reading this issue.
PlasticDigits commented 2026-08-11 01:32:52 +00:00 (Migrated from gitlab.com)
mentioned in issue PlasticDigits/cl8y-dex-terraclassic#516
PlasticDigits commented 2026-08-11 01:33:02 +00:00 (Migrated from gitlab.com)
marked as related to PlasticDigits/cl8y-dex-terraclassic#516
PlasticDigits commented 2026-08-11 01:33:10 +00:00 (Migrated from gitlab.com)

Companion DEX consumer issue: https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/516

Bundled rollout: ship ustr-cmm fee split + mainnet 200/51 first, then DEX #516 + Coolify frontend redeploy.

Companion DEX consumer issue: https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/516 Bundled rollout: ship ustr-cmm fee split + mainnet 200/51 first, then DEX #516 + Coolify frontend redeploy.
PlasticDigits commented 2026-08-11 01:43:34 +00:00 (Migrated from gitlab.com)

mentioned in commit b3fc79cbfc

mentioned in commit b3fc79cbfc42614070d2122d01441195f5e9a3d2
PlasticDigits commented 2026-08-11 01:43:42 +00:00 (Migrated from gitlab.com)

mentioned in merge request !31

mentioned in merge request !31
PlasticDigits commented 2026-08-11 01:45:17 +00:00 (Migrated from gitlab.com)

Implementation MR: https://gitlab.com/PlasticDigits2/ustr-cmm/-/merge_requests/9

Done in MR (code/docs/tests):

  • state/msgs/query expose fee_wrap_bps + fee_unwrap_bps
  • Wrap uses only wrap fee; unwrap only unwrap fee
  • Migrate from single fee_bps + mainnet migrate plan in DEPLOYMENT.md
  • Docs + retune rule + solvency invariant; audit M-4/MB-2 superseded notes
  • No InstantWithdraw gross-up added
  • Operator runbook with exact set_fees / setter JSON
  • Agent skill skills/wrap-mapper-asymmetric-fees + skills README crosslink
  • Unit/integration tests (107) including migrate, path isolation, solvency, pause, dust

Still for human ops / companion (out of this MR):

  • Mainnet store/migrate + gov SetFees 200/51
  • Tiny live wrap/unwrap probes + treasury native ≥ supply check
  • DEX #516 + Coolify frontend redeploy
Implementation MR: https://gitlab.com/PlasticDigits2/ustr-cmm/-/merge_requests/9 Done in MR (code/docs/tests): - [x] state/msgs/query expose `fee_wrap_bps` + `fee_unwrap_bps` - [x] Wrap uses only wrap fee; unwrap only unwrap fee - [x] Migrate from single `fee_bps` + mainnet migrate plan in DEPLOYMENT.md - [x] Docs + retune rule + solvency invariant; audit M-4/MB-2 superseded notes - [x] No InstantWithdraw gross-up added - [x] Operator runbook with exact `set_fees` / setter JSON - [x] Agent skill `skills/wrap-mapper-asymmetric-fees` + skills README crosslink - [x] Unit/integration tests (107) including migrate, path isolation, solvency, pause, dust Still for human ops / companion (out of this MR): - [ ] Mainnet store/migrate + gov `SetFees` 200/51 - [ ] Tiny live wrap/unwrap probes + treasury native ≥ supply check - [ ] DEX #516 + Coolify frontend redeploy
PlasticDigits commented 2026-08-15 09:28:56 +00:00 (Migrated from gitlab.com)

mentioned in commit 1939449360

mentioned in commit 1939449360565cc2fa8b860869d38cc883f1015f
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-08-15 09:28:57 +00:00
PlasticDigits commented 2026-08-15 09:57:08 +00:00 (Migrated from gitlab.com)

mentioned in issue #13

mentioned in issue #13
PlasticDigits commented 2026-08-15 09:57:08 +00:00 (Migrated from gitlab.com)

marked as related to #13

marked as related to #13
PlasticDigits commented 2026-08-15 09:57:15 +00:00 (Migrated from gitlab.com)

Remaining mainnet ops + DEX consumer rollout tracker: https://gitlab.com/PlasticDigits2/ustr-cmm/-/work_items/13

Live columbus-5 still code 11565 / { fee_bps: 200 } as of 2026-08-15. #13 covers store/migrate, SetFees 200/51, probes, and Coolify coordination with DEX #516.

Remaining mainnet ops + DEX consumer rollout tracker: https://gitlab.com/PlasticDigits2/ustr-cmm/-/work_items/13 Live columbus-5 still code `11565` / `{ fee_bps: 200 }` as of 2026-08-15. #13 covers store/migrate, `SetFees` 200/51, probes, and Coolify coordination with [DEX #516](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/516).
PlasticDigits commented 2026-08-15 10:19:46 +00:00 (Migrated from gitlab.com)
mentioned in issue PlasticDigits/cl8y-dex-terraclassic#521
PlasticDigits commented 2026-08-15 10:23:12 +00:00 (Migrated from gitlab.com)

mentioned in commit ed05356800

mentioned in commit ed0535680045977a47f89ab394fb34c24a29226a
PlasticDigits commented 2026-08-15 11:51:28 +00:00 (Migrated from gitlab.com)
mentioned in issue PlasticDigits/cl8y-dex-terraclassic#523
PlasticDigits commented 2026-08-15 12:02:16 +00:00 (Migrated from gitlab.com)

Follow-up: mainnet fees now live (ops were on #13)

columbus-5 wrap-mapper terra1xuuuh…nmd2 is no longer { fee_bps: 200 } / code 11565.

Check Live
Code 11574 (cw2 0.3.0)
Config fee_wrap_bps=200, fee_unwrap_bps=51
Store 9B305800…DA40
Migrate 8F05225E…D107
SetFees 740CB152…BDF3

This issue stays closed (code shipped in !31). Remaining human AC (tiny wrap/unwrap probes, Coolify, DEX router wasm) remains on #13 + DEX #523.

## Follow-up: mainnet fees now live (ops were on #13) columbus-5 wrap-mapper `terra1xuuuh…nmd2` is no longer `{ fee_bps: 200 }` / code `11565`. | Check | Live | |-------|------| | Code | **`11574`** (cw2 `0.3.0`) | | Config | **`fee_wrap_bps=200`, `fee_unwrap_bps=51`** | | Store | [`9B305800…DA40`](https://finder.terraclassic.community/columbus-5/tx/9B30580007763DB44DA215975D25B8046C134A46436217D462354655B5B9DA40) | | Migrate | [`8F05225E…D107`](https://finder.terraclassic.community/columbus-5/tx/8F05225E53D67C3666C1E9B0929EB69A172FA3EDD2F1B750F456995820ACD107) | | `SetFees` | [`740CB152…BDF3`](https://finder.terraclassic.community/columbus-5/tx/740CB152259CE5D02E225064CEEFB09870FD1D07779926391B2E81FA4B5FBDF3) | This issue stays **closed** (code shipped in !31). Remaining human AC (tiny wrap/unwrap probes, Coolify, DEX router wasm) remains on [#13](https://gitlab.com/PlasticDigits2/ustr-cmm/-/issues/13) + [DEX #523](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/523).
PlasticDigits commented 2026-08-15 12:42:56 +00:00 (Migrated from gitlab.com)

mentioned in issue #15

mentioned in issue #15
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/ustr-cmm#9
No description provided.