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.

core.gno

6.23 Kb · 188 lines
  1// Package core is the platform's immutable ledger (design §2.5.2): every
  2// collection, its official grc721 token and royalty, its provenance
  3// (commit–reveal v2), the engine registry, fees and the admin timelock.
  4//
  5// What core guarantees, whoever holds the admin key:
  6//
  7//   - I-1  admin cannot move, approve or burn a token, and holds no funds;
  8//     the op list is a closed set of parameters (admin.gno).
  9//   - I-2  pause only gates create, mint and trade. Transfer, burn,
 10//     finalize, distribute and every cancel stay open, and a pause expires
 11//     by itself after MaxPauseBlocks.
 12//   - I-5  supply only grows through the collection's bound minter, never
 13//     past maxSupply, and a token id is never minted twice.
 14//   - I-6  after Launch the engine, params, supply, payees, sale window and
 15//     commitments are frozen.
 16//   - I-10/11  a collection becomes Final at most once, only with both
 17//     reveals or after revealDeadline, and that deadline is bounded, so
 18//     the pre-reveal transfer lock always ends.
 19//   - I-12 engine code never runs on a state-changing path, except the
 20//     creator's own CreateCollection (ValidateParams) and the engine's own
 21//     RegisterEngine (one sample render). Every exported non-crossing
 22//     function is read-only, so an engine cannot call back into a mutator.
 23//   - I-13 every stored string passed safe.AssertText.
 24//
 25// core imports no mutable realm: minters and engines are known only by
 26// package path and interface value.
 27package core
 28
 29import (
 30	"chain"
 31	"chain/runtime"
 32	"chain/runtime/unsafe"
 33	"strconv"
 34
 35	"gno.land/p/nt/avl/v0"
 36	"gno.land/p/nt/grc721/royalty/v0"
 37	"gno.land/p/nt/grc721/v0"
 38
 39	"gno.land/p/g19dzk54hhsdc3gllpksuj6gu4a3e6fl8cr6rn4w/minhwa/safe/v1"
 40	"gno.land/p/g19dzk54hhsdc3gllpksuj6gu4a3e6fl8cr6rn4w/minhwa/types/v1"
 41)
 42
 43// Hard limits. They are constants on purpose: nobody, admin included, can
 44// raise them after deploy (block counts assume ~3.54 s blocks, RA M-1).
 45const (
 46	ProtocolFeeCapBps = 500           // 5% of any sale
 47	RoyaltyCapBps     = 1000          // 10% (RA M-3)
 48	CreateFeeCap      = 1_000_000_000 // 1,000 GNOT in ugnot
 49	MaxPayees         = 4
 50	MaxSupplyCap      = 10_000
 51	MaxMintPerTx      = 10    // hypothesis: measure n=10 gas/deposit on Onyx
 52	MaxReserveBps     = 1000  // creator reserve <= 10% of supply
 53	MaxSaleBlocks     = 732_000 // ~30 days
 54	RevealWindowMin   = 1_017   // ~1 hour
 55	RevealWindowMax   = 170_000 // ~7 days
 56	BeaconGraceBlocks = 254     // beacon may reveal first in the window's last ~15 min (< RevealWindowMin, so the creator always gets to go first)
 57	TimelockBlocks    = 48_800  // ~48 hours
 58	MaxPauseBlocks    = 195_000 // ~8 days
 59
 60	MaxNameLen        = 64
 61	MaxDescriptionLen = 1024
 62	MaxParamsLen      = 1024
 63	MinSecretLen      = 32
 64	MaxSecretLen      = 256
 65	MaxPageSize       = 50
 66)
 67
 68// Provenance badges (design §4.3).
 69const (
 70	BadgeBoth        = "2/2"
 71	BadgeCreatorOnly = "creator-only"
 72	BadgeBeaconOnly  = "beacon-only"
 73	BadgeFallback    = "fallback"
 74)
 75
 76type collection struct {
 77	id, name, symbol, description string
 78	creator, pendingCreator       address
 79	engineID, params              string
 80	maxSupply                     int64
 81	nextTID                       int64 // next token id; minted so far = nextTID-1
 82	reserved                      int64
 83	royaltyBps                    int64
 84	primaryPayees, royaltyPayees  []types.Payee
 85	minter                        string
 86	state                         types.State
 87	startHeight, endHeight        int64
 88	revealWindow                  int64
 89	closeHeight                   int64 // sell-out height, or endHeight once time ran out
 90	useBeacon                     bool
 91	createdHeight                 int64
 92	prov                          *provenance
 93	token                         *grc721.Token
 94	ledger                        *grc721.PrivateLedger
 95	roy                           *royalty.Royalty
 96	st                            *tokenState
 97	mintedBy                      avl.Tree // address -> int64, lifetime, written on every mint (I-9)
 98}
 99
100func (c *collection) minted() int64 { return c.nextTID - 1 }
101
102var (
103	// selfPath is this realm's package path, captured once in init so read
104	// functions can derive sub-addresses without a cur.
105	selfPath string
106
107	collections avl.Tree // safe.PadInt(cid) -> *collection
108	byCreator   avl.Tree // creator + ":" + padded cid -> cid
109	collCount   int64
110	usedCommits avl.Tree // commit hex -> cid; a commitment is single-use platform-wide (RA M-5)
111)
112
113func init(cur realm) {
114	selfPath = cur.PkgPath()
115}
116
117const cidWidth = 10
118
119func collKey(cid string) string {
120	return safe.PadInt(safe.ParseID("cid", cid), cidWidth)
121}
122
123func mustCollection(cid string) *collection {
124	v := collections.Get(collKey(cid))
125	if v == nil {
126		panic("unknown collection " + strconv.Quote(cid))
127	}
128	return v.(*collection)
129}
130
131// stateAt is the effective state at height h; the stored state lags until
132// the next write materializes it (refresh).
133func stateAt(c *collection, h int64) types.State {
134	switch c.state {
135	case types.StateScheduled:
136		if h > c.endHeight {
137			return types.StateClosed
138		}
139		if h >= c.startHeight {
140			return types.StateMinting
141		}
142	case types.StateMinting:
143		if h > c.endHeight {
144			return types.StateClosed
145		}
146	}
147	return c.state
148}
149
150func refresh(c *collection, h int64) {
151	s := stateAt(c, h)
152	if s == c.state {
153		return
154	}
155	if s == types.StateClosed && c.closeHeight == 0 {
156		c.closeHeight = c.endHeight
157	}
158	c.state = s
159	chain.Emit("CollectionState", "cid", c.id, "state", s.String())
160}
161
162// revealDeadline is when anyone may finalize without the missing secrets.
163// It is at most endHeight + RevealWindowMax, which bounds the transfer
164// lock (I-11).
165func revealDeadline(c *collection) int64 {
166	closed := c.closeHeight
167	if closed == 0 {
168		closed = c.endHeight
169	}
170	return closed + c.revealWindow
171}
172
173func caller(cur realm) address {
174	return cur.Previous().Address()
175}
176
177// assertNoDeposit refuses coins on functions that take none, so nothing can
178// get stuck in core (RA L-1). The origin-send envelope belongs to the first
179// realm of the call; it is only ours to judge when a wallet called us.
180func assertNoDeposit(cur realm) {
181	if cur.Previous().IsUserCall() && !unsafe.OriginSend().IsZero() {
182		panic("this function does not accept coins")
183	}
184}
185
186func itoa(n int64) string { return strconv.FormatInt(n, 10) }
187
188func height() int64 { return runtime.ChainHeight() }