Skip to content

feat: M012 CosmWasm dynamic-supply contract with unit tests#70

Open
brawlaphant wants to merge 2 commits intoregen-network:mainfrom
brawlaphant:pr/m012-cosmwasm-contract
Open

feat: M012 CosmWasm dynamic-supply contract with unit tests#70
brawlaphant wants to merge 2 commits intoregen-network:mainfrom
brawlaphant:pr/m012-cosmwasm-contract

Conversation

@brawlaphant
Copy link
Copy Markdown
Contributor

Summary

  • Adds a complete CosmWasm smart contract implementing the M012 Fixed Cap Dynamic Supply mechanism at contracts/dynamic-supply/
  • Implements the full supply algorithm from the SPEC: S[t+1] = S[t] + M[t] - B[t] with M[t] = r * (C - S[t]) where r = r_base * effective_multiplier * ecological_multiplier
  • Phase-gated effective multiplier selection integrating with M014 (Inactive/Transition/Active/Equilibrium)
  • Ecological multiplier (v0: disabled, ready for v1 oracle integration)
  • Supply state machine with automatic phase transitions (Transition -> Dynamic -> Equilibrium, with shock reversion)
  • Admin controls for regrowth rate, M014 phase, ecological toggle, equilibrium parameters
  • Query endpoints: supply state, parameters, period history, simulate (dry-run)
  • Follows the same structure, dependency versions, and coding patterns as the M013 fee-router contract
  • Adds target/ and Cargo.lock to .gitignore for Rust build artifacts

Test coverage (29 tests, all passing)

Covers all 20 SPEC acceptance tests:

  • Basic mint/burn arithmetic (AT-1)
  • Cap enforcement / cap inviolability (AT-2, AT-16)
  • Non-negative supply floor (AT-3, AT-17)
  • Staking multiplier range (0%->1.0, 50%->1.5, 100%->2.0) (AT-4)
  • Near-cap deceleration (AT-5)
  • Phase gating: INACTIVE/TRANSITION/ACTIVE (AT-6, AT-7, AT-8)
  • Ecological multiplier disabled/enabled/floored (AT-9, AT-10, AT-11)
  • State machine transitions (AT-12 through AT-15)
  • Parameter bound safety (AT-20)
  • Authorization checks (admin-only for all mutations)
  • Instantiation validation (zero cap, supply > cap, rate > 10%)
  • Multi-period convergence toward cap
  • Supply params and cap headroom queries

Test plan

  • cargo test — 29 tests passing
  • cargo wasm — compiles to wasm32-unknown-unknown
  • Review contract logic against SPEC sections 5.1-5.8
  • Verify compatibility with fee-router contract patterns

🤖 Generated with Claude Code

brawlaphant and others added 2 commits March 31, 2026 15:49
Implements the Fixed Cap Dynamic Supply mechanism (M012 SPEC) as a
CosmWasm smart contract. Covers:

- Hard-capped supply with configurable cap (default 221M REGEN)
- Algorithmic mint (regrowth) from cap headroom: M[t] = r * (C - S[t])
- Phase-gated effective multiplier (staking vs stability, M014 integration)
- Ecological multiplier (v0 disabled, ready for v1 oracle)
- Supply state machine: Transition -> Dynamic -> Equilibrium (with shock reversion)
- Admin controls for regrowth rate, M014 phase, ecological toggle, equilibrium params
- Query endpoints: supply state, params, period history, simulate
- 29 unit tests covering all 20 SPEC acceptance tests and security invariants

Follows the same structure and patterns as the M013 fee-router contract.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements a dynamic supply CosmWasm contract with mint/burn logic driven by staking, stability, and ecological multipliers. It includes administrative controls and comprehensive tests. Feedback focuses on preventing potential panics by using checked arithmetic and ratio operations, and improving maintainability by removing unused error variants and state phases.

Comment on lines +189 to +190
state.total_minted += effective_mint;
state.total_burned += effective_burn;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The cumulative totals total_minted and total_burned are updated using the += operator, which will panic on overflow. While Uint128 has a very large range, it is best practice in CosmWasm to use checked_add to handle potential overflows gracefully, especially for values that accumulate over the lifetime of the contract.

