Search Apps Documentation Source Content File Folder Download Copy Actions Download

registry.gno

6.01 Kb · 222 lines
  1package nftregistry
  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	category string,
 55	description string,
 56	externalURL string,
 57	getter grc721.NFTGetter,
 58) {
 59	caller := runtime.PreviousRealm().Address()
 60
 61	// Check if collection already registered
 62	if collections.Has(caller.String()) {
 63		panic("Collection already registered")
 64	}
 65
 66	if getter == nil {
 67		panic("NFT getter function is required")
 68	}
 69
 70	// Get the NFT instance to extract name and symbol
 71	nftInstance := getter()
 72
 73	// Try to get collection metadata
 74	var name, symbol string
 75	var supportsMetadata bool
 76
 77	// Check if implements IGRC721CollectionMetadata
 78	if metadataCollection, ok := nftInstance.(grc721.IGRC721CollectionMetadata); ok {
 79		name = metadataCollection.Name()
 80		symbol = metadataCollection.Symbol()
 81	} else {
 82		panic("Collection must implement IGRC721CollectionMetadata (Name() and Symbol())")
 83	}
 84
 85	// Check if implements IGRC721MetadataOnchain for rich metadata
 86	if _, ok := nftInstance.(grc721.IGRC721MetadataOnchain); ok {
 87		supportsMetadata = true
 88	}
 89
 90	// Validate inputs
 91	if name == "" || symbol == "" {
 92		panic("Name and symbol cannot be empty")
 93	}
 94
 95	// Note: Uncomment for production with payment
 96	// sent := banker.OriginSend()
 97	// amount := sent.AmountOf("ugnot")
 98	// if amount < registrationFee {
 99	//     panic(ufmt.Sprintf("Insufficient registration fee. Required: %d ugnot", registrationFee))
100	// }
101
102	// Create collection info
103	info := &CollectionInfo{
104		Address:          caller,
105		Name:             name,
106		Symbol:           symbol,
107		Creator:          caller,
108		RegisteredAt:     runtime.ChainHeight(),
109		Verified:         false,
110		Category:         category,
111		Description:      description,
112		ExternalURL:      externalURL,
113		NFTGetter:        getter,
114		SupportsMetadata: supportsMetadata,
115	}
116
117	collections.Set(caller.String(), info)
118	totalCollections++
119}
120
121// IsRegistered - Check if a collection is registered
122func IsRegistered(collectionAddr address) bool {
123	return collections.Has(collectionAddr.String())
124}
125
126// GetCollection - Get collection info by address
127func GetCollection(collectionAddr address) *CollectionInfo {
128	val, exists := collections.Get(collectionAddr.String())
129	if !exists {
130		return nil
131	}
132	return val.(*CollectionInfo)
133}
134
135// GetNFTGetter - Get the NFT getter function for a collection
136// This is the main function marketplaces will use
137func GetNFTGetter(collectionAddr address) (grc721.NFTGetter, bool) {
138	info := GetCollection(collectionAddr)
139	if info == nil {
140		return nil, false
141	}
142	return info.NFTGetter, true
143}
144
145// GetTokenMetadata - Get metadata for a specific token (if collection supports it)
146func GetTokenMetadata(collectionAddr address, tokenId grc721.TokenID) (grc721.Metadata, error) {
147	info := GetCollection(collectionAddr)
148	if info == nil {
149		panic("Collection not registered")
150	}
151
152	if !info.SupportsMetadata {
153		panic("Collection does not support onchain metadata")
154	}
155
156	nftInstance := info.NFTGetter()
157	metadataCollection := nftInstance.(grc721.IGRC721MetadataOnchain)
158
159	return metadataCollection.TokenMetadata(tokenId)
160}
161
162// VerifyCollection - Admin function to verify a collection
163func VerifyCollection(collectionAddr address) {
164	caller := runtime.PreviousRealm().Address()
165
166	if !isAdmin(caller) {
167		panic("Only admins can verify collections")
168	}
169
170	info := GetCollection(collectionAddr)
171	if info == nil {
172		panic("Collection not found")
173	}
174
175	if !info.Verified {
176		info.Verified = true
177		verifiedCount++
178		collections.Set(collectionAddr.String(), info)
179	}
180}
181
182// UnverifyCollection - Admin function to remove verification
183func UnverifyCollection(collectionAddr address) {
184	caller := runtime.PreviousRealm().Address()
185
186	if !isAdmin(caller) {
187		panic("Only admins can unverify collections")
188	}
189
190	info := GetCollection(collectionAddr)
191	if info == nil {
192		panic("Collection not found")
193	}
194
195	if info.Verified {
196		info.Verified = false
197		verifiedCount--
198		collections.Set(collectionAddr.String(), info)
199	}
200}
201
202// UpdateCollectionInfo - Collection creator can update mutable metadata
203func UpdateCollectionInfo(category, description, externalURL string) {
204	caller := runtime.PreviousRealm().Address()
205
206	info := GetCollection(caller)
207	if info == nil {
208		panic("Collection not registered")
209	}
210
211	info.Category = category
212	info.Description = description
213	info.ExternalURL = externalURL
214	collections.Set(caller.String(), info)
215}
216
217// Helper functions
218
219func isAdmin(addr address) bool {
220	_, exists := admins.Get(addr.String())
221	return exists
222}