registry.gno
5.63 Kb · 212 lines
1package nftregistry2
2
3import (
4 "chain/runtime"
5
6 "gno.land/p/nt/avl"
7 "gno.land/p/demo/tokens/grc721"
8)
9
10// CollectionInfo - Extended metadata stored in the registry
11type CollectionInfo struct {
12 Address address // Realm address of the NFT collection
13 Name string // Collection name (from IGRC721CollectionMetadata)
14 Symbol string // Collection symbol (from IGRC721CollectionMetadata)
15 Creator address // Address that registered the collection
16 RegisteredAt int64 // Block height at registration
17 Verified bool // Admin verification status
18 Category string // Optional: "art", "gaming", "pfp", "utility", etc.
19 Description string // Human-readable description
20 ExternalURL string // Collection website/homepage
21 NFTGetter grc721.NFTGetter // Function to get the NFT instance
22 SupportsMetadata bool // Does collection implement IGRC721MetadataOnchain?
23}
24
25var (
26 // Main registry: address → CollectionInfo
27 collections avl.Tree // key: string(address), value: *CollectionInfo
28
29 // Admin addresses that can verify collections
30 admins avl.Tree // key: string(address), value: bool
31
32 // Category index for faster filtering
33 categoriesIndex avl.Tree // key: category, value: []address
34
35 // Owner of the registry
36 owner address
37
38 // Registration fee (in ugnot)
39 registrationFee int64 = 1000000 // 1 GNOT default
40
41 // Stats
42 totalCollections int
43 verifiedCount int
44)
45
46func init() {
47 owner = runtime.PreviousRealm().Address()
48 admins.Set(owner.String(), true)
49}
50
51// RegisterCollection - Register a new NFT collection
52// The collection realm must call this function itself
53func RegisterCollection(
54 name string, // ← Ajouté
55 symbol string, // ← Ajouté
56 category string,
57 description string,
58 externalURL string,
59 getter grc721.NFTGetter,
60) {
61 caller := runtime.PreviousRealm().Address()
62
63 // Check if collection already registered
64 if collections.Has(caller.String()) {
65 panic("Collection already registered")
66 }
67
68 if getter == nil {
69 panic("NFT getter function is required")
70 }
71
72 // Validate inputs
73 if name == "" || symbol == "" {
74 panic("Name and symbol cannot be empty")
75 }
76
77 // Note: We trust the caller to provide correct name/symbol
78 // since they match their own NFT collection
79
80 // Check if getter works
81 nftInstance := getter()
82 if nftInstance == nil {
83 panic("NFT getter returned nil")
84 }
85
86 // Check if implements IGRC721MetadataOnchain for rich metadata
87 var supportsMetadata bool
88 if _, ok := nftInstance.(grc721.IGRC721MetadataOnchain); ok {
89 supportsMetadata = true
90 }
91
92 // Create collection info
93 info := &CollectionInfo{
94 Address: caller,
95 Name: name,
96 Symbol: symbol,
97 Creator: caller,
98 RegisteredAt: runtime.ChainHeight(),
99 Verified: false,
100 Category: category,
101 Description: description,
102 ExternalURL: externalURL,
103 NFTGetter: getter,
104 SupportsMetadata: supportsMetadata,
105 }
106
107 collections.Set(caller.String(), info)
108 totalCollections++
109}
110
111// IsRegistered - Check if a collection is registered
112func IsRegistered(collectionAddr address) bool {
113 return collections.Has(collectionAddr.String())
114}
115
116// GetCollection - Get collection info by address
117func GetCollection(collectionAddr address) *CollectionInfo {
118 val, exists := collections.Get(collectionAddr.String())
119 if !exists {
120 return nil
121 }
122 return val.(*CollectionInfo)
123}
124
125// GetNFTGetter - Get the NFT getter function for a collection
126// This is the main function marketplaces will use
127func GetNFTGetter(collectionAddr address) (grc721.NFTGetter, bool) {
128 info := GetCollection(collectionAddr)
129 if info == nil {
130 return nil, false
131 }
132 return info.NFTGetter, true
133}
134
135// GetTokenMetadata - Get metadata for a specific token (if collection supports it)
136func GetTokenMetadata(collectionAddr address, tokenId grc721.TokenID) (grc721.Metadata, error) {
137 info := GetCollection(collectionAddr)
138 if info == nil {
139 panic("Collection not registered")
140 }
141
142 if !info.SupportsMetadata {
143 panic("Collection does not support onchain metadata")
144 }
145
146 nftInstance := info.NFTGetter()
147 metadataCollection := nftInstance.(grc721.IGRC721MetadataOnchain)
148
149 return metadataCollection.TokenMetadata(tokenId)
150}
151
152// VerifyCollection - Admin function to verify a collection
153func VerifyCollection(collectionAddr address) {
154 caller := runtime.PreviousRealm().Address()
155
156 if !isAdmin(caller) {
157 panic("Only admins can verify collections")
158 }
159
160 info := GetCollection(collectionAddr)
161 if info == nil {
162 panic("Collection not found")
163 }
164
165 if !info.Verified {
166 info.Verified = true
167 verifiedCount++
168 collections.Set(collectionAddr.String(), info)
169 }
170}
171
172// UnverifyCollection - Admin function to remove verification
173func UnverifyCollection(collectionAddr address) {
174 caller := runtime.PreviousRealm().Address()
175
176 if !isAdmin(caller) {
177 panic("Only admins can unverify collections")
178 }
179
180 info := GetCollection(collectionAddr)
181 if info == nil {
182 panic("Collection not found")
183 }
184
185 if info.Verified {
186 info.Verified = false
187 verifiedCount--
188 collections.Set(collectionAddr.String(), info)
189 }
190}
191
192// UpdateCollectionInfo - Collection creator can update mutable metadata
193func UpdateCollectionInfo(category, description, externalURL string) {
194 caller := runtime.PreviousRealm().Address()
195
196 info := GetCollection(caller)
197 if info == nil {
198 panic("Collection not registered")
199 }
200
201 info.Category = category
202 info.Description = description
203 info.ExternalURL = externalURL
204 collections.Set(caller.String(), info)
205}
206
207// Helper functions
208
209func isAdmin(addr address) bool {
210 _, exists := admins.Get(addr.String())
211 return exists
212}