Suggested change
state.total_minted += effective_mint;
state.total_burned += effective_burn;
state.total_minted = state.total_minted.checked_add(effective_mint).map_err(StdError::overflow)?;
state.total_burned = state.total_burned.checked_add(effective_burn).map_err(StdError::overflow)?;

effective_burn - effective_mint
};
let would_reach_equilibrium = diff < params.equilibrium_threshold
&& state.consecutive_equilibrium_periods + 1 >= params.equilibrium_periods_required;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The addition state.consecutive_equilibrium_periods + 1 could theoretically overflow if consecutive_equilibrium_periods reaches u64::MAX. Although this is extremely unlikely in practice, using checked_add is a safer pattern for state-dependent calculations.

Suggested change
&& state.consecutive_equilibrium_periods + 1 >= params.equilibrium_periods_required;
&& state.consecutive_equilibrium_periods.checked_add(1).unwrap_or(u64::MAX) >= params.equilibrium_periods_required;

Comment on lines +556 to +557
let ratio = Decimal::from_ratio(staked, current_supply);
let raw = Decimal::one() + ratio;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Decimal::from_ratio will panic if the resulting ratio exceeds the maximum value of a Decimal (approx 3.4e20). If staked is significantly larger than current_supply, this could cause a panic. Since the result is clamped to MAX_MULTIPLIER (2.0) anyway, checking if staked >= current_supply first avoids the risk of overflow.

    if staked >= current_supply {
        return MAX_MULTIPLIER;
    }
    let ratio = Decimal::from_ratio(staked, current_supply);
    let raw = Decimal::one() + ratio;

Comment on lines +570 to +571
let ratio = Decimal::from_ratio(stability_committed, current_supply);
let raw = Decimal::one() + ratio;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to the staking multiplier, Decimal::from_ratio can panic if stability_committed is much larger than current_supply. Checking the bounds before calculation prevents potential panics.

    if stability_committed >= current_supply {
        return MAX_MULTIPLIER;
    }
    let ratio = Decimal::from_ratio(stability_committed, current_supply);
    let raw = Decimal::one() + ratio;

Comment on lines +589 to +594
let ratio = delta / reference_value;
if ratio >= Decimal::one() {
Decimal::zero()
} else {
Decimal::one() - ratio
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The division delta / reference_value can panic if the result exceeds the capacity of a Decimal. It is safer to check if delta >= reference_value first, which also simplifies the logic since the multiplier is floored at zero.

Suggested change
let ratio = delta / reference_value;
if ratio >= Decimal::one() {
Decimal::zero()
} else {
Decimal::one() - ratio
}
if delta >= reference_value {
Decimal::zero()
} else {
let ratio = delta / reference_value;
Decimal::one() - ratio
}

Comment on lines +21 to +41
#[error("Burn amount must be greater than zero")]
ZeroBurnAmount {},

#[error("Mint amount must be greater than zero")]
ZeroMintAmount {},

#[error("Ecological reference value must be greater than zero")]
ZeroReferenceValue {},

#[error("Invalid M014 phase: {phase}")]
InvalidPhase { phase: String },

#[error("Mint would exceed hard cap (supply {supply} + mint {mint} > cap {cap})")]
MintExceedsCap {
supply: String,
mint: String,
cap: String,
},

#[error("Burn amount {burn} exceeds current supply {supply}")]
BurnExceedsSupply { burn: String, supply: String },
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Several error variants (ZeroBurnAmount, ZeroMintAmount, InvalidPhase, MintExceedsCap, BurnExceedsSupply) are defined but never used in the contract implementation. Removing unused code improves maintainability and reduces contract size.

#[cw_serde]
pub enum SupplyPhase {
/// Legacy inflationary PoS (before M012 activation)
Inflationary,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The Inflationary variant of SupplyPhase is defined but never used in the contract logic. The contract is initialized in the Transition phase. If this variant is not intended for future use, it should be removed to improve code clarity.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant