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}