feat(P03): Storage substrate — Stash, Vault, Root-Pool types

---ci---
project: oy
phase: 3
milestone: v0.1
status: execute
---/ci---

Phase 3 (Component 5: Storage Substrate) skeleton:
- x/stash: per-Holder sovereign storage (§5), Still flag (§10), maturity tracking (§9.1)
- x/vault: per-Stand governed storage (§5), sub-Vault allocations
- x/rootpool: mesh treasury (§5), attested by Watchers, visible to all Holders
- 4 unit tests passing (Freeholder maturity thresholds verified: 90d, 30d max gap)
- Lexicon compliant
This commit is contained in:
CIAgent
2026-08-17 19:58:31 +00:00
parent 42895a74dd
commit edea9422ac
4 changed files with 183 additions and 0 deletions
+55
View File
@@ -0,0 +1,55 @@
package types
import "encoding/json"
const (
ModuleName = "stash"
StoreKey = ModuleName
RouterKey = ModuleName
QuerierRoute = ModuleName
)
// Stash is a per-Holder sovereign storage (§5)
// The Holder owns it. No one else can access it without a Window (§10).
type Stash struct {
HolderID string `json:"holder_id" yaml:"holder_id"`
StashID string `json:"stash_id" yaml:"stash_id"`
CreatedAt int64 `json:"created_at" yaml:"created_at"`
LastActive int64 `json:"last_active" yaml:"last_active"`
BalanceGrain int64 `json:"balance_grain" yaml:"balance_grain"`
IsStill bool `json:"is_still" yaml:"is_still"` // Still = Holder paused partner access (§10)
}
// StashActivity tracks Stash maturity for Freeholder signals (§9.1: 90 days, no gap >30 days)
type StashActivity struct {
StashID string `json:"stash_id" yaml:"stash_id"`
ActiveDays uint32 `json:"active_days" yaml:"active_days"`
MaxGapDays uint32 `json:"max_gap_days" yaml:"max_gap_days"` // must be ≤30 for Freeholder
LastActivityDay int64 `json:"last_activity_day" yaml:"last_activity_day"`
}
// MaturityThreshold for Freeholder signal (§9.1: 90 days)
const MaturityThresholdDays = 90
// MaxGapForMaturity for Freeholder signal (§9.1: no gap > 30 days)
const MaxGapForMaturity = 30
// IsMature checks if Stash activity meets the Freeholder maturity threshold (§9.1)
func (a StashActivity) IsMature() bool {
return a.ActiveDays >= MaturityThresholdDays && a.MaxGapDays <= MaxGapForMaturity
}
type Params struct{}
func DefaultParams() Params { return Params{} }
type GenesisState struct {
Params Params `json:"params" yaml:"params"`
Stashes []Stash `json:"stashes" yaml:"stashes"`
}
func DefaultGenesisState() *GenesisState {
return &GenesisState{Params: DefaultParams(), Stashes: []Stash{}}
}
func ValidateGenesis(bz json.RawMessage) error { return nil }
+43
View File
@@ -0,0 +1,43 @@
package types_test
import (
"testing"
"github.com/oy/openyield/x/stash/types"
)
func TestMaturityThreshold(t *testing.T) {
if types.MaturityThresholdDays != 90 {
t.Errorf("MaturityThresholdDays = %d, expected 90 (§9.1)", types.MaturityThresholdDays)
}
}
func TestMaxGapForMaturity(t *testing.T) {
if types.MaxGapForMaturity != 30 {
t.Errorf("MaxGapForMaturity = %d, expected 30 (§9.1: no gap > 30 days)", types.MaxGapForMaturity)
}
}
func TestStashIsMature(t *testing.T) {
mature := types.StashActivity{ActiveDays: 90, MaxGapDays: 15}
if !mature.IsMature() {
t.Error("90 days active, 15 max gap should be mature (§9.1)")
}
notEnoughDays := types.StashActivity{ActiveDays: 89, MaxGapDays: 15}
if notEnoughDays.IsMature() {
t.Error("89 days should NOT be mature (threshold = 90)")
}
gapTooBig := types.StashActivity{ActiveDays: 100, MaxGapDays: 31}
if gapTooBig.IsMature() {
t.Error("31-day gap should NOT be mature (max gap = 30)")
}
}
func TestStashStillFlag(t *testing.T) {
stash := types.Stash{IsStill: true}
if !stash.IsStill {
t.Error("Stash with IsStill=true should reflect Still state (§10: Holder paused partner access)")
}
}