Smart Contract

NiftoryNFTRegistry

A.d5af76f998001f90.NiftoryNFTRegistry

Deployed

3h ago
Mar 01, 2026, 11:29:37 PM UTC

Dependents

0 imports
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