Smart Contract
NiftoryNFTRegistry
A.d5af76f998001f90.NiftoryNFTRegistry
1/*
2NFTRegistry
3
4Niftory NFTs should ideally be functionally the same. This would allow
5other applications to refer to any Niftory NFT without having to know about
6the properties of any individual NFT project. For example, a developer should
7not be required to import code from a specific NFT project just to get the
8path of where a collection should be found in a users account.
9
10To make this possible, this NFTRegistry associates a single String identifier
11to struct of metadata required for the type of agnostic access described above.
12This includes
13
14- Paths of NonFungibleToken.Collection
15- Paths of the NFT project's Manager
16- Paths of the MutableSetManager
17- Paths of the MetadataViewManager
18
19*/
20
21import NonFungibleToken from 0x1d7e57aa55817448
22import MetadataViews from 0x1d7e57aa55817448
23
24import MutableMetadataSetManager from 0x7ec1f607f0872a9e
25import MetadataViewsManager from 0x7ec1f607f0872a9e
26
27import NiftoryNonFungibleToken from 0x7ec1f607f0872a9e
28
29pub contract NiftoryNFTRegistry {
30
31 // ========================================================================
32 // Constants
33 // ========================================================================
34
35 // Suggested paths where this Registry could be stored
36 pub let PUBLIC_PATH: PublicPath
37 pub let PRIVATE_PATH: PrivatePath
38 pub let STORAGE_PATH: StoragePath
39
40 // Suggested RegistryItem path suffixes
41 pub let NFT_COLLECTION_PATH_SUFFIX: String
42 pub let NFT_MANAGER_PATH_SUFFIX: String
43 pub let SET_MANAGER_PATH_SUFFIX: String
44 pub let METADATA_VIEWS_MANAGER_PATH_SUFFIX: String
45
46 // ========================================================================
47 // RegistryItem
48 // ========================================================================
49
50 // Struct to co-locate Path related metadata for a stored resource
51 pub struct Paths {
52
53 // Public path of capability
54 pub let public: PublicPath
55
56 // Private path of capability
57 pub let private: PrivatePath
58
59 // Storage path of resource
60 pub let storage: StoragePath
61
62 init(
63 public: PublicPath,
64 private: PrivatePath,
65 storage: StoragePath
66 ) {
67 self.public = public
68 self.private = private
69 self.storage = storage
70 }
71 }
72
73 pub struct StoredResource {
74
75 // Account where resource is stored
76 pub let account: Address
77
78 // Relevant paths of stored resource
79 pub let paths: Paths
80
81 init(account: Address, paths: Paths) {
82 self.account = account
83 self.paths = paths
84 }
85 }
86
87 pub struct Record {
88
89 // Address of the actual contract
90 pub let contractAddress: Address
91
92 // NFT collection's standard paths
93 pub let collectionPaths: Paths
94
95 // NFT's Manager path metadata
96 pub let nftManager: StoredResource
97
98 // MutableMetadataSetManager path metadata
99 pub let setManager: StoredResource
100
101 // MetadataViewsManager path metadata
102 pub let metadataViewsManager: StoredResource
103
104 init(
105 contractAddress: Address,
106 collectionPaths: Paths,
107 nftManager: StoredResource,
108 setManager: StoredResource,
109 metadataViewsManager: StoredResource
110 ) {
111 self.contractAddress = contractAddress
112 self.collectionPaths = collectionPaths
113 self.nftManager = nftManager
114 self.setManager = setManager
115 self.metadataViewsManager = metadataViewsManager
116 }
117 }
118
119 // ========================================================================
120 // Registry
121 // ========================================================================
122
123 pub resource interface Public {
124
125 // Return all entries from the registry
126 pub fun all(): {String: Record}
127
128 // Return information for a particular brand in the registry
129 pub fun infoFor(_ brand: String): Record
130 }
131
132 pub resource interface Private {
133
134 // Get a modifiable ref of the underlying registry
135 pub fun auth(): auth &{String: Record}
136
137 // Register a new brand
138 pub fun register(brand: String, entry: Record)
139
140 // Deregister an existing brand
141 pub fun deregister(_ brand: String)
142 }
143
144 pub resource Registry: Public, Private {
145
146 // ========================================================================
147 // Attributes
148 // ========================================================================
149
150 // The registry is stored as a simple String -> RegistryItem map, so the
151 // admin here must be careful to keep track of the keys present
152 access(self) let _registry: {String: Record}
153
154 // ========================================================================
155 // Public
156 // ========================================================================
157
158 pub fun all(): {String: Record} {
159 return self._registry
160 }
161
162 pub fun infoFor(_ brand: String): Record {
163 pre {
164 self._registry.containsKey(brand) :
165 "NFT ".concat(brand).concat(" is not registered")
166 }
167 return self._registry[brand]!
168 }
169
170 // ========================================================================
171 // Private
172 // ========================================================================
173
174 pub fun auth(): auth &{String: Record} {
175 return &self._registry as auth &{String: Record}
176 }
177
178 pub fun register(brand: String, entry: Record) {
179 self._registry[brand] = entry
180 }
181
182 pub fun deregister(_ brand: String) {
183 pre {
184 self._registry.containsKey(brand) :
185 "NFT ".concat(brand).concat(" is not registered")
186 }
187 self._registry.remove(key: brand)
188 }
189
190 // ========================================================================
191 // init/destroy
192 // ========================================================================
193
194 init() {
195 self._registry = {}
196 }
197 }
198
199 // ========================================================================
200 // Contract functions
201 // ========================================================================
202
203 // Create a new Registry
204 pub fun create(): @Registry {
205 return <-create Registry()
206 }
207
208 // Helper to generate a Paths struct with common public/private/storage paths
209 pub fun generatePaths(
210 prefix: String,
211 suffix: String
212 ): Paths {
213 let public = PublicPath(identifier: prefix.concat(suffix))!
214 let private = PrivatePath(identifier: prefix.concat(suffix))!
215 let storage = StoragePath(identifier: prefix.concat(suffix))!
216 return Paths(
217 public: public,
218 private: private,
219 storage: storage
220 )
221 }
222
223 // Generate collection paths
224 pub fun generateCollectionPaths(project: String): Paths {
225 return self.generatePaths(
226 prefix: project,
227 suffix: self.NFT_COLLECTION_PATH_SUFFIX
228 )
229 }
230
231 // Generate NFT manager paths
232 pub fun generateNFTManagerPaths(project: String): Paths {
233 return self.generatePaths(
234 prefix: project,
235 suffix: self.NFT_MANAGER_PATH_SUFFIX
236 )
237 }
238
239 // Generate MutableMetadataSetManager paths
240 pub fun generateSetManagerPaths(project: String): Paths {
241 return self.generatePaths(
242 prefix: project,
243 suffix: self.SET_MANAGER_PATH_SUFFIX
244 )
245 }
246
247 // Generate MetadataViewsManager paths
248 pub fun generateMetadataViewsManagerPaths(project: String): Paths {
249 return self.generatePaths(
250 prefix: project,
251 suffix: self.METADATA_VIEWS_MANAGER_PATH_SUFFIX
252 )
253 }
254
255 // Default way to construct a RegistryItem, using suggested contract suffixes
256 pub fun generateRecordFull(
257 contractAddress: Address,
258 nftManagerAddress: Address,
259 setManagerAddress: Address,
260 metadataViewsManagerAddress: Address,
261 project: String
262 ): Record {
263 let collectionPaths = self.generateCollectionPaths(project: project)
264 let nftManager = StoredResource(
265 account: nftManagerAddress,
266 paths: self.generateNFTManagerPaths(project: project)
267 )
268 let setManager = StoredResource(
269 account: setManagerAddress,
270 paths: self.generateSetManagerPaths(project: project)
271 )
272 let metadataViewsManager = StoredResource(
273 account: metadataViewsManagerAddress,
274 paths: self.generateMetadataViewsManagerPaths(project: project)
275 )
276 return Record(
277 contractAddress: contractAddress,
278 collectionPaths: collectionPaths,
279 nftManager: nftManager,
280 setManager: setManager,
281 metadataViewsManager: metadataViewsManager
282 )
283 }
284
285 // Default way to construct a RegistryItem, using suggested contract suffixes
286 pub fun generateRecord(account: Address, project: String): Record {
287 return self.generateRecordFull(
288 contractAddress: account,
289 nftManagerAddress: account,
290 setManagerAddress: account,
291 metadataViewsManagerAddress: account,
292 project: project
293 )
294 }
295
296 // Nicely formatted error for a resource not found that's listed in the
297 // registry
298 pub fun _notFoundError(
299 _ registryAddress: Address,
300 _ brand: String,
301 _ entity: String
302 ): String {
303 return entity
304 .concat(" not found for registry at ")
305 .concat(registryAddress.toString())
306 .concat(" for brand ")
307 .concat(brand)
308 .concat(".")
309 }
310
311 // Get a registry record for a registry address and brand
312 pub fun getRegistryRecord(
313 _ registryAddress: Address,
314 _ brand: String
315 ): Record {
316 let registry = getAccount(registryAddress)
317 .getCapability<&{Public}>(self.PUBLIC_PATH)
318 .borrow()
319 ?? panic(self._notFoundError(registryAddress, brand, "Registry Record"))
320 return registry.infoFor(brand)
321 }
322
323 // Get the collection paths for a registry address and brand
324 pub fun getCollectionPaths(
325 _ registryAddress: Address,
326 _ brand: String
327 ): Paths {
328 let record = self.getRegistryRecord(registryAddress, brand)
329 return record.collectionPaths
330 }
331
332 // Get the NFT Manager for a registry address and brand
333 pub fun getNFTManagerPublic(_ registryAddress: Address, _ brand: String):
334 &{NiftoryNonFungibleToken.ManagerPublic} {
335 let record = self.getRegistryRecord(registryAddress, brand)
336 let manager = getAccount(record.nftManager.account)
337 .getCapability<&{NiftoryNonFungibleToken.ManagerPublic}>(
338 record.nftManager.paths.public
339 )
340 return manager.borrow()
341 ?? panic(self._notFoundError(registryAddress, brand, "NFT Manager"))
342 }
343
344 // Get the MutableMetadataSetManager for a registry address and brand
345 pub fun getSetManagerPublic(_ registryAddress: Address, _ brand: String):
346 &MutableMetadataSetManager.Manager{MutableMetadataSetManager.Public} {
347 let record = self.getRegistryRecord(registryAddress, brand)
348 let manager = getAccount(record.setManager.account)
349 .getCapability<&
350 MutableMetadataSetManager.Manager{MutableMetadataSetManager.Public}
351 >(
352 record.setManager.paths.public
353 )
354 return manager.borrow()
355 ?? panic(self._notFoundError(registryAddress, brand, "MutableMetadataSetManager"))
356 }
357
358 // Get the MetadataViewsManager for a registry address and brand
359 pub fun getMetadataViewsManagerPublic(
360 _ registryAddress: Address,
361 _ brand: String
362 ):
363 &MetadataViewsManager.Manager{MetadataViewsManager.Public} {
364 let record = self.getRegistryRecord(registryAddress, brand)
365 let manager = getAccount(record.metadataViewsManager.account)
366 .getCapability<&
367 MetadataViewsManager.Manager{MetadataViewsManager.Public}
368 >(
369 record.metadataViewsManager.paths.public
370 )
371 return manager.borrow()
372 ?? panic(self._notFoundError(registryAddress, brand, "MetadataViewsManager"))
373 }
374
375 // Get NFTCollectionData for a registry address and brand
376 pub fun buildNFTCollectionData(
377 _ registryAddress: Address,
378 _ brand: String,
379 _ createEmptyCollection: ((): @NonFungibleToken.Collection)
380 ): MetadataViews.NFTCollectionData {
381 let record = self.getRegistryRecord(registryAddress, brand)
382 let paths = record.collectionPaths
383 return MetadataViews.NFTCollectionData(
384 storagePath: paths.storage,
385 publicPath: paths.public,
386 providerPath: paths.private,
387 publicCollection: Type<&{
388 NonFungibleToken.CollectionPublic,
389 NiftoryNonFungibleToken.CollectionPublic
390 }>(),
391 publicLinkedType: Type<&{
392 NonFungibleToken.Receiver,
393 NonFungibleToken.CollectionPublic,
394 MetadataViews.ResolverCollection,
395 NiftoryNonFungibleToken.CollectionPublic
396 }>(),
397 providerLinkedType: Type<&{
398 NonFungibleToken.Provider,
399 NonFungibleToken.Receiver,
400 NonFungibleToken.CollectionPublic,
401 MetadataViews.ResolverCollection,
402 NiftoryNonFungibleToken.CollectionPublic
403 }>(),
404 createEmptyCollectionFunction: createEmptyCollection
405 )
406 }
407
408 // Initialize contract constants
409 init() {
410 self.PUBLIC_PATH = /public/niftorynftregistry
411 self.PRIVATE_PATH = /private/niftorynftregistry
412 self.STORAGE_PATH = /storage/niftorynftregistry
413
414 self.NFT_COLLECTION_PATH_SUFFIX = "_nft_collection"
415 self.NFT_MANAGER_PATH_SUFFIX = "_nft_manager"
416 self.SET_MANAGER_PATH_SUFFIX = "_set_manager"
417 self.METADATA_VIEWS_MANAGER_PATH_SUFFIX = "_metadata_views_manager"
418 }
419}
420
421
422
423
424
425
426
427
428
429
430
431