Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

types.gno

4.71 Kb · 168 lines
  1// Package types holds the value types shared by core, the minters, the
  2// market and the readers. Every type is plain data (no pointers into realm
  3// state), so passing one across a realm boundary hands over a copy and
  4// never a capability.
  5package types
  6
  7import (
  8	"errors"
  9)
 10
 11// State is a collection's lifecycle (design §4.1):
 12//
 13//	Draft -> Scheduled -> Minting -> Closed -> Final
 14//
 15// Scheduled->Minting and Minting->Closed happen by block height; core
 16// reports the effective state and materializes it on the next write.
 17type State int
 18
 19const (
 20	StateDraft State = iota
 21	StateScheduled
 22	StateMinting
 23	StateClosed
 24	StateFinal
 25)
 26
 27func (s State) String() string {
 28	switch s {
 29	case StateDraft:
 30		return "draft"
 31	case StateScheduled:
 32		return "scheduled"
 33	case StateMinting:
 34		return "minting"
 35	case StateClosed:
 36		return "closed"
 37	case StateFinal:
 38		return "final"
 39	}
 40	return "unknown"
 41}
 42
 43// MaxUnitPrice caps every price (ugnot) so price*count and fee math stay
 44// far from int64 overflow: 1e15 ugnot = 1e9 GNOT.
 45const MaxUnitPrice = 1_000_000_000_000_000
 46
 47// Payee receives Bps/10000 of a payment.
 48type Payee struct {
 49	Addr address
 50	Bps  int64
 51}
 52
 53// CollectionSpec is what a minter asks core to create. Payees travel as
 54// "addr:bps,…" strings and are parsed by core, so core never stores a slice
 55// allocated by another realm.
 56type CollectionSpec struct {
 57	Name, Symbol, Description string
 58	EngineID, Params          string
 59	MaxSupply, RoyaltyBps     int64
 60	PrimaryPayees             string
 61	RoyaltyPayees             string
 62}
 63
 64// LaunchSpec freezes a collection's sale window and commitments (I-6).
 65type LaunchSpec struct {
 66	StartHeight, EndHeight int64
 67	RevealWindow           int64 // blocks after close; core bounds it
 68	CreatorCommit          string
 69	UseBeacon              bool
 70}
 71
 72// CollectionInfo is core's typed read model for other realms.
 73type CollectionInfo struct {
 74	ID, Name, Symbol, Description string
 75	Creator                       address
 76	Minter, EngineID, Params      string
 77	State                         State
 78	MaxSupply, Minted, Reserved   int64
 79	StartHeight, EndHeight        int64
 80	RevealDeadline                int64 // 0 before Launch
 81	RoyaltyBps                    int64
 82	Badge                         string // provenance badge once Final
 83}
 84
 85// Fees are the protocol's fee parameters; core enforces hard caps.
 86type Fees struct {
 87	CreateFee    int64 // ugnot per collection
 88	PrimaryBps   int64 // of every primary sale
 89	SecondaryBps int64 // of every market sale
 90	Treasury     address
 91}
 92
 93// Phase kinds.
 94const (
 95	PhaseAllow  = "allow"
 96	PhasePublic = "public"
 97)
 98
 99// Phase is one sale window. A fixed price has StartPrice == FloorPrice and
100// no steps; a Dutch auction starts at StartPrice and drops StepAmount every
101// StepBlocks until FloorPrice (design §4.5). WalletCap counts lifetime
102// mints of the collection (I-9); 0 means no cap.
103type Phase struct {
104	Kind, Set                string
105	StartHeight, EndHeight   int64
106	StartPrice, FloorPrice   int64
107	StepBlocks, StepAmount   int64
108	WalletCap                int64
109}
110
111// Validate checks a phase in isolation; the minter checks the set grammar
112// and how phases relate to each other.
113func (p Phase) Validate() error {
114	switch p.Kind {
115	case PhasePublic:
116		if p.Set != "" {
117			return errors.New("a public phase has no allowlist set")
118		}
119	case PhaseAllow:
120		if p.Set == "" {
121			return errors.New("an allow phase needs a set")
122		}
123	default:
124		return errors.New("phase kind must be allow or public")
125	}
126	if p.StartHeight <= 0 || p.EndHeight < p.StartHeight {
127		return errors.New("phase heights must satisfy 0 < start <= end")
128	}
129	if p.FloorPrice < 0 || p.StartPrice < p.FloorPrice || p.StartPrice > MaxUnitPrice {
130		return errors.New("prices must satisfy 0 <= floor <= start <= MaxUnitPrice")
131	}
132	if p.StartPrice == p.FloorPrice {
133		if p.StepBlocks != 0 || p.StepAmount != 0 {
134			return errors.New("a fixed-price phase has no steps")
135		}
136	} else if p.StepBlocks <= 0 || p.StepAmount <= 0 {
137		return errors.New("a Dutch phase needs positive stepBlocks and stepAmount")
138	}
139	if p.WalletCap < 0 {
140		return errors.New("walletCap must be >= 0")
141	}
142	return nil
143}
144
145// Covers reports whether height h is inside the phase window.
146func (p Phase) Covers(h int64) bool {
147	return h >= p.StartHeight && h <= p.EndHeight
148}
149
150// PriceAt is the unit price at height h:
151//
152//	max(floor, start - floor((h-startH)/stepBlocks) * stepAmount)
153//
154// computed so that no intermediate value can overflow.
155func (p Phase) PriceAt(h int64) int64 {
156	if p.StartPrice == p.FloorPrice || h <= p.StartHeight {
157		return p.StartPrice
158	}
159	steps := (h - p.StartHeight) / p.StepBlocks
160	if steps > (p.StartPrice-p.FloorPrice)/p.StepAmount {
161		return p.FloorPrice
162	}
163	price := p.StartPrice - steps*p.StepAmount
164	if price < p.FloorPrice {
165		return p.FloorPrice
166	}
167	return price
168}