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