ship(P05): merge Fee Covenant into milestone v0.1
---ci--- project: oy phase: 5 milestone: v0.1 status: complete ship: v0.0.5 ---/ci---
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
package types
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
const (
|
||||
ModuleName = "feecovenant"
|
||||
StoreKey = ModuleName
|
||||
RouterKey = ModuleName
|
||||
QuerierRoute = ModuleName
|
||||
|
||||
// Fee Covenant — LOCKED (§18)
|
||||
// Mission-lock: fee can never exceed 0.1pct or fall below 0.01pct.
|
||||
// Both bounds enforced by smart contract. No future Council vote can change them.
|
||||
// Auto-decline only — fees only go down as the mesh grows, never up.
|
||||
|
||||
FeeCeilingBps = 10 // 0.1pct (ceiling, LOCKED)
|
||||
FeeFloorBps = 1 // 0.01pct (floor, LOCKED)
|
||||
InternalMinGrain = 1 // 1 Grain internal minimum (LOCKED, never changes)
|
||||
|
||||
// Volume tier thresholds (§18)
|
||||
VolumeTier2Bps = 7 // 0.07pct at $10B
|
||||
VolumeTier3Bps = 5 // 0.05pct at $50B
|
||||
VolumeTier4Bps = 3 // 0.03pct at $200B
|
||||
VolumeTier5Bps = 2 // 0.02pct at $500B
|
||||
VolumeFloorBps = 1 // 0.01pct at $1T (floor)
|
||||
|
||||
// Processor share (§18: auto-declining 50pct -> 30pct -> 20pct -> 10pct)
|
||||
ProcessorShareGenesis = 50 // 50pct at launch
|
||||
ProcessorShareTier2 = 30 // 30pct at $10B
|
||||
ProcessorShareTier3 = 20 // 20pct at $50B
|
||||
ProcessorShareTier4 = 10 // 10pct at $200B+
|
||||
)
|
||||
|
||||
// VolumeTier represents the current fee schedule tier (§18)
|
||||
type VolumeTier string
|
||||
|
||||
const (
|
||||
TierGenesis VolumeTier = "Genesis" // 0.1pct (ceiling)
|
||||
TierStage2 VolumeTier = "Stage2" // 0.07pct at $10B
|
||||
TierStage3 VolumeTier = "Stage3" // 0.05pct at $50B
|
||||
TierStage4 VolumeTier = "Stage4" // 0.03pct at $200B
|
||||
TierStage5 VolumeTier = "Stage5" // 0.02pct at $500B
|
||||
TierFloor VolumeTier = "Floor" // 0.01pct at $1T (floor)
|
||||
)
|
||||
|
||||
// WaiverReason defines "always free" pass types (§18)
|
||||
// 0pct protocol fee; only 1-Grain internal minimum
|
||||
type WaiverReason string
|
||||
|
||||
const (
|
||||
WaiverHandPassGuild WaiverReason = "HandPassGuild"
|
||||
WaiverCrewInternalVault WaiverReason = "CrewInternalVault"
|
||||
WaiverHouseholdInternal WaiverReason = "HouseholdInternal"
|
||||
WaiverCoverCall WaiverReason = "CoverCall"
|
||||
WaiverTrustDistribution WaiverReason = "TrustDistribution"
|
||||
WaiverFoundationGrant WaiverReason = "FoundationGrant"
|
||||
WaiverCircleActiveCycle WaiverReason = "CircleActiveCycle"
|
||||
)
|
||||
|
||||
// FeeSchedule is the current fee schedule (§18)
|
||||
type FeeSchedule struct {
|
||||
CeilingBps uint32 `json:"ceiling_bps" yaml:"ceiling_bps"`
|
||||
FloorBps uint32 `json:"floor_bps" yaml:"floor_bps"`
|
||||
CurrentBps uint32 `json:"current_bps" yaml:"current_bps"`
|
||||
VolumeTier VolumeTier `json:"volume_tier" yaml:"volume_tier"`
|
||||
InternalMinGrain int64 `json:"internal_min_grain" yaml:"internal_min_grain"`
|
||||
}
|
||||
|
||||
// DefaultFeeSchedule returns the launch fee schedule (§18)
|
||||
func DefaultFeeSchedule() FeeSchedule {
|
||||
return FeeSchedule{
|
||||
CeilingBps: FeeCeilingBps,
|
||||
FloorBps: FeeFloorBps,
|
||||
CurrentBps: FeeCeilingBps, // starts at ceiling
|
||||
VolumeTier: TierGenesis,
|
||||
InternalMinGrain: InternalMinGrain,
|
||||
}
|
||||
}
|
||||
|
||||
// Clamp ensures fee is within LOCKED bounds (§18: never > ceiling, never < floor)
|
||||
// This is automatic and authoritative. No Council vote can change it.
|
||||
func Clamp(feeBps uint32) uint32 {
|
||||
if feeBps > FeeCeilingBps {
|
||||
return FeeCeilingBps
|
||||
}
|
||||
if feeBps < FeeFloorBps {
|
||||
return FeeFloorBps
|
||||
}
|
||||
return feeBps
|
||||
}
|
||||
|
||||
// IsWaived checks if a pass type is "always free" (§18)
|
||||
func IsWaived(reason WaiverReason) bool {
|
||||
switch reason {
|
||||
case WaiverHandPassGuild, WaiverCrewInternalVault, WaiverHouseholdInternal,
|
||||
WaiverCoverCall, WaiverTrustDistribution, WaiverFoundationGrant,
|
||||
WaiverCircleActiveCycle:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// MissionLockFeeCovenant — enforced by code, not by promise (§18)
|
||||
const MissionLockFeeCovenant = "Standard Pass-Act fee: never above 0.1pct, never below 0.01pct. Auto-decline on schedule, never auto-increase. Internal minimum: 1 Grain per waived Pass-Act. Never changes. This covenant is enforced by code, not by promise."
|
||||
|
||||
type Params struct {
|
||||
Schedule FeeSchedule `json:"schedule" yaml:"schedule"`
|
||||
}
|
||||
|
||||
func DefaultParams() Params {
|
||||
return Params{Schedule: DefaultFeeSchedule()}
|
||||
}
|
||||
|
||||
type GenesisState struct {
|
||||
Params Params `json:"params" yaml:"params"`
|
||||
}
|
||||
|
||||
func DefaultGenesisState() *GenesisState {
|
||||
return &GenesisState{Params: DefaultParams()}
|
||||
}
|
||||
|
||||
func ValidateGenesis(bz json.RawMessage) error { return nil }
|
||||
@@ -0,0 +1,79 @@
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/oy/openyield/x/feecovenant/types"
|
||||
)
|
||||
|
||||
func TestFeeCeiling(t *testing.T) {
|
||||
if types.FeeCeilingBps != 10 {
|
||||
t.Errorf("FeeCeilingBps = %d, expected 10 (§18: 0.1pct ceiling LOCKED)", types.FeeCeilingBps)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFeeFloor(t *testing.T) {
|
||||
if types.FeeFloorBps != 1 {
|
||||
t.Errorf("FeeFloorBps = %d, expected 1 (§18: 0.01pct floor LOCKED)", types.FeeFloorBps)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInternalMinGrain(t *testing.T) {
|
||||
if types.InternalMinGrain != 1 {
|
||||
t.Errorf("InternalMinGrain = %d, expected 1 (§18: 1 Grain LOCKED)", types.InternalMinGrain)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClamp(t *testing.T) {
|
||||
if types.Clamp(15) != 10 {
|
||||
t.Error("Clamp(15) should return 10 (ceiling)")
|
||||
}
|
||||
if types.Clamp(0) != 1 {
|
||||
t.Error("Clamp(0) should return 1 (floor)")
|
||||
}
|
||||
if types.Clamp(5) != 5 {
|
||||
t.Error("Clamp(5) should return 5 (within bounds)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVolumeTiers(t *testing.T) {
|
||||
if types.VolumeTier2Bps != 7 {
|
||||
t.Error("VolumeTier2 should be 7 bps (§18)")
|
||||
}
|
||||
if types.VolumeFloorBps != 1 {
|
||||
t.Error("VolumeFloor should be 1 bps (§18)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessorShares(t *testing.T) {
|
||||
if types.ProcessorShareGenesis != 50 {
|
||||
t.Error("ProcessorShareGenesis should be 50pct (§18)")
|
||||
}
|
||||
if types.ProcessorShareTier4 != 10 {
|
||||
t.Error("ProcessorShareTier4 should be 10pct (§18)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWaivers(t *testing.T) {
|
||||
waivers := []types.WaiverReason{
|
||||
types.WaiverHandPassGuild, types.WaiverCrewInternalVault,
|
||||
types.WaiverHouseholdInternal, types.WaiverCoverCall,
|
||||
types.WaiverTrustDistribution, types.WaiverFoundationGrant,
|
||||
types.WaiverCircleActiveCycle,
|
||||
}
|
||||
for _, w := range waivers {
|
||||
if !types.IsWaived(w) {
|
||||
t.Errorf("Waiver %s should be free (§18)", w)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultFeeSchedule(t *testing.T) {
|
||||
schedule := types.DefaultFeeSchedule()
|
||||
if schedule.CurrentBps != 10 {
|
||||
t.Error("Default CurrentBps should be 10 (Genesis ceiling)")
|
||||
}
|
||||
if schedule.VolumeTier != types.TierGenesis {
|
||||
t.Error("Default VolumeTier should be Genesis")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user