Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions mechanisms/m015-contribution-weighted-rewards/datasets/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# m015 datasets (replay fixtures)

These fixtures are **deterministic inputs** for generating non-zero m015 KPI outputs **without MCP**.

## Files
- `schema.json` -- JSON schema for replay datasets
- `fixtures/v0_sample.json` -- single distribution period with 4 participants and 1 stability commitment
- `fixtures/v0_stability_sample.json` -- stability tier scenarios (committed, matured, early exit, cap overflow)
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

This line references fixtures/v0_stability_sample.json, but this file is not included in the pull request. To avoid confusion, please either add the file or update the documentation to indicate that it is planned for a future update.


## How they are used
A replay runner (e.g., in `regen-heartbeat`) can read a fixture file and compute:
- Per-participant activity scores using `computeActivityScore` from `reference-impl/m015_score.js`
- Stability tier allocation using `computeStabilityAllocation` from `reference-impl/m015_score.js`
- Pro-rata distribution using `computeDistribution` from `reference-impl/m015_score.js`
- Aggregated KPIs using `computeM015KPI` from `reference-impl/m015_kpi.js`

Key metrics produced:
- `total_distributed_uregen` -- total rewards distributed (stability + activity)
- `activity_pool_uregen` -- pool available for activity-based distribution
- `stability_allocation_uregen` -- stability tier payout (capped at 30% of inflow)
- `participant_count` -- participants with non-zero activity scores
- `gini_coefficient` -- inequality measure of reward distribution

## Units
All token amounts are in **uregen** (1 REGEN = 1,000,000 uregen) and represented as integers.

