diff --git a/README.md b/README.md index 945e9db..2653814 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ This repository contains the design specifications and implementation plans for - [m010 — Reputation Signal (v0 advisory)](mechanisms/m010-reputation-signal/) +- [m012 — Fixed Cap Dynamic Supply](mechanisms/m012-fixed-cap-dynamic-supply/) diff --git a/docs/MECHANISM_CONSUMERS.md b/docs/MECHANISM_CONSUMERS.md index abd7745..f43873f 100644 --- a/docs/MECHANISM_CONSUMERS.md +++ b/docs/MECHANISM_CONSUMERS.md @@ -18,3 +18,23 @@ This document maps **mechanism IDs** to known **consumers** (agents, digests, sc - Heartbeat replay runner: `scripts/replay-m010.mjs` (regen-heartbeat) - Heartbeat stub runner: `scripts/stub-run-signal-agent.mjs` (regen-heartbeat) - Heartbeat validator: `scripts/validate-signal-agent.mjs` (regen-heartbeat) + +## m012 — Fixed Cap Dynamic Supply +**Canonical spec** +- `mechanisms/m012-fixed-cap-dynamic-supply/SPEC.md` + +**Outputs** +- KPI JSON block schema: `mechanisms/m012-fixed-cap-dynamic-supply/schemas/m012_kpi.schema.json` +- Supply state schema: `mechanisms/m012-fixed-cap-dynamic-supply/schemas/m012_supply_state.schema.json` +- Period record schema: `mechanisms/m012-fixed-cap-dynamic-supply/schemas/m012_period_record.schema.json` + +**Reference implementation** +- Supply period computation: `mechanisms/m012-fixed-cap-dynamic-supply/reference-impl/m012_supply.js` (`computeSupplyPeriod`) +- KPI computation: `mechanisms/m012-fixed-cap-dynamic-supply/reference-impl/m012_kpi.js` (`computeM012KPI`) + +**Datasets (deterministic)** +- Replay fixtures: `mechanisms/m012-fixed-cap-dynamic-supply/datasets/fixtures/v0_sample.json` +- Equilibrium fixtures: `mechanisms/m012-fixed-cap-dynamic-supply/datasets/fixtures/v0_equilibrium_sample.json` + +**Known consumers** +- Reference self-test: `node mechanisms/m012-fixed-cap-dynamic-supply/reference-impl/m012_supply.js` diff --git a/mechanisms/m012-fixed-cap-dynamic-supply/README.md b/mechanisms/m012-fixed-cap-dynamic-supply/README.md new file mode 100644 index 0000000..81431ce --- /dev/null +++ b/mechanisms/m012-fixed-cap-dynamic-supply/README.md @@ -0,0 +1,31 @@ +# m012 — Fixed Cap Dynamic Supply + +m012 replaces the current inflationary proof-of-stake supply model with a **hard-capped, algorithmically managed supply** that ties token minting and burning to real ecological activity on the network. Inspired by Blockscience's carrying capacity model and Ethereum's EIP-1559 burn mechanics. + +## What it does +- Enforces a **hard cap** of 221,000,000 REGEN (221,000,000,000,000 uregen). +- Computes per-period **regrowth** (minting): `M[t] = r * (C - S[t])`, where `r` is dynamically adjusted based on staking participation and ecological metrics. +- Consumes per-period **burns** from M013 fee routing: `B[t] = sum(burn_share * fee)`. +- Updates supply: `S[t+1] = S[t] + M[t] - B[t]`. +- Approaches **dynamic equilibrium** where minting equals burning. + +## What it does not do (v0) +- No ecological multiplier oracle integration (set to 1.0 / disabled). +- No on-chain enforcement (reference computation only). +- No per-block granularity (operates per-epoch). + +## How to reference +- Canonical spec: `mechanisms/m012-fixed-cap-dynamic-supply/SPEC.md` +- Reference implementation: `reference-impl/m012_supply.js` +- KPI computation: `reference-impl/m012_kpi.js` + +## Replay datasets +See `datasets/` for deterministic fixtures used to generate non-zero KPI outputs without MCP. +- `v0_sample.json` — 5 periods of supply state transitions +- `v0_equilibrium_sample.json` — periods approaching equilibrium (mint ~ burn) + +## Schemas +Canonical JSON schemas for m012 outputs live in `schemas/`. +- `m012_supply_state.schema.json` — supply state (current supply, cap, staked amounts, multiplier config) +- `m012_period_record.schema.json` — per-period mint/burn record +- `m012_kpi.schema.json` — KPI output with mechanism_id const "m012" diff --git a/mechanisms/m012-fixed-cap-dynamic-supply/SPEC.md b/mechanisms/m012-fixed-cap-dynamic-supply/SPEC.md new file mode 100644 index 0000000..a1f9295 --- /dev/null +++ b/mechanisms/m012-fixed-cap-dynamic-supply/SPEC.md @@ -0,0 +1,273 @@ +# m012 — Fixed Cap Dynamic Supply (SPEC) + +## 0. Header +- **ID:** m012 +- **Name:** Fixed Cap Dynamic Supply +- **Status:** draft +- **Owner:** (unset) +- **Last updated:** 2026-02-18 +- **Scope:** Protocol-level supply management; replaces inflationary PoS minting with hard-capped, algorithmically managed mint/burn + +## 1. Problem +The current Regen Network supply model is inflationary proof-of-stake: validators are compensated through continuous token minting, which dilutes holders and provides no upper bound on supply. This creates long-term policy uncertainty and misaligns token economics with ecological outcomes. The network needs a supply model that provides both **long-term monetary policy certainty** (like gold's fixed supply) and **adaptive flexibility** (like managed fiat), tied to real ecological activity. + +## 2. Target actor and action +- **Actors:** the Network (protocol-level supply controller), Fee Payers (source of burn input via M013), Validators (receive compensation from fee revenue, not inflation), Governance (token holders adjusting supply parameters). +- **Action being evaluated (one action):** per-period **algorithmic supply adjustment** via mint (regrowth) and burn (fee consumption). +- **Event source:** epoch-level supply computation executed by the `x/supply` module (or parameter update to `x/mint`). + +## 3. Signal definition +- **Signal name:** Supply State +- **Unit:** uregen (1 REGEN = 1,000,000 uregen) +- **Directionality:** supply trends toward dynamic equilibrium where minting equals burning +- **Granularity:** per-period (1 epoch, ~7 days) +- **Key outputs:** current supply, minted amount, burned amount, regrowth rate, effective multiplier, cap headroom + +## 4. Evidence inputs + +| Input | Source | Fields | Validity rules | Anti-spoof assumptions | Refresh cadence | +|---|---|---|---|---|---| +| Current supply | Chain state (`x/supply` or `x/bank`) | `current_supply` (uregen) | Must be non-negative and <= hard_cap | Read from chain state (not self-reported) | Per period | +| Staked amount | Chain staking module | `staked_amount` (uregen) | Must be non-negative and <= current_supply | Read from chain state | Per period | +| Burn amount | M013 fee routing | `burn_amount` (uregen) | Sum of `burn_share * fee` for all transactions in period | Computed from on-chain fee events | Per period | +| Stability commitments | M015 (via M014) | `stability_committed` (uregen) | Non-negative; only used when M014 state != INACTIVE | Read from M015 module state | Per period | +| M014 state | M014 module | `m014_state` enum | One of: INACTIVE, TRANSITION, ACTIVE, EQUILIBRIUM | Read from M014 module state | Per period | +| Ecological metric | External oracle (v1) | `delta_co2` (ppm) | Non-negative; v0 uses 1.0 (disabled) | Oracle integration required; v0 disabled | Per period (when enabled) | + +## 5. Scoring function + +### 5.1 Supply algorithm + +``` +S[t+1] = S[t] + M[t] - B[t] + +where: + S[t] = circulating supply at period t (uregen) + M[t] = tokens minted in period t (regrowth) (uregen) + B[t] = tokens burned in period t (from fees via M013) (uregen) + C = hard cap (221,000,000,000,000 uregen = 221,000,000 REGEN) +``` + +### 5.2 Minting (regrowth) + +``` +M[t] = r * (C - S[t]) + +where r = regrowth rate, dynamically adjusted: + r = r_base * effective_multiplier * ecological_multiplier + + r_base = 0.02 (2% base regrowth rate per period) +``` + +### 5.3 Effective multiplier (phase-gated) + +The effective multiplier depends on the M014 module state: + +``` +staking_multiplier = clamp(1 + (S_staked / S_total), 1.0, 2.0) + +stability_multiplier = clamp(1 + (S_stability_committed / S_total), 1.0, 2.0) + +Phase-gated behavior: + - if m014.state == INACTIVE: effective_multiplier = staking_multiplier + - if m014.state == TRANSITION: effective_multiplier = max(staking_multiplier, stability_multiplier) + - if m014.state in {ACTIVE, EQUILIBRIUM}: effective_multiplier = stability_multiplier +``` + +The `max()` selection during TRANSITION prevents a regrowth discontinuity as `S_staked` declines during the unbonding period while `S_stability_committed` ramps up. + +### 5.4 Ecological multiplier + +``` +ecological_multiplier = max(0, 1 - (delta_co2 / reference_value)) + range: [0.0, 1.0+] + reference_value = 50 ppm (configurable) +``` + +- Improving ecological metrics increase regrowth; worsening metrics slow regrowth toward zero. +- Floored at 0 to prevent negative minting. Supply contraction occurs exclusively through burning. +- **v0:** ecological_multiplier = 1.0 (disabled) until reliable oracle integration exists. + +### 5.5 Burning + +``` +B[t] = sum of burn_share * fee for all fee-generating transactions in period t +``` + +Burn routing is defined in M013. This module receives the aggregated burn amount per period. + +### 5.6 Key properties + +- As `S[t]` approaches `C`, minting naturally decelerates toward zero. +- Supply can only increase through minting; supply can only decrease through burning. +- System reaches equilibrium when `M[t] = B[t]`. + +### 5.7 Normalization + +All supply values are in uregen (1 REGEN = 1,000,000 uregen). The regrowth rate `r` is a dimensionless multiplier applied to the headroom `(C - S[t])`. + +### 5.8 Controls + +- `hard_cap`: absolute upper bound on supply (Layer 4 constitutional governance). +- `r_base`: base regrowth rate, bounded to `[0, 0.10]` (Layer 3). +- `ecological_multiplier_enabled`: boolean toggle (Layer 3). +- `min_burn_rate`: safety floor for burn rate (Layer 2). +- `period_length`: epoch cadence for mint/burn cycles (Layer 2). + +## 6. State machine + +``` +States: {INFLATIONARY, TRANSITION, DYNAMIC, EQUILIBRIUM} + +INFLATIONARY -> TRANSITION + trigger: governance.approve(m012_activation_proposal) + guard: M013 fee routing active, M014 PoA active + action: set hard_cap, disable inflation module, enable mint/burn module + +TRANSITION -> DYNAMIC + trigger: first_burn_period_complete AND validators_compensated + guard: fee_revenue > 0, burn_executed + action: begin algorithmic mint/burn cycles + +DYNAMIC -> EQUILIBRIUM + trigger: abs(M[t] - B[t]) < threshold for N consecutive periods + guard: N >= 12 (~3 months of near-balance at ~7-day periods) + action: log equilibrium_reached, reduce monitoring frequency + note: equilibrium is not permanent -- external shocks can return to DYNAMIC + +EQUILIBRIUM -> DYNAMIC + trigger: abs(M[t] - B[t]) >= threshold + guard: deviation persists for >= 1 period + action: resume full monitoring frequency +``` + +## 7. Economic linkage + +This mechanism directly manages the monetary supply of REGEN: + +- **Minting** distributes new tokens into circulation as regrowth, replacing inflationary PoS rewards. +- **Burning** permanently removes tokens from circulation, funded by value-based fees (M013). +- **Validator compensation** shifts from inflation-funded to fee-revenue-funded (via M014). +- **Equilibrium** represents the steady state where network activity (fees/burns) matches regrowth, creating a self-sustaining economic loop. + +**Carrying capacity metaphor:** the hard cap mirrors nature's carrying capacity -- an upper limit for population within an ecosystem. Supply can contract and expand within the cap but never exceed it. + +## 8. On-chain vs off-chain boundary + +- **On-chain:** supply state storage (`SupplyState`), per-period mint/burn execution, parameter governance, cap enforcement. +- **Off-chain (v0):** KPI computation, equilibrium monitoring, digest reporting, ecological multiplier oracle integration (when enabled). +- **Events:** `EventSupplyMint`, `EventSupplyBurn`, `EventParameterUpdate`, `EventEquilibriumReached`. +- **Storage:** `SupplyState` (current supply, cap, parameters), `MintBurnRecord` (per-period history). + +## 9. Attack model + +- **Cap violation:** invariant enforcement at protocol level; no transaction may cause `S[t] > C`. +- **Runaway minting:** `r_base` is bounded to `[0, 0.10]`; effective multiplier capped at 2.0; ecological multiplier capped at 1.0+. Maximum possible `r` is `0.10 * 2.0 * 1.0 = 0.20`. +- **Burn manipulation:** burn amounts are derived from M013 fee routing, which has its own anti-gaming measures. Mint and burn are computed independently (Invariant 4). +- **Oracle manipulation (v1):** ecological multiplier oracle could be gamed to inflate regrowth. Mitigated by: v0 disables ecological multiplier; v1 requires governance-approved oracle sources; floor at 0 prevents negative minting. +- **Governance capture:** `hard_cap` changes require Layer 4 (67% supermajority); `r_base` changes require Layer 3 (community deliberation). No single actor can unilaterally change supply parameters. +- **Supply oscillation:** the regrowth formula inherently dampens oscillation -- as supply approaches cap, minting decelerates; as supply drops, minting accelerates. + +## 10. Integration points + +- **M013 (Value-Based Fee Routing):** provides the per-period burn amount `B[t]`. M012 consumes the aggregated burn total. +- **M014 (Authority Validator Governance):** determines the phase gate for effective multiplier selection. M012 reads `m014.state` to choose between staking and stability multipliers. +- **M015 (Contribution Rewards):** provides `S_stability_committed` for the stability multiplier computation (post-M014 activation). +- **Staking module:** provides `S_staked` for the staking multiplier computation (pre-M014 or during transition). +- **Bank module:** provides `S_total` (current circulating supply). +- **Governance module:** parameter updates for `hard_cap`, `r_base`, `ecological_multiplier_enabled`, etc. +- **KOI MCP (knowledge):** digest publication, equilibrium monitoring reports. +- **Ledger MCP (chain data):** supply state queries, mint/burn event history. + +## 11. Acceptance tests + +**Supply algorithm:** +1) **Basic mint/burn:** given a supply state with known staking ratio, compute `M[t]` and `B[t]`; verify `S[t+1] = S[t] + M[t] - B[t]`. +2) **Cap enforcement:** if `S[t] + M[t] - B[t] > C`, then `S[t+1] = C` (cap inviolability). +3) **Non-negative supply:** if `B[t] > S[t] + M[t]`, then `S[t+1] = 0` (non-negative supply). +4) **Staking multiplier range:** with 0% staked, multiplier = 1.0; with 100% staked, multiplier = 2.0; with 50% staked, multiplier = 1.5. +5) **Near-cap deceleration:** when supply is at 99% of cap, minted amount is very small (1% of headroom * rate). + +**Phase gating (M014 integration):** +6) **INACTIVE phase:** only staking_multiplier is used regardless of stability_committed. +7) **TRANSITION phase:** effective_multiplier = max(staking, stability); larger value wins. +8) **ACTIVE phase:** only stability_multiplier is used; staking_multiplier is ignored. + +**Ecological multiplier:** +9) **Disabled (v0):** ecological_multiplier = 1.0 when disabled; no effect on regrowth rate. +10) **Enabled (v1):** with delta_co2 = 25 ppm and reference = 50 ppm, ecological_multiplier = 0.5. +11) **Floor:** with delta_co2 = 100 ppm and reference = 50 ppm, ecological_multiplier = 0 (not negative). + +**State machine:** +12) **INFLATIONARY to TRANSITION:** requires M013 active and M014 active. +13) **TRANSITION to DYNAMIC:** requires first burn period complete and fee revenue > 0. +14) **DYNAMIC to EQUILIBRIUM:** requires 12 consecutive periods of near-balance. +15) **EQUILIBRIUM to DYNAMIC:** external shock returns to DYNAMIC state. + +**Security invariants:** +16) **Cap inviolability:** `S[t] <= hard_cap` at all times. +17) **Non-negative supply:** `S[t] >= 0` at all times. +18) **Monotonic cap:** `hard_cap` cannot be changed without Layer 4 governance. +19) **Mint-burn independence:** minting and burning are computed independently. +20) **Parameter bound safety:** `r_base` is bounded to `[0, 0.10]`. + +## 12. Rollout plan + +### v0 checklist +- Define supply parameters (`hard_cap`, `r_base`, `period_length`). +- Implement reference computation for `computeSupplyPeriod` (this spec). +- Disable ecological multiplier (set to 1.0). +- Implement KPI computation and digest reporting. +- Validate with test vectors covering normal, near-cap, high-staking, and large-burn scenarios. +- Integrate with M013 burn routing (consume aggregated burn amounts). +- Integrate with M014 phase gate (read `m014.state` for multiplier selection). + +### Optional v1 outline (non-binding) +- Deploy `x/supply` module or migrate `x/mint` parameters on Regen Ledger. +- Enable ecological multiplier with governance-approved oracle source. +- Implement on-chain equilibrium detection and state transitions. +- Add governance proposal types for parameter updates. +- Implement per-block granularity (EIP-1559 style) if WG resolves OQ-M012-3. + +## 13. Governance parameters + +| Parameter | Initial Value | Governance Authority | Rationale | +|-----------|--------------|---------------------|-----------| +| `hard_cap` | 221,000,000 REGEN (221,000,000,000,000 uregen) | Layer 4 (Constitutional) | Fundamental monetary policy; requires 67% supermajority | +| `base_regrowth_rate` | 0.02 (2%) | Layer 3 (Human-in-Loop) | Significant economic impact; needs community deliberation | +| `staking_multiplier_enabled` | true | Layer 3 | Affects incentive structure | +| `ecological_multiplier_enabled` | false (v0) | Layer 3 | Requires oracle dependency; enable when ready | +| `ecological_reference_value` | 50 ppm | Layer 3 | Ecological sensitivity parameter | +| `min_burn_rate` | 0 | Layer 2 (Agentic + Oversight) | Safety floor; can be adjusted operationally | +| `period_length` | 1 epoch (~7 days) | Layer 2 | Operational cadence | + +--- + +## Appendix A -- Security Invariants + +1. **Cap Inviolability**: `S[t] <= hard_cap` at all times; no transaction may cause supply to exceed cap. +2. **Non-Negative Supply**: `S[t] >= 0`; burn cannot reduce supply below zero. +3. **Monotonic Cap**: `hard_cap` can only be changed via Layer 4 constitutional governance (67% supermajority). +4. **Mint-Burn Independence**: Minting and burning are computed independently; neither can block the other. +5. **Parameter Bound Safety**: `r_base` is in `[0, 0.10]`; regrowth rate bounded to prevent runaway minting. + +## Appendix B -- Open Questions (for WG Resolution) + +> **OQ-M012-1**: The exact hard cap value. Token-economics-synthesis says "~221M" based on current total supply (~224M). Should the cap be set at current total supply, slightly below (to create immediate scarcity), or at a round number? + +> **OQ-M012-2**: The ecological multiplier oracle. What data source provides delta_co2 or equivalent ecological metric? Is this sourced from on-chain attestation data (M008) or from an external oracle? The v0 spec disables this until resolved. + +> **OQ-M012-3**: Period length for mint/burn cycles. Is per-epoch (weekly) the right cadence, or should it be per-block (like EIP-1559) for finer granularity? + +> **OQ-M012-4**: Should burned tokens be permanently destroyed or sent to a reserve pool that can be re-minted under governance control? + +> **OQ-M012-5**: Should the staking_multiplier be replaced by a stability_multiplier (from M015 commitments) or a validator_participation_multiplier (from M014 active set health)? + +## Appendix C -- Source anchors + +- `phase-2/2.6-economic-reboot-mechanisms.md` section "M012 -- Fixed Cap Dynamic Supply" +- [Fixed Cap, Dynamic Supply (forum/34)](https://forum.regen.network/t/fixed-cap-dynamic-supply/34) +- [Economic Reboot Roadmap v0.1 (forum/567)](https://forum.regen.network/t/regen-economic-reboot-roadmap-v0-1/567) +- [Token Economics Synthesis](../docs/economics/token-economics-synthesis.md) +- Blockscience carrying capacity model (referenced in design philosophy) +- Ethereum EIP-1559 burn mechanics (referenced in design philosophy) diff --git a/mechanisms/m012-fixed-cap-dynamic-supply/datasets/README.md b/mechanisms/m012-fixed-cap-dynamic-supply/datasets/README.md new file mode 100644 index 0000000..d31ea0b --- /dev/null +++ b/mechanisms/m012-fixed-cap-dynamic-supply/datasets/README.md @@ -0,0 +1,24 @@ +# m012 datasets (replay fixtures) + +These fixtures are **deterministic inputs** for generating non-zero m012 KPI outputs **without MCP**. + +## Files +- `schema.json` -- JSON schema for replay datasets +- `fixtures/v0_sample.json` -- 5 periods of supply state transitions (varying staking, standard burn) +- `fixtures/v0_equilibrium_sample.json` -- periods approaching equilibrium where minting approximately equals burning + +## How they are used +A replay runner (e.g., in `regen-heartbeat`) can read a fixture file and compute: +- Per-period supply changes using `computeSupplyPeriod` from `reference-impl/m012_supply.js` +- Aggregated KPIs using `computeM012KPI` from `reference-impl/m012_kpi.js` + +Key metrics produced: +- `current_supply` -- circulating supply at end of evaluated periods (uregen) +- `cap_headroom` -- remaining capacity before hard cap (uregen) +- `total_minted`, `total_burned` -- aggregate mint/burn over all periods +- `equilibrium_status` -- whether minting approximately equals burning + +## Units +All token amounts are in **uregen** (1 REGEN = 1,000,000 uregen) and represented as strings to preserve BigInt precision. + +These datasets are **reference-only** and do not imply enforcement or on-chain actions. diff --git a/mechanisms/m012-fixed-cap-dynamic-supply/datasets/fixtures/v0_equilibrium_sample.json b/mechanisms/m012-fixed-cap-dynamic-supply/datasets/fixtures/v0_equilibrium_sample.json new file mode 100644 index 0000000..cb1d147 --- /dev/null +++ b/mechanisms/m012-fixed-cap-dynamic-supply/datasets/fixtures/v0_equilibrium_sample.json @@ -0,0 +1,74 @@ +{ + "mechanism_id": "m012", + "scope": "v0_equilibrium_replay", + "as_of": "2026-02-18T12:00:00Z", + "description": "Periods approaching equilibrium where minting approximately equals burning", + "config": { + "hard_cap": "221000000000000", + "r_base": 0.02, + "ecological_multiplier": 1.0, + "ecological_multiplier_enabled": false + }, + "periods": [ + { + "period": 0, + "timestamp": "2026-06-01T00:00:00Z", + "supply_state": { + "current_supply": "210000000000000", + "staked_amount": "105000000000000", + "stability_committed": "0", + "m014_state": "INACTIVE", + "ecological_multiplier_enabled": false + }, + "burn_amount": "320000000000" + }, + { + "period": 1, + "timestamp": "2026-06-08T00:00:00Z", + "supply_state": { + "current_supply": "210010000000000", + "staked_amount": "105005000000000", + "stability_committed": "0", + "m014_state": "INACTIVE", + "ecological_multiplier_enabled": false + }, + "burn_amount": "325000000000" + }, + { + "period": 2, + "timestamp": "2026-06-15T00:00:00Z", + "supply_state": { + "current_supply": "210014700000000", + "staked_amount": "105007350000000", + "stability_committed": "0", + "m014_state": "INACTIVE", + "ecological_multiplier_enabled": false + }, + "burn_amount": "328000000000" + }, + { + "period": 3, + "timestamp": "2026-06-22T00:00:00Z", + "supply_state": { + "current_supply": "210016259000000", + "staked_amount": "105008129500000", + "stability_committed": "0", + "m014_state": "INACTIVE", + "ecological_multiplier_enabled": false + }, + "burn_amount": "329000000000" + }, + { + "period": 4, + "timestamp": "2026-06-29T00:00:00Z", + "supply_state": { + "current_supply": "210016781230000", + "staked_amount": "105008390615000", + "stability_committed": "0", + "m014_state": "INACTIVE", + "ecological_multiplier_enabled": false + }, + "burn_amount": "329500000000" + } + ] +} diff --git a/mechanisms/m012-fixed-cap-dynamic-supply/datasets/fixtures/v0_sample.json b/mechanisms/m012-fixed-cap-dynamic-supply/datasets/fixtures/v0_sample.json new file mode 100644 index 0000000..4739906 --- /dev/null +++ b/mechanisms/m012-fixed-cap-dynamic-supply/datasets/fixtures/v0_sample.json @@ -0,0 +1,73 @@ +{ + "mechanism_id": "m012", + "scope": "v0_reference_replay", + "as_of": "2026-02-18T12:00:00Z", + "config": { + "hard_cap": "221000000000000", + "r_base": 0.02, + "ecological_multiplier": 1.0, + "ecological_multiplier_enabled": false + }, + "periods": [ + { + "period": 0, + "timestamp": "2026-01-06T00:00:00Z", + "supply_state": { + "current_supply": "150000000000000", + "staked_amount": "75000000000000", + "stability_committed": "0", + "m014_state": "INACTIVE", + "ecological_multiplier_enabled": false + }, + "burn_amount": "500000000000" + }, + { + "period": 1, + "timestamp": "2026-01-13T00:00:00Z", + "supply_state": { + "current_supply": "151630000000000", + "staked_amount": "75815000000000", + "stability_committed": "0", + "m014_state": "INACTIVE", + "ecological_multiplier_enabled": false + }, + "burn_amount": "450000000000" + }, + { + "period": 2, + "timestamp": "2026-01-20T00:00:00Z", + "supply_state": { + "current_supply": "153261400000000", + "staked_amount": "76630700000000", + "stability_committed": "0", + "m014_state": "INACTIVE", + "ecological_multiplier_enabled": false + }, + "burn_amount": "600000000000" + }, + { + "period": 3, + "timestamp": "2026-01-27T00:00:00Z", + "supply_state": { + "current_supply": "154693558000000", + "staked_amount": "77346779000000", + "stability_committed": "0", + "m014_state": "INACTIVE", + "ecological_multiplier_enabled": false + }, + "burn_amount": "550000000000" + }, + { + "period": 4, + "timestamp": "2026-02-03T00:00:00Z", + "supply_state": { + "current_supply": "156133424660000", + "staked_amount": "78066712330000", + "stability_committed": "0", + "m014_state": "INACTIVE", + "ecological_multiplier_enabled": false + }, + "burn_amount": "500000000000" + } + ] +} diff --git a/mechanisms/m012-fixed-cap-dynamic-supply/datasets/schema.json b/mechanisms/m012-fixed-cap-dynamic-supply/datasets/schema.json new file mode 100644 index 0000000..a6e2326 --- /dev/null +++ b/mechanisms/m012-fixed-cap-dynamic-supply/datasets/schema.json @@ -0,0 +1,101 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "m012 replay dataset", + "type": "object", + "required": [ + "mechanism_id", + "scope", + "as_of", + "config", + "periods" + ], + "properties": { + "mechanism_id": { + "const": "m012" + }, + "scope": { + "type": "string" + }, + "as_of": { + "type": "string", + "description": "ISO-8601 datetime", + "format": "date-time" + }, + "config": { + "type": "object", + "required": ["hard_cap", "r_base"], + "properties": { + "hard_cap": { + "type": "string", + "pattern": "^[0-9]+$", + "description": "Hard cap in uregen" + }, + "r_base": { + "type": "number", + "minimum": 0, + "maximum": 0.10, + "description": "Base regrowth rate" + }, + "ecological_multiplier": { + "type": "number", + "minimum": 0, + "description": "Ecological multiplier (1.0 when disabled)" + }, + "ecological_multiplier_enabled": { + "type": "boolean" + } + } + }, + "periods": { + "type": "array", + "items": { + "type": "object", + "required": [ + "period", + "timestamp", + "supply_state", + "burn_amount" + ], + "properties": { + "period": { + "type": "integer", + "minimum": 0 + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "supply_state": { + "type": "object", + "required": ["current_supply", "staked_amount", "m014_state"], + "properties": { + "current_supply": { + "type": "string", + "pattern": "^[0-9]+$" + }, + "staked_amount": { + "type": "string", + "pattern": "^[0-9]+$" + }, + "stability_committed": { + "type": "string", + "pattern": "^[0-9]+$" + }, + "m014_state": { + "type": "string", + "enum": ["INACTIVE", "TRANSITION", "ACTIVE", "EQUILIBRIUM"] + }, + "ecological_multiplier_enabled": { + "type": "boolean" + } + } + }, + "burn_amount": { + "type": "string", + "pattern": "^[0-9]+$" + } + } + } + } + } +} diff --git a/mechanisms/m012-fixed-cap-dynamic-supply/reference-impl/README.md b/mechanisms/m012-fixed-cap-dynamic-supply/reference-impl/README.md new file mode 100644 index 0000000..7a42cbc --- /dev/null +++ b/mechanisms/m012-fixed-cap-dynamic-supply/reference-impl/README.md @@ -0,0 +1,52 @@ +# m012 reference implementation (v0) + +This folder provides a **canonical computation** for m012 outputs so that different agents/runners +produce consistent numbers. + +## Inputs + +### Supply period computation (`computeSupplyPeriod`) +- `supply_state.current_supply` (string, uregen) -- current circulating supply +- `supply_state.hard_cap` (string, uregen, default "221000000000000") -- hard cap +- `supply_state.staked_amount` (string, uregen) -- staked tokens +- `supply_state.stability_committed` (string, uregen, default "0") -- M015 stability commitments +- `supply_state.m014_state` (string) -- INACTIVE | TRANSITION | ACTIVE | EQUILIBRIUM +- `supply_state.ecological_multiplier_enabled` (boolean) -- false in v0 +- `burn_amount` (string, uregen) -- tokens burned this period (from M013) +- `config.r_base` (number, default 0.02) -- base regrowth rate + +### KPI computation (`computeM012KPI`) +- `as_of` (ISO-8601 Z string) -- point-in-time +- `periods[]` -- array of period records with minted, burned, supply_after, rates + +## Outputs + +### Supply period +- `next_supply` -- supply after period (uregen string) +- `minted` -- tokens minted as regrowth (uregen string) +- `burned` -- tokens burned (uregen string) +- `regrowth_rate` -- effective rate r = r_base * multiplier * eco_mult +- `effective_multiplier` -- phase-gated multiplier +- `staking_multiplier`, `stability_multiplier`, `ecological_multiplier` +- `headroom_before`, `headroom_after` -- cap headroom (uregen strings) + +### KPI block +- `mechanism_id` = "m012" +- `current_supply`, `hard_cap`, `cap_headroom` -- uregen strings +- `total_minted`, `total_burned`, `net_supply_change` -- uregen strings +- `latest_regrowth_rate`, `latest_effective_multiplier` +- `periods_evaluated`, `equilibrium_status`, `equilibrium_gap_pct` + +## Self-test + +```bash +node m012_supply.js +``` + +Reads `test_vectors/vector_v0_sample.input.json` and validates against +`test_vectors/vector_v0_sample.expected.json`. + +## Design notes +- All token amounts use **BigInt** to avoid floating-point precision issues on large uregen values. +- Rates and multipliers remain as floating-point numbers (they are small dimensionless multipliers). +- The `Math.floor()` truncation when converting rate * headroom to BigInt is intentional and matches the expected test vectors. diff --git a/mechanisms/m012-fixed-cap-dynamic-supply/reference-impl/m012_kpi.js b/mechanisms/m012-fixed-cap-dynamic-supply/reference-impl/m012_kpi.js new file mode 100644 index 0000000..8e73919 --- /dev/null +++ b/mechanisms/m012-fixed-cap-dynamic-supply/reference-impl/m012_kpi.js @@ -0,0 +1,90 @@ +/** + * m012 — Fixed Cap Dynamic Supply: KPI computation. + * + * Aggregates period records into a KPI block conforming to + * schemas/m012_kpi.schema.json. + * + * @module m012_kpi + */ + +/** + * Compute m012 KPI from an array of period records. + * + * @param {Object} opts + * @param {string} opts.as_of - ISO-8601 timestamp for KPI point-in-time + * @param {Array} opts.periods - Array of period records with { supply_after, minted, burned, regrowth_rate, effective_multiplier } + * @param {string} [opts.hard_cap] - Hard cap in uregen (string, default "221000000000000") + * @returns {Object} KPI block conforming to m012_kpi.schema.json + */ +export function computeM012KPI({ as_of, periods, hard_cap }) { + const cap = BigInt(hard_cap ?? "221000000000000"); + const ps = periods ?? []; + + if (!ps.length) { + return { + mechanism_id: "m012", + scope: "v0_reference", + as_of, + current_supply: "0", + hard_cap: cap.toString(), + cap_headroom: cap.toString(), + cap_utilization: 0, + total_minted: "0", + total_burned: "0", + net_supply_change: "0", + latest_regrowth_rate: 0, + latest_effective_multiplier: 1.0, + periods_evaluated: 0, + equilibrium_status: "not_reached", + equilibrium_gap_pct: null + }; + } + + let totalMinted = 0n; + let totalBurned = 0n; + + for (const p of ps) { + totalMinted += BigInt(p.minted); + totalBurned += BigInt(p.burned); + } + + const last = ps[ps.length - 1]; + const currentSupply = BigInt(last.supply_after ?? last.next_supply); + const headroom = cap - currentSupply; + const capUtil = Number(cap) > 0 ? Number(currentSupply) / Number(cap) : 0; + const netChange = totalMinted - totalBurned; + + // Equilibrium detection: gap as percentage + const maxMB = totalMinted > totalBurned ? totalMinted : totalBurned; + const absGap = netChange >= 0n ? netChange : -netChange; + const gapPct = maxMB > 0n + ? Number(absGap) / Number(maxMB) * 100 + : null; + + let equilibriumStatus = "not_reached"; + if (gapPct !== null) { + if (gapPct < 1) { + equilibriumStatus = "reached"; + } else if (gapPct < 5) { + equilibriumStatus = "approaching"; + } + } + + return { + mechanism_id: "m012", + scope: "v0_reference", + as_of, + current_supply: currentSupply.toString(), + hard_cap: cap.toString(), + cap_headroom: headroom.toString(), + cap_utilization: Number(capUtil.toFixed(6)), + total_minted: totalMinted.toString(), + total_burned: totalBurned.toString(), + net_supply_change: netChange.toString(), + latest_regrowth_rate: last.regrowth_rate, + latest_effective_multiplier: last.effective_multiplier, + periods_evaluated: ps.length, + equilibrium_status: equilibriumStatus, + equilibrium_gap_pct: gapPct !== null ? Number(gapPct.toFixed(4)) : null + }; +} diff --git a/mechanisms/m012-fixed-cap-dynamic-supply/reference-impl/m012_supply.js b/mechanisms/m012-fixed-cap-dynamic-supply/reference-impl/m012_supply.js new file mode 100644 index 0000000..e4341fb --- /dev/null +++ b/mechanisms/m012-fixed-cap-dynamic-supply/reference-impl/m012_supply.js @@ -0,0 +1,192 @@ +/** + * m012 — Fixed Cap Dynamic Supply: canonical reference computation. + * + * Supply algorithm (SPEC.md section 5): + * S[t+1] = S[t] + M[t] - B[t] + * M[t] = r * (C - S[t]) + * r = r_base * effective_multiplier * ecological_multiplier + * + * All token amounts are in uregen (1 REGEN = 1,000,000 uregen). + * Uses BigInt for supply arithmetic to avoid floating-point precision issues. + * + * @module m012_supply + */ + +const DEFAULT_HARD_CAP = 221_000_000_000_000n; // 221M REGEN in uregen +const DEFAULT_R_BASE = 0.02; + +/** + * Clamp a number to [min, max]. + * @param {number} v + * @param {number} lo + * @param {number} hi + * @returns {number} + */ +function clamp(v, lo, hi) { + return Math.max(lo, Math.min(hi, v)); +} + +/** + * Compute a single supply period. + * + * @param {Object} opts + * @param {Object} opts.supply_state + * @param {string} opts.supply_state.current_supply - uregen (string for BigInt) + * @param {string} [opts.supply_state.hard_cap] - uregen (string, default 221T) + * @param {string} opts.supply_state.staked_amount - uregen (string) + * @param {string} [opts.supply_state.stability_committed] - uregen (string, default "0") + * @param {string} [opts.supply_state.m014_state] - INACTIVE | TRANSITION | ACTIVE | EQUILIBRIUM + * @param {boolean} [opts.supply_state.ecological_multiplier_enabled] - false in v0 + * @param {number} [opts.supply_state.delta_co2] - ppm, only when eco enabled + * @param {number} [opts.supply_state.ecological_reference_value] - ppm, default 50 + * @param {string} opts.burn_amount - uregen (string) + * @param {Object} [opts.config] - override defaults + * @param {number} [opts.config.r_base] - base regrowth rate (default 0.02) + * @returns {{ next_supply: string, minted: string, burned: string, regrowth_rate: number, effective_multiplier: number, staking_multiplier: number, stability_multiplier: number, ecological_multiplier: number, headroom_before: string, headroom_after: string }} + */ +export function computeSupplyPeriod({ supply_state, burn_amount, config }) { + const r_base = config?.r_base ?? DEFAULT_R_BASE; + + // Parse BigInt values from strings + const S = BigInt(supply_state.current_supply); + const C = BigInt(supply_state.hard_cap ?? DEFAULT_HARD_CAP.toString()); + const staked = BigInt(supply_state.staked_amount); + const stabilityCommitted = BigInt(supply_state.stability_committed ?? "0"); + const B = BigInt(burn_amount); + const m014State = supply_state.m014_state ?? "INACTIVE"; + + // Validate parameter bounds (Security Invariant 5) + if (r_base < 0 || r_base > 0.10) { + throw new RangeError(`r_base must be in [0, 0.10], got ${r_base}`); + } + + // Compute staking_multiplier: 1 + (S_staked / S_total), clamped [1.0, 2.0] + const sFloat = Number(S); + const stakingMult = sFloat > 0 + ? clamp(1 + Number(staked) / sFloat, 1.0, 2.0) + : 1.0; + + // Compute stability_multiplier: 1 + (S_stability_committed / S_total), clamped [1.0, 2.0] + const stabilityMult = sFloat > 0 + ? clamp(1 + Number(stabilityCommitted) / sFloat, 1.0, 2.0) + : 1.0; + + // Phase-gated effective multiplier (SPEC.md section 5.3) + let effectiveMult; + if (m014State === "INACTIVE") { + effectiveMult = stakingMult; + } else if (m014State === "TRANSITION") { + effectiveMult = Math.max(stakingMult, stabilityMult); + } else { + // ACTIVE or EQUILIBRIUM + effectiveMult = stabilityMult; + } + + // Ecological multiplier (SPEC.md section 5.4) + let ecoMult = 1.0; + if (supply_state.ecological_multiplier_enabled) { + const deltaCo2 = supply_state.delta_co2 ?? 0; + const refValue = supply_state.ecological_reference_value ?? 50; + ecoMult = Math.max(0, 1 - (deltaCo2 / refValue)); + } + + // Regrowth rate + const r = r_base * effectiveMult * ecoMult; + + // Headroom before: C - S[t] + const headroomBefore = C > S ? C - S : 0n; + + // Minting: M[t] = r * (C - S[t]) + // Use floating point for rate * headroom, then truncate to BigInt + const mintedFloat = r * Number(headroomBefore); + const M = BigInt(Math.floor(mintedFloat)); + + // Next supply: S[t+1] = S[t] + M[t] - B[t] + // With safety: cap inviolability and non-negative supply + let nextSupply = S + M - B; + + // Security Invariant 1: Cap Inviolability + if (nextSupply > C) { + nextSupply = C; + } + + // Security Invariant 2: Non-Negative Supply + if (nextSupply < 0n) { + nextSupply = 0n; + } + + const headroomAfter = C - nextSupply; + + return { + next_supply: nextSupply.toString(), + minted: M.toString(), + burned: B.toString(), + regrowth_rate: r, + effective_multiplier: effectiveMult, + staking_multiplier: stakingMult, + stability_multiplier: stabilityMult, + ecological_multiplier: ecoMult, + headroom_before: headroomBefore.toString(), + headroom_after: headroomAfter.toString() + }; +} + +// --------------- Self-test harness --------------- + +import { readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { dirname, join } from "node:path"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +function selfTest() { + const inputPath = join(__dirname, "test_vectors", "vector_v0_sample.input.json"); + const expectedPath = join(__dirname, "test_vectors", "vector_v0_sample.expected.json"); + + const input = JSON.parse(readFileSync(inputPath, "utf8")); + const expected = JSON.parse(readFileSync(expectedPath, "utf8")); + + let pass = 0; + let fail = 0; + + for (let i = 0; i < input.periods.length; i++) { + const p = input.periods[i]; + const exp = expected.periods[i]; + + const result = computeSupplyPeriod({ + supply_state: p.supply_state, + burn_amount: p.burn_amount, + config: input.config + }); + + const checks = [ + ["next_supply", result.next_supply, exp.next_supply], + ["minted", result.minted, exp.minted], + ["burned", result.burned, exp.burned], + ["regrowth_rate", result.regrowth_rate, exp.regrowth_rate], + ["effective_multiplier", result.effective_multiplier, exp.effective_multiplier], + ["staking_multiplier", result.staking_multiplier, exp.staking_multiplier], + ["ecological_multiplier", result.ecological_multiplier, exp.ecological_multiplier], + ["headroom_before", result.headroom_before, exp.headroom_before], + ["headroom_after", result.headroom_after, exp.headroom_after] + ]; + + for (const [field, got, want] of checks) { + if (String(got) !== String(want)) { + console.error(`FAIL period ${i + 1} (${p.label}) ${field}: got ${got}, want ${want}`); + fail++; + } else { + pass++; + } + } + } + + console.log(`m012_supply self-test: ${pass} passed, ${fail} failed`); + if (fail > 0) process.exit(1); +} + +// Run self-test when executed directly +if (process.argv[1] === __filename) { + selfTest(); +} diff --git a/mechanisms/m012-fixed-cap-dynamic-supply/reference-impl/test_vectors/vector_v0_sample.expected.json b/mechanisms/m012-fixed-cap-dynamic-supply/reference-impl/test_vectors/vector_v0_sample.expected.json new file mode 100644 index 0000000..169651d --- /dev/null +++ b/mechanisms/m012-fixed-cap-dynamic-supply/reference-impl/test_vectors/vector_v0_sample.expected.json @@ -0,0 +1,70 @@ +{ + "description": "Expected outputs for vector_v0_sample.input.json, computed per SPEC.md section 5", + "periods": [ + { + "label": "Period 1: Normal period, 50% staked, eco disabled", + "next_supply": "151630000000000", + "minted": "2130000000000", + "burned": "500000000000", + "regrowth_rate": 0.03, + "effective_multiplier": 1.5, + "staking_multiplier": 1.5, + "ecological_multiplier": 1.0, + "headroom_before": "71000000000000", + "headroom_after": "69370000000000", + "notes": "r = 0.02 * 1.5 * 1.0 = 0.03; M = 0.03 * 71T = 2.13T; S' = 150T + 2.13T - 0.5T = 151.63T" + }, + { + "label": "Period 2: High staking (90% staked)", + "next_supply": "181358000000000", + "minted": "1558000000000", + "burned": "200000000000", + "regrowth_rate": 0.038, + "effective_multiplier": 1.9, + "staking_multiplier": 1.9, + "ecological_multiplier": 1.0, + "headroom_before": "41000000000000", + "headroom_after": "39642000000000", + "notes": "r = 0.02 * 1.9 * 1.0 = 0.038; M = 0.038 * 41T = 1.558T; S' = 180T + 1.558T - 0.2T = 181.358T" + }, + { + "label": "Period 3: Near-cap supply (supply at 99% of cap)", + "next_supply": "218756300000000", + "minted": "66300000000", + "burned": "100000000000", + "regrowth_rate": 0.03, + "effective_multiplier": 1.5, + "staking_multiplier": 1.5, + "ecological_multiplier": 1.0, + "headroom_before": "2210000000000", + "headroom_after": "2243700000000", + "notes": "Near-cap: M = 0.03 * 2.21T = 66.3B; burn exceeds mint so supply decreases. S' = 218.79T + 66.3B - 100B = 218.7563T" + }, + { + "label": "Period 4: Large burn period", + "next_supply": "195630000000000", + "minted": "630000000000", + "burned": "5000000000000", + "regrowth_rate": 0.03, + "effective_multiplier": 1.5, + "staking_multiplier": 1.5, + "ecological_multiplier": 1.0, + "headroom_before": "21000000000000", + "headroom_after": "25370000000000", + "notes": "Large burn: M = 0.03 * 21T = 630B; B = 5T >> M; net contraction. S' = 200T + 0.63T - 5T = 195.63T" + }, + { + "label": "Period 5: Post-M014 with stability_multiplier", + "next_supply": "190692000000000", + "minted": "992000000000", + "burned": "300000000000", + "regrowth_rate": 0.032, + "effective_multiplier": 1.6, + "staking_multiplier": 1.0, + "ecological_multiplier": 1.0, + "headroom_before": "31000000000000", + "headroom_after": "30308000000000", + "notes": "Post-M014 ACTIVE: staking_mult ignored (S_staked=0 -> 1.0), stability_mult = 1 + 114T/190T = 1.6; r = 0.02 * 1.6 = 0.032; M = 0.032 * 31T = 992B" + } + ] +} diff --git a/mechanisms/m012-fixed-cap-dynamic-supply/reference-impl/test_vectors/vector_v0_sample.input.json b/mechanisms/m012-fixed-cap-dynamic-supply/reference-impl/test_vectors/vector_v0_sample.input.json new file mode 100644 index 0000000..d6adae4 --- /dev/null +++ b/mechanisms/m012-fixed-cap-dynamic-supply/reference-impl/test_vectors/vector_v0_sample.input.json @@ -0,0 +1,71 @@ +{ + "description": "5 periods with different supply states for m012 Fixed Cap Dynamic Supply", + "config": { + "hard_cap": "221000000000000", + "r_base": 0.02, + "ecological_multiplier": 1.0, + "ecological_multiplier_enabled": false + }, + "periods": [ + { + "label": "Period 1: Normal period, 50% staked, eco disabled", + "supply_state": { + "current_supply": "150000000000000", + "hard_cap": "221000000000000", + "staked_amount": "75000000000000", + "stability_committed": "0", + "m014_state": "INACTIVE", + "ecological_multiplier_enabled": false + }, + "burn_amount": "500000000000" + }, + { + "label": "Period 2: High staking (90% staked)", + "supply_state": { + "current_supply": "180000000000000", + "hard_cap": "221000000000000", + "staked_amount": "162000000000000", + "stability_committed": "0", + "m014_state": "INACTIVE", + "ecological_multiplier_enabled": false + }, + "burn_amount": "200000000000" + }, + { + "label": "Period 3: Near-cap supply (supply at 99% of cap)", + "supply_state": { + "current_supply": "218790000000000", + "hard_cap": "221000000000000", + "staked_amount": "109395000000000", + "stability_committed": "0", + "m014_state": "INACTIVE", + "ecological_multiplier_enabled": false + }, + "burn_amount": "100000000000" + }, + { + "label": "Period 4: Large burn period", + "supply_state": { + "current_supply": "200000000000000", + "hard_cap": "221000000000000", + "staked_amount": "100000000000000", + "stability_committed": "0", + "m014_state": "INACTIVE", + "ecological_multiplier_enabled": false + }, + "burn_amount": "5000000000000" + }, + { + "label": "Period 5: Post-M014 with stability_multiplier", + "supply_state": { + "current_supply": "190000000000000", + "hard_cap": "221000000000000", + "staked_amount": "0", + "stability_committed": "114000000000000", + "m014_state": "ACTIVE", + "ecological_multiplier_enabled": false + }, + "burn_amount": "300000000000" + } + ] +} diff --git a/mechanisms/m012-fixed-cap-dynamic-supply/schemas/README.md b/mechanisms/m012-fixed-cap-dynamic-supply/schemas/README.md new file mode 100644 index 0000000..5fb9e7f --- /dev/null +++ b/mechanisms/m012-fixed-cap-dynamic-supply/schemas/README.md @@ -0,0 +1,14 @@ +# m012 output schemas + +These JSON Schemas define **canonical output shapes** for m012 (Fixed Cap Dynamic Supply) artifacts. + +## Files +- `m012_supply_state.schema.json` -- schema for the supply state input (current supply, cap, staked amounts, multiplier config). +- `m012_period_record.schema.json` -- schema for per-period mint/burn records (supply before/after, minted, burned, rates, multipliers). +- `m012_kpi.schema.json` -- schema for the KPI JSON block emitted by agents/digests, including equilibrium status. + +## Notes +- These schemas are intended for **validation** and consistency across repos (Heartbeat, agent skills, etc.). +- All token amounts are in **uregen** (1 REGEN = 1,000,000 uregen) and represented as strings to preserve BigInt precision. +- The `m014_state` field gates which multiplier is used (staking vs stability vs max of both). +- v0 disables the ecological multiplier (set to 1.0). diff --git a/mechanisms/m012-fixed-cap-dynamic-supply/schemas/m012_kpi.schema.json b/mechanisms/m012-fixed-cap-dynamic-supply/schemas/m012_kpi.schema.json new file mode 100644 index 0000000..c05c583 --- /dev/null +++ b/mechanisms/m012-fixed-cap-dynamic-supply/schemas/m012_kpi.schema.json @@ -0,0 +1,97 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "m012 KPI output", + "type": "object", + "additionalProperties": false, + "required": [ + "mechanism_id", + "scope", + "as_of", + "current_supply", + "hard_cap", + "cap_headroom", + "total_minted", + "total_burned", + "latest_regrowth_rate", + "latest_effective_multiplier", + "periods_evaluated", + "equilibrium_status" + ], + "properties": { + "mechanism_id": { + "const": "m012" + }, + "scope": { + "type": "string" + }, + "as_of": { + "type": "string", + "description": "ISO-8601 datetime with timezone (e.g., 2026-02-18T12:00:00Z)", + "format": "date-time" + }, + "current_supply": { + "type": "string", + "pattern": "^[0-9]+$", + "description": "Current circulating supply in uregen" + }, + "hard_cap": { + "type": "string", + "pattern": "^[0-9]+$", + "description": "Hard cap in uregen" + }, + "cap_headroom": { + "type": "string", + "pattern": "^[0-9]+$", + "description": "Remaining capacity: hard_cap - current_supply (uregen)" + }, + "cap_utilization": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "current_supply / hard_cap" + }, + "total_minted": { + "type": "string", + "pattern": "^[0-9]+$", + "description": "Sum of all minted amounts across evaluated periods (uregen)" + }, + "total_burned": { + "type": "string", + "pattern": "^[0-9]+$", + "description": "Sum of all burned amounts across evaluated periods (uregen)" + }, + "net_supply_change": { + "type": "string", + "pattern": "^-?[0-9]+$", + "description": "total_minted - total_burned (uregen, may be negative)" + }, + "latest_regrowth_rate": { + "type": "number", + "minimum": 0, + "description": "Regrowth rate from most recent period" + }, + "latest_effective_multiplier": { + "type": "number", + "minimum": 1.0, + "maximum": 2.0, + "description": "Effective multiplier from most recent period" + }, + "periods_evaluated": { + "type": "integer", + "minimum": 0, + "description": "Number of periods included in this KPI computation" + }, + "equilibrium_status": { + "type": "string", + "enum": ["not_reached", "approaching", "reached"], + "description": "Whether minting approximately equals burning over recent periods" + }, + "equilibrium_gap_pct": { + "type": ["number", "null"], + "description": "abs(total_minted - total_burned) / max(total_minted, total_burned) as percentage. Null if no periods." + }, + "notes": { + "type": "string" + } + } +} diff --git a/mechanisms/m012-fixed-cap-dynamic-supply/schemas/m012_period_record.schema.json b/mechanisms/m012-fixed-cap-dynamic-supply/schemas/m012_period_record.schema.json new file mode 100644 index 0000000..0337512 --- /dev/null +++ b/mechanisms/m012-fixed-cap-dynamic-supply/schemas/m012_period_record.schema.json @@ -0,0 +1,92 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "m012 period record", + "description": "Per-period mint/burn record for the fixed cap dynamic supply mechanism. All token amounts are in uregen.", + "type": "object", + "additionalProperties": false, + "required": [ + "period", + "timestamp", + "supply_before", + "supply_after", + "minted", + "burned", + "regrowth_rate", + "effective_multiplier" + ], + "properties": { + "period": { + "type": "integer", + "minimum": 0, + "description": "Period index (0-based)" + }, + "timestamp": { + "type": "string", + "format": "date-time", + "description": "ISO-8601 datetime of period computation" + }, + "supply_before": { + "type": "string", + "pattern": "^[0-9]+$", + "description": "Supply at start of period in uregen" + }, + "supply_after": { + "type": "string", + "pattern": "^[0-9]+$", + "description": "Supply at end of period in uregen" + }, + "minted": { + "type": "string", + "pattern": "^[0-9]+$", + "description": "Tokens minted (regrowth) in this period in uregen" + }, + "burned": { + "type": "string", + "pattern": "^[0-9]+$", + "description": "Tokens burned in this period in uregen" + }, + "regrowth_rate": { + "type": "number", + "minimum": 0, + "description": "Effective regrowth rate r = r_base * effective_multiplier * ecological_multiplier" + }, + "effective_multiplier": { + "type": "number", + "minimum": 1.0, + "maximum": 2.0, + "description": "Phase-gated multiplier (staking, stability, or max of both)" + }, + "staking_multiplier": { + "type": "number", + "minimum": 1.0, + "maximum": 2.0, + "description": "Staking participation multiplier: 1 + (S_staked / S_total)" + }, + "stability_multiplier": { + "type": "number", + "minimum": 1.0, + "maximum": 2.0, + "description": "Stability commitment multiplier: 1 + (S_stability_committed / S_total)" + }, + "ecological_multiplier": { + "type": "number", + "minimum": 0, + "description": "Ecological multiplier (1.0 when disabled in v0)" + }, + "headroom_before": { + "type": "string", + "pattern": "^[0-9]+$", + "description": "Cap headroom before period: hard_cap - supply_before (uregen)" + }, + "headroom_after": { + "type": "string", + "pattern": "^[0-9]+$", + "description": "Cap headroom after period: hard_cap - supply_after (uregen)" + }, + "m014_state": { + "type": "string", + "enum": ["INACTIVE", "TRANSITION", "ACTIVE", "EQUILIBRIUM"], + "description": "M014 module state at time of computation" + } + } +} diff --git a/mechanisms/m012-fixed-cap-dynamic-supply/schemas/m012_supply_state.schema.json b/mechanisms/m012-fixed-cap-dynamic-supply/schemas/m012_supply_state.schema.json new file mode 100644 index 0000000..ca07e4f --- /dev/null +++ b/mechanisms/m012-fixed-cap-dynamic-supply/schemas/m012_supply_state.schema.json @@ -0,0 +1,55 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "m012 supply state", + "description": "Current supply state for the fixed cap dynamic supply mechanism. All token amounts are in uregen (1 REGEN = 1,000,000 uregen).", + "type": "object", + "additionalProperties": false, + "required": [ + "current_supply", + "hard_cap", + "staked_amount", + "m014_state", + "ecological_multiplier_enabled" + ], + "properties": { + "current_supply": { + "type": "string", + "pattern": "^[0-9]+$", + "description": "Current circulating supply in uregen (string to support BigInt precision)" + }, + "hard_cap": { + "type": "string", + "pattern": "^[0-9]+$", + "description": "Hard cap in uregen (default: 221000000000000)" + }, + "staked_amount": { + "type": "string", + "pattern": "^[0-9]+$", + "description": "Currently staked amount in uregen" + }, + "stability_committed": { + "type": "string", + "pattern": "^[0-9]+$", + "description": "Amount committed to stability tiers (M015) in uregen. Only used when m014_state != INACTIVE." + }, + "m014_state": { + "type": "string", + "enum": ["INACTIVE", "TRANSITION", "ACTIVE", "EQUILIBRIUM"], + "description": "Current M014 module state, determines multiplier selection" + }, + "ecological_multiplier_enabled": { + "type": "boolean", + "description": "Whether ecological multiplier is active (false in v0)" + }, + "delta_co2": { + "type": "number", + "minimum": 0, + "description": "Change in CO2 metric (ppm). Only used when ecological_multiplier_enabled is true." + }, + "ecological_reference_value": { + "type": "number", + "minimum": 0, + "description": "Reference value for ecological multiplier computation (default: 50 ppm)" + } + } +} diff --git a/package.json b/package.json index d174e09..bf7a676 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "agentic-tokenomics", "private": true, + "type": "module", "version": "0.0.0", "scripts": { "verify": "node scripts/verify.mjs", diff --git a/scripts/verify.mjs b/scripts/verify.mjs index 3253586..b02db64 100644 --- a/scripts/verify.mjs +++ b/scripts/verify.mjs @@ -35,14 +35,34 @@ requireFile("mechanisms/m010-reputation-signal/schemas/m010_kpi.schema.json"); requireFile("mechanisms/m010-reputation-signal/schemas/m010_signal.schema.json"); requireFile("mechanisms/m010-reputation-signal/datasets/fixtures/v0_sample.json"); +// m012 core files +requireFile("mechanisms/m012-fixed-cap-dynamic-supply/SPEC.md"); +requireFile("mechanisms/m012-fixed-cap-dynamic-supply/README.md"); +requireFile("mechanisms/m012-fixed-cap-dynamic-supply/schemas/m012_kpi.schema.json"); +requireFile("mechanisms/m012-fixed-cap-dynamic-supply/schemas/m012_supply_state.schema.json"); +requireFile("mechanisms/m012-fixed-cap-dynamic-supply/schemas/m012_period_record.schema.json"); +requireFile("mechanisms/m012-fixed-cap-dynamic-supply/datasets/fixtures/v0_sample.json"); +requireFile("mechanisms/m012-fixed-cap-dynamic-supply/reference-impl/m012_supply.js"); +requireFile("mechanisms/m012-fixed-cap-dynamic-supply/reference-impl/m012_kpi.js"); + // Mechanism index check run("node", ["scripts/build-mechanism-index.mjs", "--check"]); -// Basic schema sanity +// Basic schema sanity — m010 const kpiSchema = readJson("mechanisms/m010-reputation-signal/schemas/m010_kpi.schema.json"); if (!kpiSchema.required || !kpiSchema.required.includes("mechanism_id")) { - console.error("KPI schema missing required fields."); + console.error("m010 KPI schema missing required fields."); process.exit(4); } +// Basic schema sanity — m012 +const m012KpiSchema = readJson("mechanisms/m012-fixed-cap-dynamic-supply/schemas/m012_kpi.schema.json"); +if (!m012KpiSchema.required || !m012KpiSchema.required.includes("mechanism_id")) { + console.error("m012 KPI schema missing required fields."); + process.exit(4); +} + +// m012 self-test +run("node", ["mechanisms/m012-fixed-cap-dynamic-supply/reference-impl/m012_supply.js"]); + console.log("agentic-tokenomics verify: PASS");