173 lines
6.2 KiB
Python
173 lines
6.2 KiB
Python
"""Proof-of-contribution labor issuance channel.
|
|
|
|
Non-financial minting: verified labor/contribution units are converted
|
|
to $MYCO at a governed rate, subject to rate limits (flow dampening).
|
|
|
|
Design:
|
|
- An attestation system (oracle, DAO vote, or peer review) reports
|
|
"contribution units" for a contributor
|
|
- Each unit converts to $MYCO at a governed rate (tokens_per_unit)
|
|
- Rate limits prevent gaming: max units per period, cooldown between claims
|
|
- Contribution types have different conversion rates (code, governance, community)
|
|
|
|
This is the "subscription-based bonding curve" concept from the MycoFi paper,
|
|
extended to general labor contributions.
|
|
"""
|
|
|
|
import numpy as np
|
|
from dataclasses import dataclass, field
|
|
from typing import Optional
|
|
|
|
|
|
@dataclass
|
|
class ContributionType:
|
|
"""A type of contribution with its conversion rate."""
|
|
name: str
|
|
tokens_per_unit: float # $MYCO per contribution unit
|
|
max_units_per_period: float # Rate limit per period
|
|
period_length: float # Length of rate-limit period (time units)
|
|
cooldown: float = 0.0 # Minimum time between claims
|
|
decay_rate: float = 0.0 # Annual decay of unclaimed units (use-it-or-lose-it)
|
|
|
|
|
|
@dataclass
|
|
class ContributorState:
|
|
"""State for a single contributor."""
|
|
address: str
|
|
unclaimed_units: dict[str, float] = field(default_factory=dict)
|
|
claimed_this_period: dict[str, float] = field(default_factory=dict)
|
|
period_start: dict[str, float] = field(default_factory=dict)
|
|
last_claim_time: dict[str, float] = field(default_factory=dict)
|
|
total_claimed: float = 0.0
|
|
|
|
|
|
@dataclass
|
|
class LaborIssuanceSystem:
|
|
"""The labor issuance channel."""
|
|
contribution_types: dict[str, ContributionType]
|
|
global_rate_multiplier: float = 1.0 # Governance can scale all rates
|
|
total_minted: float = 0.0
|
|
max_total_mint: float = float("inf") # Cap on total labor-minted tokens
|
|
|
|
|
|
def attest_contribution(
|
|
system: LaborIssuanceSystem,
|
|
contributor: ContributorState,
|
|
contribution_type: str,
|
|
units: float,
|
|
current_time: float,
|
|
) -> ContributorState:
|
|
"""Record a contribution attestation.
|
|
|
|
Called by the oracle/attestation layer when a contribution is verified.
|
|
Does NOT mint tokens — just records units for later claiming.
|
|
"""
|
|
assert contribution_type in system.contribution_types
|
|
assert units > 0
|
|
|
|
ct = system.contribution_types[contribution_type]
|
|
|
|
# Apply decay to existing unclaimed units
|
|
if contribution_type in contributor.unclaimed_units:
|
|
last_time = contributor.last_claim_time.get(contribution_type, current_time)
|
|
elapsed = current_time - last_time
|
|
if ct.decay_rate > 0 and elapsed > 0:
|
|
decay = np.exp(-ct.decay_rate * elapsed)
|
|
contributor.unclaimed_units[contribution_type] *= decay
|
|
|
|
# Add new units
|
|
current = contributor.unclaimed_units.get(contribution_type, 0.0)
|
|
contributor.unclaimed_units[contribution_type] = current + units
|
|
|
|
return contributor
|
|
|
|
|
|
def claim_tokens(
|
|
system: LaborIssuanceSystem,
|
|
contributor: ContributorState,
|
|
contribution_type: str,
|
|
current_time: float,
|
|
units_to_claim: float | None = None,
|
|
) -> tuple[LaborIssuanceSystem, ContributorState, float]:
|
|
"""Claim $MYCO for verified contributions.
|
|
|
|
Returns (updated_system, updated_contributor, tokens_minted).
|
|
"""
|
|
assert contribution_type in system.contribution_types
|
|
ct = system.contribution_types[contribution_type]
|
|
|
|
# Check cooldown
|
|
last_claim = contributor.last_claim_time.get(contribution_type, -float("inf"))
|
|
if current_time - last_claim < ct.cooldown:
|
|
return system, contributor, 0.0
|
|
|
|
# Available units
|
|
available = contributor.unclaimed_units.get(contribution_type, 0.0)
|
|
if available <= 0:
|
|
return system, contributor, 0.0
|
|
|
|
# Check rate limit (reset period if needed)
|
|
period_start = contributor.period_start.get(contribution_type, current_time)
|
|
if current_time - period_start >= ct.period_length:
|
|
# New period
|
|
contributor.claimed_this_period[contribution_type] = 0.0
|
|
contributor.period_start[contribution_type] = current_time
|
|
|
|
claimed_so_far = contributor.claimed_this_period.get(contribution_type, 0.0)
|
|
remaining_allowance = ct.max_units_per_period - claimed_so_far
|
|
|
|
# Determine how many units to claim
|
|
if units_to_claim is not None:
|
|
to_claim = min(units_to_claim, available, remaining_allowance)
|
|
else:
|
|
to_claim = min(available, remaining_allowance)
|
|
|
|
if to_claim <= 0:
|
|
return system, contributor, 0.0
|
|
|
|
# Compute tokens
|
|
tokens = to_claim * ct.tokens_per_unit * system.global_rate_multiplier
|
|
|
|
# Check global cap
|
|
tokens = min(tokens, system.max_total_mint - system.total_minted)
|
|
if tokens <= 0:
|
|
return system, contributor, 0.0
|
|
|
|
# Update state
|
|
contributor.unclaimed_units[contribution_type] -= to_claim
|
|
contributor.claimed_this_period[contribution_type] = claimed_so_far + to_claim
|
|
contributor.last_claim_time[contribution_type] = current_time
|
|
contributor.total_claimed += tokens
|
|
system.total_minted += tokens
|
|
|
|
return system, contributor, tokens
|
|
|
|
|
|
def create_default_system() -> LaborIssuanceSystem:
|
|
"""Create a default labor issuance system with common contribution types."""
|
|
types = {
|
|
"code": ContributionType(
|
|
name="Code Contribution",
|
|
tokens_per_unit=10.0, # 10 MYCO per merged PR equivalent
|
|
max_units_per_period=50.0, # Max 50 units per period
|
|
period_length=30.0, # 30-day periods
|
|
cooldown=1.0, # 1-day cooldown between claims
|
|
),
|
|
"governance": ContributionType(
|
|
name="Governance Participation",
|
|
tokens_per_unit=5.0,
|
|
max_units_per_period=20.0,
|
|
period_length=30.0,
|
|
cooldown=7.0, # Weekly claims
|
|
),
|
|
"community": ContributionType(
|
|
name="Community Building",
|
|
tokens_per_unit=3.0,
|
|
max_units_per_period=100.0,
|
|
period_length=30.0,
|
|
cooldown=1.0,
|
|
decay_rate=0.1, # Unclaimed units decay
|
|
),
|
|
}
|
|
return LaborIssuanceSystem(contribution_types=types)
|