These datasets are **reference-only** and do not imply enforcement or on-chain actions.
231 changes: 231 additions & 0 deletions mechanisms/m015-contribution-weighted-rewards/datasets/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "m015 replay dataset",
"type": "object",
"required": [
"community_pool_inflow_uregen",
"periods_per_year",
"max_stability_share",
"stability_commitments",
"participants"
],
"properties": {
"description": {
"type": "string",
"description": "Human-readable description of the dataset"
},
"epoch": {
"type": "string",
"description": "ISO week identifier (e.g. 2026-W07)"
},
"community_pool_inflow_uregen": {
"type": "integer",
"minimum": 0,
"description": "Community Pool inflow for this period in uregen"
},
"periods_per_year": {
"type": "integer",
"minimum": 1,
"description": "Number of distribution periods per year (default 52 for weekly epochs)"
},
"max_stability_share": {
"type": "number",
"minimum": 0,
"maximum": 1,
"description": "Maximum fraction of inflow allocated to stability tier (default 0.30)"
},
"stability_commitments": {
"type": "array",
"description": "Active stability tier commitments for the period",
"items": {
"type": "object",
"required": ["holder_address", "committed_amount_uregen", "lock_period_months"],
"properties": {
"holder_address": {
"type": "string",
"description": "Bech32 address of the commitment holder"
},
"committed_amount_uregen": {
"type": "integer",
"minimum": 0,
"description": "Amount of uregen locked in stability commitment"
},
"lock_period_months": {
"type": "integer",
"minimum": 6,
"maximum": 24,
"description": "Lock period in months (min 6, max 24)"
},
"status": {
"type": "string",
"enum": ["uncommitted", "committed", "matured", "early_exit"],
"description": "Lifecycle state of the commitment"
},
"committed_at": {
"type": "string",
"format": "date-time",
"description": "ISO-8601 timestamp when commitment was made"
},
"matured_at": {
"type": ["string", "null"],
"format": "date-time",
"description": "ISO-8601 timestamp when commitment matured (null if not matured)"
},
"exited_at": {
"type": ["string", "null"],
"format": "date-time",
"description": "ISO-8601 timestamp of early exit (null if not exited)"
},
"accrued_rewards_uregen": {
"type": "integer",
"minimum": 0,
"description": "Total rewards accrued to date in uregen"
},
"forfeited_rewards_uregen": {
"type": "integer",
"minimum": 0,
"description": "Rewards forfeited due to early exit (50% penalty)"
},
"annual_return_rate": {
"type": "number",
"minimum": 0,
"description": "Annual return rate (default 0.06)"
}
}
}
},
"participants": {
"type": "array",
"description": "Participants with activity data for the period",
"items": {
"type": "object",
"required": ["address", "activities"],
"properties": {
"address": {
"type": "string",
"description": "Bech32 address of the participant"
},
"label": {
"type": "string",
"description": "Human-readable label for the participant"
},
"activities": {
"type": "object",
"required": [
"credit_purchase_value",
"credit_retirement_value",
"platform_facilitation_value",
"governance_votes_cast",
"proposals"
],
"properties": {
"credit_purchase_value": {
"type": "integer",
"minimum": 0,
"description": "Total credit purchase value in uregen for this period"
},
"credit_retirement_value": {
"type": "integer",
"minimum": 0,
"description": "Total credit retirement value in uregen for this period"
},
"platform_facilitation_value": {
"type": "integer",
"minimum": 0,
"description": "Total platform facilitation value in uregen for this period"
},
"governance_votes_cast": {
"type": "integer",
"minimum": 0,
"description": "Number of governance votes cast in this period"
},
"proposals": {
"type": "array",
"description": "Proposals submitted in this period",
"items": {
"type": "object",
"required": ["passed", "reached_quorum"],
"properties": {
"id": {
"type": "string",
"description": "Proposal identifier"
},
"passed": {
"type": "boolean",
"description": "Whether the proposal passed"
},
"reached_quorum": {
"type": "boolean",
"description": "Whether the proposal reached quorum"
}
}
}
}
}
},
"computed": {
"type": "object",
"description": "Pre-computed values for validation (optional, present in fixture files)",
"properties": {
"total_score": {
"type": "number",
"minimum": 0
},
"activity_reward_uregen": {
"type": "integer",
"minimum": 0
},
"activity_share": {
"type": "number",
"minimum": 0,
"maximum": 1
},
"stability_reward_uregen": {
"type": "integer",
"minimum": 0
},
"total_reward_uregen": {
"type": "integer",
"minimum": 0
}
}
}
}
}
},
"summary": {
"type": "object",
"description": "Pre-computed summary for validation (optional, present in fixture files)",
"properties": {
"stability_allocation_uregen": {
"type": "integer",
"minimum": 0
},
"activity_pool_uregen": {
"type": "integer",
"minimum": 0
},
"total_distributed_uregen": {
"type": "integer",
"minimum": 0
},
"participant_count": {
"type": "integer",
"minimum": 0
},
"stability_commitments_count": {
"type": "integer",
"minimum": 0
},
"stability_cap_utilization": {
"type": "number",
"minimum": 0,
"maximum": 1
},
"notes": {
"type": "string"
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# m015 reference implementation (v0)

This folder provides a **canonical computation** for m015 outputs so that different agents/runners
produce consistent numbers.

## Modules

### Activity scoring and distribution (`m015_score.js`)
- `computeActivityScore({ activities })` -- weighted score from five activity types
- `computeStabilityAllocation({ community_pool_inflow, stability_commitments, ... })` -- stability tier payout with 30% cap
- `computeDistribution({ activity_pool_amount, participants })` -- pro-rata reward distribution with remainder handling

### KPI computation (`m015_kpi.js`)
- `computeM015KPI({ community_pool_inflow_uregen, stability_commitments, participants, ... })` -- aggregated KPI block
- `giniCoefficient(values)` -- Gini inequality measure for reward distribution

## Inputs

### Activity scoring
- `activities.credit_purchase_value` (number, uregen) -- weight 0.30
- `activities.credit_retirement_value` (number, uregen) -- weight 0.30
- `activities.platform_facilitation_value` (number, uregen) -- weight 0.20
- `activities.governance_votes_cast` (number) -- weight 0.10
- `activities.proposals[]` (array of `{ passed, reached_quorum }`) -- weight 0.10, anti-gaming rules apply

### Stability allocation
- `community_pool_inflow` (number, uregen) -- Community Pool inflow for the period
- `stability_commitments[]` (array of `{ committed_amount_uregen }`) -- active commitments
- `periods_per_year` (number, default 52) -- weekly epochs
- `max_stability_share` (number, default 0.30) -- 30% cap on stability tier

## Outputs

### Activity score
- `total_score` -- weighted sum of all activity contributions
- `breakdown` -- per-activity detail (raw_value, weight, weighted_value)

### Stability allocation
- `stability_allocation` -- uregen allocated to stability tier (capped)
- `activity_pool` -- uregen remaining for activity distribution

### Distribution
- Per participant: `address`, `reward` (uregen), `share` (0-1)
- Remainder from `Math.floor()` truncation assigned to largest-share participant

### KPI block
- `total_distributed_uregen` -- stability + activity distributions
- `stability_allocation_uregen`, `activity_pool_uregen`
- `stability_utilization` -- fraction of 30% cap used
- `participant_count` -- participants with score > 0
- `gini_coefficient` -- inequality measure (0 = equal, 1 = max inequality)
- `top_earner_share` -- share of rewards going to highest scorer
- `revenue_constraint_satisfied` -- boolean: total payout <= inflow
- `stability_cap_satisfied` -- boolean: stability <= 30% cap

## Self-test

```bash
node m015_score.js
node m015_kpi.js
```

Each script reads all test vectors from `test_vectors/` and validates computed outputs against expected values.

## Test vectors

| Vector | Scenario |
|--------|----------|
| `vector_v0_sample` | 4 participants with diverse activity profiles, 1 stability commitment |
| `vector_v0_early_exit` | Stability tier with early exit penalty (50% forfeiture), 3 participants |
| `vector_v0_cap_overflow` | Stability obligations exceed 30% cap, demonstrating cap enforcement |
| `vector_v0_zero_activity` | All participants have zero activity, no stability commitments |

Each vector has a `.input.json` and `.expected.json` pair.

## Design notes
- All monetary values are integers in **uregen** (1 REGEN = 1,000,000 uregen).
- `Math.floor()` truncation is intentional for all reward computations, matching on-chain integer arithmetic.
- The remainder from floor truncation is assigned to the largest-share participant to ensure `sum(rewards) == activity_pool`.
- Stability allocation uses `Math.min(Math.floor(rawAllocation), Math.floor(cap))` to prevent over-allocation.
Loading