guardians.gno
3.92 Kb · 160 lines
1package guardians_pilot_v1
2
3import (
4 "gno.land/p/nt/grc721/v0"
5 "gno.land/p/nt/ufmt/v0"
6)
7
8const (
9 collectionName = "Guardians TEST"
10 collectionSymbol = "GTEST"
11 maxSupply = int64(10)
12 maxMintBatch = 4
13)
14
15// The pilot owner is fixed independently of the production collection.
16const localAdmin address = "g1j4ehkkrqcr53a233wamtvqggt3skr86c6777xd"
17
18func PilotVersion() string { return "GUARDIANS_PILOT_V1" }
19
20var (
21 token *grc721.Token
22 ledger *grc721.PrivateLedger
23 callerTeller grc721.Teller
24)
25
26func init(cur realm) {
27 token, ledger = grc721.NewToken(collectionName, collectionSymbol, 0, cur)
28 callerTeller = ledger.CallerTeller()
29 attachExtensions(ledger)
30}
31
32// Collection getters.
33
34func Status() string { return "REVEAL_ENGINE_READY" }
35
36func Name() string { return token.GetName() }
37
38func Symbol() string { return token.GetSymbol() }
39
40func CollectionID() string { return token.ID() }
41
42func TotalSupply() int64 { return token.TotalSupply() }
43
44func MaxSupply() int64 { return maxSupply }
45
46func Admin() address { return localAdmin }
47
48// GRC-721 read facade.
49
50func BalanceOf(owner address) int64 {
51 balance, err := token.BalanceOf(owner)
52 if err != nil {
53 panic(err)
54 }
55 return balance
56}
57
58func OwnerOf(tokenID string) address {
59 owner, err := token.OwnerOf(grc721.TokenID(tokenID))
60 if err != nil {
61 panic(err)
62 }
63 return owner
64}
65
66func GetApproved(tokenID string) address {
67 approved, err := token.GetApproved(grc721.TokenID(tokenID))
68 if err != nil {
69 panic(err)
70 }
71 return approved
72}
73
74func IsApprovedForAll(owner, operator address) bool {
75 return token.IsApprovedForAll(owner, operator)
76}
77
78// GRC-721 write facade. CallerTeller derives the acting account from the
79// current cross-realm frame and keeps the PrivateLedger inside this realm.
80
81func Approve(cur realm, to address, tokenID string) {
82 if err := callerTeller.Approve(0, cur, to, grc721.TokenID(tokenID)); err != nil {
83 panic(err)
84 }
85}
86
87func SetApprovalForAll(cur realm, operator address, approved bool) {
88 if err := callerTeller.SetApprovalForAll(0, cur, operator, approved); err != nil {
89 panic(err)
90 }
91}
92
93func TransferFrom(cur realm, from, to address, tokenID string) {
94 if cur.Previous().Address() != from {
95 panic("guardians: operator transfers require marketplace settlement")
96 }
97 if err := callerTeller.TransferFrom(0, cur, from, to, grc721.TokenID(tokenID)); err != nil {
98 panic(err)
99 }
100}
101
102// AdminMint is for reserve/local-development minting and is permanently locked
103// after the first sale activation to prevent post-sale rare-token allocation.
104func AdminMint(cur realm, to address, quantity int) {
105 assertAdmin(cur.Previous().Address())
106 assertMintingAllowed()
107 if !salePaused || allowlistRootLocked {
108 panic("guardians: admin mint is locked after sale activation")
109 }
110 mintTokens(to, quantity)
111}
112
113func mintTokens(to address, quantity int) {
114 assertMintingAllowed()
115 assertMintQuantity(quantity)
116 if token.TotalSupply()+int64(quantity) > maxSupply {
117 panic("guardians: max supply reached")
118 }
119 for i := 0; i < quantity; i++ {
120 nextID := token.TotalSupply() + 1
121 tokenID := grc721.TokenID(ufmt.Sprintf("%d", nextID))
122 if err := ledger.Mint(to, tokenID); err != nil {
123 panic(err)
124 }
125 setInitialTokenURI(tokenID)
126 }
127}
128
129func Render(path string) string {
130 if path != "" {
131 return "404\n"
132 }
133
134 out := token.RenderHome()
135 out += ufmt.Sprintf("* **Maximum supply**: %d\n", maxSupply)
136 out += ufmt.Sprintf("* **Sale phase**: %s\n", SalePhase())
137 out += ufmt.Sprintf("* **Sale paused**: %t\n", SalePaused())
138 out += ufmt.Sprintf("* **Status**: %s\n", Status())
139 return out
140}
141
142func assertAdmin(caller address) {
143 if !isAdmin(caller) {
144 panic("guardians: restricted access")
145 }
146}
147
148func isAdmin(caller address) bool {
149 return caller == localAdmin
150}
151
152func assertMintQuantity(quantity int) {
153 if !validMintQuantity(quantity) {
154 panic(ufmt.Sprintf("guardians: quantity must be between 1 and %d", maxMintBatch))
155 }
156}
157
158func validMintQuantity(quantity int) bool {
159 return quantity >= 1 && quantity <= maxMintBatch
160}