test(integration): M013 fee-router → M012 dynamic-supply burn flow#102
Conversation
…o workspace Repairs two pre-existing CI breakages that have been blocking every PR against main: 1. `cargo clippy --workspace -- -D warnings` failed on Rust 1.94 with 17 errors across 6 contracts — pre-existing lint issues from before the toolchain auto-upgraded. Fixed mechanically with no behavior change. 2. `reputation-signal`, `dynamic-supply`, and `fee-router` were present as sibling package directories under `contracts/` but NOT listed in `contracts/Cargo.toml`'s `workspace.members` array. The packages were in limbo — cargo refused to run their tests standalone (refusing with "current package believes it's in a workspace when it's not"), AND they were invisible to workspace-scoped commands like `cargo test --workspace` and `cargo build --release --target wasm32-unknown-unknown --workspace`. The result was that ~80 unit tests (38 in reputation-signal, 29 in dynamic-supply, 13 in fee-router) were running nowhere. This PR adds them to the workspace. Combined impact: - Contracts CI clippy job: FAIL → PASS - Contracts CI test job: 98 tests → 178 tests (+80) - Contracts CI wasm build job: 6 contracts → 9 contracts (+3) - Future integration tests can now depend on the three previously-orphaned contracts (they were already valid CosmWasm contracts, just not visible to the workspace) ## Clippy fixes (mechanical, no behavior change) attestation-bonding/src/contract.rs - Line 545, 571: `start_after.map(|s| cw_storage_plus::Bound::exclusive(s))` → `start_after.map(cw_storage_plus::Bound::exclusive)` (redundant_closure) - Line 552, 553, 576: `.map_or(true, |x| ...)` → `.is_none_or(|x| ...)` (unnecessary_map_or — modern Rust idiom) credit-class-voting/src/contract.rs - Line 560: `execute_update_config` has 8 parameters; added `#[allow(clippy::too_many_arguments)]` above the fn declaration (all 8 are legitimately independent optional fields on the config) - Line 653, 680: redundant closure on `Bound::exclusive` contribution-rewards/src/contract.rs - Line 390: `execute_record_activity` has 8 parameters; added `#[allow(clippy::too_many_arguments)]` marketplace-curation/src/contract.rs - Line 847: redundant closure on `Bound::exclusive` - Line 863: `&coll.curator != c` comparing a reference to a reference → `coll.curator != *c` (op_ref on left operand) service-escrow/src/contract.rs - Line 767: `execute_update_config` has 11 parameters; added `#[allow(clippy::too_many_arguments)]` - Line 916: `value < MIN_BOND_RATIO || value > MAX_BOND_RATIO` → `!(MIN_BOND_RATIO..=MAX_BOND_RATIO).contains(&value)` (manual_range_contains — modern Rust idiom) reputation-signal/src/contract.rs - Line 162: `exec_submit_signal` has 8 parameters; added `#[allow(clippy::too_many_arguments)]` - Line 173: `endorsement_level < 1 || endorsement_level > 5` → `!(1..=5).contains(&endorsement_level)` (manual_range_contains) - Line 537: `exec_update_config` has 9 parameters; added `#[allow(clippy::too_many_arguments)]` - Line 632: `config.arbiters.retain(|a| a != &addr)` → `config.arbiters.retain(|a| *a != addr)` (op_ref on right operand) ## Workspace wiring contracts/Cargo.toml: added `"dynamic-supply"`, `"fee-router"`, `"reputation-signal"` to `workspace.members` in alphabetical order between existing entries. No change to `workspace.dependencies`. ## Validation Ran locally on `rustc 1.94.0 (85eff7c80 2026-01-15)` with the same commands CI uses: - `cargo clippy --workspace -- -D warnings` — PASS (0 errors) - `cargo test --workspace` — 178 passed, 0 failed - `cargo build --release --target wasm32-unknown-unknown --workspace` — all 9 contracts compile, release profile, ~55s cold Every behavior change in this PR is a mechanical refactor that the Rust compiler can prove equivalent (clippy's suggestions are peephole transformations). No state machine, no fee math, no access control was touched. - Lands in: `contracts/` - Changes: fix 17 clippy errors + add 3 orphaned contracts to workspace.members - Validate: `cd contracts && cargo test --workspace && cargo clippy --workspace -- -D warnings`
Adds a new integration test that exercises the canonical Economic Reboot flow: fees are collected by M013 fee-router, the burn portion accumulates in its burn pool, and an off-chain aggregator hands that burn amount to M012 dynamic-supply as the burn_amount argument to ExecutePeriod. The two contracts do not message each other on-chain in v0 — but their data contract at the boundary must be bit-exact. This is now possible because PR regen-network#90 wired both fee-router and dynamic-supply into the workspace members list. Before that, they could not be imported by integration-tests. ## What this test pins 1. **Workspace coexistence** — both contracts deploy in the same App and instantiate without collisions. 2. **Fee Conservation inside m013** — after collecting a fee, the burn share lands in `burn_pool` and matches the dry-run calculation from `CalculateFee`. Specifically: burn + validator + community + agent == fee_amount A 10B uregen marketplace trade at the default 1% rate produces fee 100M, split 30M/40M/25M/5M (pins the Model A 30/40/25/5 distribution). 3. **Supply conservation inside m012** — after ExecutePeriod runs: current_supply = supply_before + total_minted - total_burned The test asserts this identity directly rather than hardcoding exact supply numbers (which would pin the regrowth math as a side effect). The identity must hold regardless of the effective multiplier's numerical value. 4. **m013 → m012 hand-off is bit-exact** — the burn amount m012 records via `total_burned` equals the burn amount we read from m013's `pools.burn_pool`. No off-by-one, no rounding drift. This is the single most important assertion in the test — if the two contracts' uregen accounting ever drifts, the Economic Reboot burn ladder breaks silently. ## What this test does NOT do It is not a cross-contract MESSAGE flow — m012 does not call m013 and vice versa. The hand-off is off-chain in v0. A future upgrade that adds a real IBC or Wasm-level query can extend this test to cover that path; for now the test guards the workspace wiring and the uregen data contract at the boundary. ## Cargo.toml change Adds `fee-router` and `dynamic-supply` to `contracts/integration-tests/Cargo.toml` under `[dev-dependencies]`. Both contracts were already in the workspace members list after PR regen-network#90. ## Validation $ cd contracts && cargo test --package integration-tests \ test_fee_router_to_dynamic_supply_burn_flow test result: ok. 1 passed; 0 failed The full workspace test suite still passes — 180 tests total after this PR (179 after regen-network#101, +1 from this test). - Lands in: `contracts/integration-tests/` - Changes: new M013→M012 burn flow test (222 LOC) + Cargo.toml deps - Validate: `cd contracts && cargo test --workspace` ## PR relationship Based on PR regen-network#90 (which wired fee-router and dynamic-supply into the workspace members list). Sibling to regen-network#101 (M010/M011 coexistence test). The two integration tests together exercise two of the three conceptual flows in the Economic Reboot design — with the third (M009 escrow → M015 rewards) deferred to a future follow-up. Refs `mechanisms/m013-value-based-fee-routing/SPEC.md` §5 Refs `mechanisms/m012-fixed-cap-dynamic-supply/SPEC.md` §5.3
There was a problem hiding this comment.
Code Review
This pull request integrates new contract modules—dynamic-supply, fee-router, and reputation-signal—into the workspace and adds a comprehensive integration test for the fee-to-burn flow. It also includes several idiomatic Rust refactorings, such as using is_none_or and range contains, and suppresses clippy warnings for functions with many arguments. A review comment suggests enhancing the integration test by including actual tokens in the CollectFee call to ensure the contract's fee validation logic is properly exercised.
| app.execute_contract( | ||
| admin.clone(), | ||
| fr_addr.clone(), | ||
| &fee_router::msg::ExecuteMsg::CollectFee { | ||
| tx_type: fee_router::msg::TxType::MarketplaceTrade, | ||
| value: trade_value, | ||
| }, | ||
| &[], | ||
| ) | ||
| .unwrap(); |
There was a problem hiding this comment.
The CollectFee execution is called with an empty funds array (&[]). If the fee-router contract is intended to manage real token balances (as implied by the PoolBalances query and the PR description), this call should likely include the actual fee tokens. If the test passes without sending funds, it suggests that the fee-router contract might be missing a validation check to ensure that the tokens sent match the calculated fee, which could be a security concern in the contract itself.
| app.execute_contract( | |
| admin.clone(), | |
| fr_addr.clone(), | |
| &fee_router::msg::ExecuteMsg::CollectFee { | |
| tx_type: fee_router::msg::TxType::MarketplaceTrade, | |
| value: trade_value, | |
| }, | |
| &[], | |
| ) | |
| .unwrap(); | |
| app.execute_contract( | |
| admin.clone(), | |
| fr_addr.clone(), | |
| &fee_router::msg::ExecuteMsg::CollectFee { | |
| tx_type: fee_router::msg::TxType::MarketplaceTrade, | |
| value: trade_value, | |
| }, | |
| &[Coin::new(calc.fee_amount.u128(), DENOM)], | |
| ) | |
| .unwrap(); |
Addresses Gemini review feedback on PR regen-network#102: the CollectFee call passed `&[]` for funds, which both hid the intended integrator call shape and masked any future on-chain funds-validation check. Wire `calc.fee_amount` through the mock bank as `Coin { denom: DENOM, amount: calc.fee_amount }` and annotate that v0 m013 is accounting-only so the funds currently just sit in the contract — but the test will fail closed the day m013 enforces that `info.funds == calculated_fee`. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
Adds a new integration test that exercises the canonical Economic Reboot flow: fees are collected by M013 fee-router, the burn portion accumulates in its burn pool, and an off-chain aggregator hands that burn amount to M012 dynamic-supply as the `burn_amount` argument to `ExecutePeriod`. The two contracts do not message each other on-chain in v0 — but their data contract at the boundary must be bit-exact.
Now possible because #90 wired both contracts into the workspace members list. Before that, they couldn't be imported by `integration-tests`.
What this test pins
The bit-exactness assertion is the single most important one in the test — if the two contracts' uregen accounting ever drifts, the Economic Reboot burn ladder breaks silently.
What this test does NOT do
Not a cross-contract MESSAGE flow — m012 does not call m013 and vice versa. The hand-off is off-chain in v0. A future upgrade that adds a real IBC or Wasm-level query can extend this test to cover that path; for now the test guards the workspace wiring and the uregen data contract at the boundary.
Validation
```
$ cd contracts && cargo test --package integration-tests test_fee_router_to_dynamic_supply_burn_flow
test result: ok. 1 passed; 0 failed
$ cd contracts && cargo test --workspace
(180 tests passed — +1 after #101 which added the M010/M011 test)
```
PR relationship
Based on #90 (contracts CI fix — where `fee-router` and `dynamic-supply` were added to workspace members). Sibling to #101 (M010 reputation-signal coexistence with M011 curation). The two integration tests together exercise two of the three conceptual flows in the Economic Reboot design. The third (M009 escrow → M015 rewards) is a future follow-up.
Refs `mechanisms/m013-value-based-fee-routing/SPEC.md` §5
Refs `mechanisms/m012-fixed-cap-dynamic-supply/SPEC.md` §5.3