Smart Contract

The_Next_Cartel_NFT

A.329feb3ab062d289.The_Next_Cartel_NFT

Deployed

1d ago
Feb 26, 2026, 09:44:05 PM UTC

Dependents

0 imports
1
2import FungibleToken from 0xf233dcee88fe0abe
3import NonFungibleToken from 0x1d7e57aa55817448
4import MetadataViews from 0x1d7e57aa55817448
5import ViewResolver from 0x1d7e57aa55817448
6
7access(all) contract The_Next_Cartel_NFT: NonFungibleToken {
8
9    // An entitlement to perform Admin actions on this contract
10    access(all) entitlement Administrator
11
12    // The_Next_Cartel_NFT Events
13    //
14
15    // Emitted when an NFT is minted
16    access(all) event Minted(id: UInt64, setId: UInt32, seriesId: UInt32)
17
18    // Events for Series-related actions
19    //
20    // Emitted when a new Series is created
21    access(all) event SeriesCreated(seriesId: UInt32)
22    // Emitted when a Series is sealed, meaning Series metadata
23    // cannot be updated
24    access(all) event SeriesSealed(seriesId: UInt32)
25    // Emitted when a Series' metadata is updated
26    access(all) event SeriesMetadataUpdated(seriesId: UInt32)
27
28    // Events for Set-related actions
29    //
30    // Emitted when a new Set is created
31    access(all) event SetCreated(seriesId: UInt32, setId: UInt32)
32    // Emitted when a Set's metadata is updated
33    access(all) event SetMetadataUpdated(seriesId: UInt32, setId: UInt32)
34
35    // Named Paths
36    //
37    access(all) let CollectionStoragePath: StoragePath
38    access(all) let CollectionPublicPath: PublicPath
39    access(all) let AdminStoragePath: StoragePath
40
41    // Variable size dictionary of SetData structs
42    access(self) var setData: {UInt32: NFTSetData}
43
44    // Variable size dictionary of SeriesData structs
45    access(self) var seriesData: {UInt32: SeriesData}
46
47    // Variable size dictionary of Series resources
48    access(self) var series: @{UInt32: Series}
49
50
51    // An NFTSetData is a Struct that holds metadata associated with
52    // a specific NFT Set.
53    access(all) struct NFTSetData {
54
55        // Unique ID for the Set
56        access(all) let setId: UInt32
57
58        // Series ID the Set belongs to
59        access(all) let seriesId: UInt32
60
61        // Maximum number of editions that can be minted in this Set
62        access(all) let maxEditions: UInt32
63                  
64        // The JSON metadata for each NFT edition can be stored off-chain on IPFS.
65        // This is an optional dictionary of IPFS hashes, which will allow marketplaces
66        // to pull the metadata for each NFT edition
67        access(self) var ipfsMetadataHashes: {UInt32: String}
68
69        // Set level metadata
70        // Dictionary of metadata key value pairs
71        access(self) var metadata: {String: String}
72        
73        init(
74            setId: UInt32,
75            seriesId: UInt32,
76            maxEditions: UInt32,
77            ipfsMetadataHashes: {UInt32: String},
78            metadata: {String: String}) {
79
80            self.setId = setId
81            self.seriesId = seriesId
82            self.maxEditions = maxEditions
83            self.metadata = metadata
84            self.ipfsMetadataHashes = ipfsMetadataHashes
85        }
86
87        access(all) fun getIpfsMetadataHash(editionNum: UInt32): String? {
88            return self.ipfsMetadataHashes[editionNum]
89        }
90
91        access(all) fun getMetadata(): {String: String} {
92            return self.metadata
93        }
94
95        access(all) fun getMetadataField(field: String): String? {
96            return self.metadata[field]
97        }
98    }
99
100    // A SeriesData is a struct that groups metadata for a 
101    // a related group of NFTSets.
102    access(all) struct SeriesData {
103
104        // Unique ID for the Series
105        access(all) let seriesId: UInt32
106
107        // Dictionary of metadata key value pairs
108        access(self) var metadata: {String: String}
109
110        init(
111            seriesId: UInt32,
112            metadata: {String: String}) {
113            self.seriesId = seriesId
114            self.metadata = metadata
115        }
116
117        access(all) fun getMetadata(): {String: String} {
118            return self.metadata
119        }
120    }
121
122
123    // A Series is special resource type that contains functions to mint The_Next_Cartel_NFT NFTs, 
124    // add NFTSets, update NFTSet and Series metadata, and seal Series.
125	access(all) resource Series {
126
127        // Unique ID for the Series
128        access(all) let seriesId: UInt32
129
130        // Array of NFTSets that belong to this Series
131        access(all) var setIds: [UInt32]
132
133        // Series sealed state
134        access(all) var seriesSealedState: Bool;
135
136        // Set sealed state
137        access(self) var setSealedState: {UInt32: Bool};
138
139        // Current number of editions minted per Set
140        access(all) var numberEditionsMintedPerSet: {UInt32: UInt32}
141
142        init(
143            seriesId: UInt32,
144            metadata: {String: String}) {
145
146            self.seriesId = seriesId
147            self.seriesSealedState = false
148            self.numberEditionsMintedPerSet = {}
149            self.setIds = []
150            self.setSealedState = {}
151
152            The_Next_Cartel_NFT.seriesData[seriesId] = SeriesData(
153                    seriesId: seriesId,
154                    metadata: metadata
155            )
156
157            emit SeriesCreated(seriesId: seriesId)   
158        }
159
160        access(all) fun addNftSet(
161            setId: UInt32,
162            maxEditions: UInt32,
163            ipfsMetadataHashes: {UInt32: String},
164            metadata: {String: String}) {
165            pre {
166                self.setIds.contains(setId) == false: "The Set has already been added to the Series."
167            }
168
169            // Create the new Set struct
170            var newNFTSet = NFTSetData(
171                setId: setId,
172                seriesId: self.seriesId,
173                maxEditions: maxEditions,
174                ipfsMetadataHashes: ipfsMetadataHashes,
175                metadata: metadata
176            )
177
178            // Add the NFTSet to the array of Sets
179            self.setIds.append(setId)
180
181            // Initialize the NFT edition count to zero
182            self.numberEditionsMintedPerSet[setId] = 0
183
184            // Store it in the sets mapping field
185            The_Next_Cartel_NFT.setData[setId] = newNFTSet
186
187            emit SetCreated(seriesId: self.seriesId, setId: setId)
188        }
189
190        // updateSeriesMetadata
191        // For practical reasons, a short period of time is given to update metadata
192        // following Series creation or minting of the NFT editions. Once the Series is
193        // sealed, no updates to the Series metadata will be possible - the information
194        // is permanent and immutable.
195        access(all) fun updateSeriesMetadata(metadata: {String: String}) {
196            pre {
197                self.seriesSealedState == false:
198                    "The Series is permanently sealed. No metadata updates can be made."
199            }
200            let newSeriesMetadata = SeriesData(
201                    seriesId: self.seriesId,
202                    metadata: metadata
203            )  
204            // Store updated Series in the Series mapping field
205            The_Next_Cartel_NFT.seriesData[self.seriesId] = newSeriesMetadata
206
207            emit SeriesMetadataUpdated(seriesId: self.seriesId)
208        }
209
210        // updateSetMetadata
211        // For practical reasons, a short period of time is given to update metadata
212        // following Set creation or minting of the NFT editions. Once the Series is
213        // sealed, no updates to the Set metadata will be possible - the information
214        // is permanent and immutable.
215        access(all) fun updateSetMetadata(
216            setId: UInt32,
217            maxEditions: UInt32,
218            ipfsMetadataHashes: {UInt32: String},
219            metadata: {String: String}) {
220            pre {
221                self.seriesSealedState == false:
222                    "The Series is permanently sealed. No metadata updates can be made."
223                self.setIds.contains(setId) == true: "The Set is not part of this Series."
224            }
225            let newSetMetadata = NFTSetData(
226                setId: setId,
227                seriesId: self.seriesId,
228                maxEditions: maxEditions,
229                ipfsMetadataHashes: ipfsMetadataHashes,
230                metadata: metadata
231            )
232            // Store updated Set in the Sets mapping field
233            The_Next_Cartel_NFT.setData[setId] = newSetMetadata
234
235            emit SetMetadataUpdated(seriesId: self.seriesId, setId: setId)
236        }
237
238		// mintThe_Next_Cartel_NFT
239        // Mints a new NFT with a new ID
240		// and deposits it in the recipients collection using their collection reference
241        //
242	    access(all) fun mintThe_Next_Cartel_NFT(
243            recipient: &{NonFungibleToken.CollectionPublic},
244            tokenId: UInt64,
245            setId: UInt32) {
246            
247            pre {
248                self.numberEditionsMintedPerSet[setId] != nil: "The Set does not exist."
249                self.numberEditionsMintedPerSet[setId]! <= The_Next_Cartel_NFT.getSetMaxEditions(setId: setId)!:
250                    "Set has reached maximum NFT edition capacity."
251            }
252
253            // Gets the number of editions that have been minted so far in 
254            // this set
255            let editionNum: UInt32 = self.numberEditionsMintedPerSet[setId]! + 1
256
257			// deposit it in the recipient's account using their reference
258			recipient.deposit(token: <-create The_Next_Cartel_NFT.NFT(
259                tokenId: tokenId,
260                setId: setId,
261                editionNum: editionNum
262            ))
263
264            // Update the count of Editions minted in the set
265            self.numberEditionsMintedPerSet[setId] = editionNum
266        }
267
268        // mintEditionThe_Next_Cartel_NFT
269        // Mints a new NFT with a new ID and specific edition Num (random open edition)
270		// and deposits it in the recipients collection using their collection reference
271        //
272	    access(all) fun mintEditionThe_Next_Cartel_NFT(
273            recipient: &{NonFungibleToken.CollectionPublic},
274            tokenId: UInt64,
275            setId: UInt32,
276            edition: UInt32) {
277            
278            pre {
279                self.numberEditionsMintedPerSet[setId] != nil: "The Set does not exist."
280                self.numberEditionsMintedPerSet[setId]! <= The_Next_Cartel_NFT.getSetMaxEditions(setId: setId)!:
281                    "Set has reached maximum NFT edition capacity."
282            }
283
284			// deposit it in the recipient's account using their reference
285			recipient.deposit(token: <-create The_Next_Cartel_NFT.NFT(
286                tokenId: tokenId,
287                setId: setId,
288                editionNum: edition
289            ))
290
291            // Update the count of Editions minted in the set
292            self.numberEditionsMintedPerSet[setId] = self.numberEditionsMintedPerSet[setId]! + 1
293        }
294
295        // batchMintThe_Next_Cartel_NFT
296        // Mints multiple new NFTs given and deposits the NFTs
297        // into the recipients collection using their collection reference
298		access(all) fun batchMintThe_Next_Cartel_NFT(
299            recipient: &{NonFungibleToken.CollectionPublic},
300            setId: UInt32,
301            tokenIds: [UInt64]) {
302
303            pre {
304                tokenIds.length > 0:
305                    "Number of token Ids must be > 0"
306            }
307
308            for tokenId in tokenIds {
309                self.mintThe_Next_Cartel_NFT(
310                    recipient: recipient,
311                    tokenId: tokenId,
312                    setId: setId
313                )
314            }
315		}
316
317        // sealSeries
318        // Once a series is sealed, the metadata for the NFTs in the Series can no
319        // longer be updated
320        //
321        access(all) fun sealSeries() {
322            pre {
323                self.seriesSealedState == false: "The Series is already sealed"
324            }
325            self.seriesSealedState = true
326
327            emit SeriesSealed(seriesId: self.seriesId)
328        }
329	}
330
331    // A resource that represents the The_Next_Cartel_NFT NFT
332    //
333    access(all) resource NFT: NonFungibleToken.NFT {
334        // The token's ID
335        access(all) let id: UInt64
336
337        // The Set id references this NFT belongs to
338        access(all) let setId: UInt32
339
340        // The specific edition number for this NFT
341        access(all) let editionNum: UInt32
342
343        // initializer
344        //
345        init(
346          tokenId: UInt64,
347          setId: UInt32,
348          editionNum: UInt32) {
349
350            self.id = tokenId
351            self.setId = setId
352            self.editionNum = editionNum
353
354            let seriesId = The_Next_Cartel_NFT.getSetSeriesId(setId: setId)!
355
356            emit Minted(id: self.id, setId: setId, seriesId: seriesId)
357        }
358
359        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
360            return <- The_Next_Cartel_NFT.createEmptyCollection(nftType: Type<@The_Next_Cartel_NFT.NFT>())
361        }
362
363        access(all) view fun getViews(): [Type] {
364            return [
365                Type<MetadataViews.Display>(),
366                Type<MetadataViews.Royalties>(),
367                Type<MetadataViews.Editions>(),
368                Type<MetadataViews.ExternalURL>(),
369                Type<MetadataViews.NFTCollectionData>(),
370                Type<MetadataViews.NFTCollectionDisplay>(),
371                Type<MetadataViews.Serial>(),
372                Type<MetadataViews.Traits>(),
373                Type<MetadataViews.Medias>()
374            ]
375        }
376        access(all) fun resolveView(_ view: Type): AnyStruct? {
377            switch view {
378                case Type<MetadataViews.Display>():
379                    return MetadataViews.Display(
380                        name: The_Next_Cartel_NFT.getSetMetadataByField(setId: self.setId, field: "name")!,
381                        description: The_Next_Cartel_NFT.getSetMetadataByField(setId: self.setId, field: "description")!,
382                        thumbnail: MetadataViews.HTTPFile(
383                            url: The_Next_Cartel_NFT.getSetMetadataByField(setId: self.setId, field: "preview")!
384                        )
385                    )
386                case Type<MetadataViews.Serial>():
387                    return MetadataViews.Serial(
388                        self.id
389                    )
390                case Type<MetadataViews.Editions>():
391                    let maxEditions = The_Next_Cartel_NFT.setData[self.setId]?.maxEditions ?? 0
392                    let editionName = The_Next_Cartel_NFT.getSetMetadataByField(setId: self.setId, field: "name")!
393                    let editionInfo = MetadataViews.Edition(name: editionName, number: UInt64(self.editionNum), max: maxEditions > 0 ? UInt64(maxEditions) : nil)
394                    let editionList: [MetadataViews.Edition] = [editionInfo]
395                    return MetadataViews.Editions(
396                        editionList
397                    )
398                case Type<MetadataViews.ExternalURL>():
399                    if let externalBaseURL = The_Next_Cartel_NFT.getSetMetadataByField(setId: self.setId, field: "external_token_base_url") {
400                        return MetadataViews.ExternalURL(externalBaseURL.concat("/").concat(self.id.toString()))
401                    }
402                    return MetadataViews.ExternalURL("")
403                case Type<MetadataViews.Royalties>():
404                    let royalties: [MetadataViews.Royalty] = []
405                    // There is only a legacy {String: String} dictionary to store royalty information.
406                    // There may be multiple royalty cuts defined per NFT. Pull each royalty
407                    // based on keys that have the "royalty_addr_" prefix in the dictionary.
408                    for metadataKey in The_Next_Cartel_NFT.getSetMetadata(setId: self.setId)!.keys {
409                        // For efficiency, only check keys that are > 13 chars, which is the length of "royalty_addr_" key
410                        if metadataKey.length >= 13 {
411                            if metadataKey.slice(from: 0, upTo: 13) == "royalty_addr_" {
412                                // A royalty has been found. Use the suffix from the key for the royalty name.
413                                let royaltyName = metadataKey.slice(from: 13, upTo: metadataKey.length)
414                                let royaltyAddress = The_Next_Cartel_NFT.convertStringToAddress(The_Next_Cartel_NFT.getSetMetadataByField(setId: self.setId, field: "royalty_addr_".concat(royaltyName))!)!
415                                let royaltyReceiver: PublicPath = PublicPath(identifier: The_Next_Cartel_NFT.getSetMetadataByField(setId: self.setId, field: "royalty_rcv_".concat(royaltyName))!)!
416                                let royaltyCut = The_Next_Cartel_NFT.getSetMetadataByField(setId: self.setId, field: "royalty_cut_".concat(royaltyName))!
417                                let cutValue: UFix64 = The_Next_Cartel_NFT.royaltyCutStringToUFix64(royaltyCut)
418                                if cutValue != 0.0 {
419                                    royalties.append(MetadataViews.Royalty(
420                                        receiver: getAccount(royaltyAddress).capabilities.get<&{FungibleToken.Receiver}>(royaltyReceiver),
421                                        cut: cutValue,
422                                        description: The_Next_Cartel_NFT.getSetMetadataByField(setId: self.setId, field: "royalty_desc_".concat(royaltyName))!
423                                    )
424                                    )
425                                }
426                            }
427                        }
428                    }
429                    return MetadataViews.Royalties(royalties)
430                case Type<MetadataViews.NFTCollectionData>():
431                    return The_Next_Cartel_NFT.resolveContractView(resourceType: nil, viewType: Type<MetadataViews.NFTCollectionData>())
432                case Type<MetadataViews.NFTCollectionDisplay>():
433                    return The_Next_Cartel_NFT.resolveContractView(resourceType: nil, viewType: Type<MetadataViews.NFTCollectionDisplay>())
434                case Type<MetadataViews.Traits>():
435                    let traitDictionary: {String: AnyStruct} = {}
436                    // There is only a legacy {String: String} dictionary to store trait information.
437                    // There may be multiple traits defined per NFT. Pull trait information
438                    // based on keys that have the "trait_" prefix in the dictionary.
439                    for metadataKey in The_Next_Cartel_NFT.getSetMetadata(setId: self.setId)!.keys {
440                        // For efficiency, only check keys that are > 6 chars, which is the length of "trait_" key
441                        if metadataKey.length >= 6 {
442                            if metadataKey.slice(from: 0, upTo: 6) == "trait_" {
443                                // A trait has been found. Set the trait name to only the trait key suffix.
444                                traitDictionary.insert(key: metadataKey.slice(from: 6, upTo: metadataKey.length), The_Next_Cartel_NFT.getSetMetadataByField(setId: self.setId, field: metadataKey)!)
445                            }
446                        }
447                    }
448                    return MetadataViews.dictToTraits(dict: traitDictionary, excludedNames: [])
449                case Type<MetadataViews.Medias>():
450                    return MetadataViews.Medias(
451                        [
452                            MetadataViews.Media(
453                                file: MetadataViews.HTTPFile(
454                                    url: The_Next_Cartel_NFT.getSetMetadataByField(setId: self.setId, field: "image")!
455                                ),
456                                mediaType: self.getMimeType()
457                            )
458                        ]
459                    )
460            }
461            return nil
462        }
463
464        access(all) fun getMimeType(): String {
465            var metadataFileType = The_Next_Cartel_NFT.getSetMetadataByField(setId: self.setId, field: "image_file_type")!.toLower()
466            switch metadataFileType {
467                case "mp4":
468                    return "video/mp4"
469                case "mov":
470                    return "video/quicktime"
471                case "webm":
472                    return "video/webm"
473                case "ogv":
474                    return "video/ogg"
475                case "png":
476                    return "image/png"
477                case "jpeg":
478                    return "image/jpeg"
479                case "jpg":
480                    return "image/jpeg"
481                case "gif":
482                    return "image/gif"
483                case "webp":
484                    return "image/webp"
485                case "svg":
486                    return "image/svg+xml"
487                case "glb":
488                    return "model/gltf-binary"
489                case "gltf":
490                    return "model/gltf+json"
491                case "obj":
492                    return "model/obj"
493                case "mtl":
494                    return "model/mtl"
495                case "mp3":
496                    return "audio/mpeg"
497                case "ogg":
498                    return "audio/ogg"
499                case "oga":
500                    return "audio/ogg"
501                case "wav":
502                    return "audio/wav"
503                case "html":
504                    return "text/html"
505            }
506            return ""
507        }
508    }
509
510    // Admin is a special authorization resource that 
511    // allows the owner to perform important NFT 
512    // functions
513    //
514    access(all) resource Admin {
515
516        access(all) fun addSeries(seriesId: UInt32, metadata: {String: String}) {
517            pre {
518                The_Next_Cartel_NFT.series[seriesId] == nil:
519                    "Cannot add Series: The Series already exists"
520            }
521
522            // Create the new Series
523            var newSeries <- create Series(
524                seriesId: seriesId,
525                metadata: metadata
526            )
527
528            // Add the new Series resource to the Series dictionary in the contract
529            The_Next_Cartel_NFT.series[seriesId] <-! newSeries
530        }
531
532        access(all) fun borrowSeries(seriesId: UInt32): &Series  {
533            pre {
534                The_Next_Cartel_NFT.series[seriesId] != nil:
535                    "Cannot borrow Series: The Series does not exist"
536            }
537
538            // Get a reference to the Series and return it
539            return (&The_Next_Cartel_NFT.series[seriesId])!
540        }
541
542        access(all) fun createNewAdmin(): @Admin {
543            return <-create Admin()
544        }
545
546    }
547
548    // This is the interface that users can cast their NFT Collection as
549    // to allow others to deposit The_Next_Cartel_NFT into their Collection. It also allows for reading
550    // the details of The_Next_Cartel_NFT in the Collection.
551    access(all) resource interface The_Next_Cartel_NFTCollectionPublic : NonFungibleToken.CollectionPublic {
552        access(all) fun batchDeposit(tokens: @{NonFungibleToken.Collection})
553        access(all) fun borrowThe_Next_Cartel_NFT(id: UInt64): &NFT? {
554            // If the result isn't nil, the id of the returned reference
555            // should be the same as the argument to the function
556            post {
557                (result == nil) || (result?.id == id): 
558                    "Cannot borrow The_Next_Cartel_NFT reference: The ID of the returned reference is incorrect"
559            }
560        }
561    }
562
563    // Collection
564    // A collection of The_Next_Cartel_NFT NFTs owned by an account
565    //
566    access(all) resource Collection: The_Next_Cartel_NFTCollectionPublic, NonFungibleToken.Collection {
567        // dictionary of NFT conforming tokens
568        // NFT is a resource type with an UInt64 ID field
569        //
570        access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
571
572        access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
573            let supportedType: {Type: Bool} = {}
574            supportedType[Type<@The_Next_Cartel_NFT.NFT>()] = true
575            return supportedType
576        }
577
578        access(all) view fun isSupportedNFTType(type: Type): Bool {
579            if type == Type<@The_Next_Cartel_NFT.NFT>() {
580                return true
581            }
582            return false
583        }
584
585        // withdraw
586        // Removes an NFT from the collection and moves it to the caller
587        //
588        access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
589            let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
590
591            return <-token
592        }
593
594        // batchWithdraw withdraws multiple NFTs and returns them as a Collection
595        //
596        // Parameters: ids: An array of IDs to withdraw
597        //
598        // Returns: @NonFungibleToken.Collection: The collection of withdrawn tokens
599        //
600
601        access(NonFungibleToken.Withdraw) fun batchWithdraw(ids: [UInt64]): @{NonFungibleToken.Collection} {
602            // Create a new empty Collection
603            var batchCollection <- create Collection()
604            
605            // Iterate through the ids and withdraw them from the Collection
606            for id in ids {
607                batchCollection.deposit(token: <-self.withdraw(withdrawID: id))
608            }
609            
610            // Return the withdrawn tokens
611            return <-batchCollection
612        }
613
614        // deposit
615        // Takes a NFT and adds it to the collections dictionary
616        // and adds the ID to the id array
617        //
618        access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
619            let token <- token as! @The_Next_Cartel_NFT.NFT
620
621            let id: UInt64 = token.id
622
623            // add the new token to the dictionary which removes the old one
624            let oldToken <- self.ownedNFTs[id] <- token
625
626            destroy oldToken
627        }
628
629        // batchDeposit takes a Collection object as an argument
630        // and deposits each contained NFT into this Collection
631        access(all) fun batchDeposit(tokens: @{NonFungibleToken.Collection}) {
632
633            // Get an array of the IDs to be deposited
634            let keys = tokens.getIDs()
635
636            // Iterate through the keys in the collection and deposit each one
637            for key in keys {
638                self.deposit(token: <-tokens.withdraw(withdrawID: key))
639            }
640
641            // Destroy the empty Collection
642            destroy tokens
643        }
644
645        // getIDs
646        // Returns an array of the IDs that are in the collection
647        //
648        access(all) view fun getIDs(): [UInt64] {
649            return self.ownedNFTs.keys
650        }
651
652        access(all) view fun getLength(): Int {
653            return self.ownedNFTs.keys.length
654        }
655
656        // borrowNFT
657        // Gets a reference to an NFT in the collection
658        // so that the caller can read its metadata and call its methods
659        //
660        access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
661            return &self.ownedNFTs[id]
662        }
663
664        // borrowThe_Next_Cartel_NFT
665        // Gets a reference to an NFT in the collection as a The_Next_Cartel_NFT,
666        // exposing all of its fields.
667        // This is safe as there are no functions that can be called on the The_Next_Cartel_NFT.
668        //
669        access(all) fun borrowThe_Next_Cartel_NFT(id: UInt64): &NFT? {
670            if let nft = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? {
671                return nft as! &NFT
672            }
673            return nil
674        }
675
676        // borrowViewResolver
677        // Gets a reference to the MetadataViews resolver in the collection,
678        // giving access to all metadata information made available.
679        //
680        access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
681            if let nft = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? {
682                return nft as &{ViewResolver.Resolver}
683            }
684            return nil
685        }
686
687        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
688            return <- create Collection()
689        }
690
691        // initializer
692        //
693        init () {
694            self.ownedNFTs <- {}
695        }
696    }
697
698    // createEmptyCollection
699    // public function that anyone can call to create a new empty collection
700    //
701    access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
702        return <- create Collection()
703    }
704
705    access(all) view fun getContractViews(resourceType: Type?): [Type] {
706        return [
707            Type<MetadataViews.NFTCollectionData>(),
708            Type<MetadataViews.NFTCollectionDisplay>()
709        ]
710    }
711
712    access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
713        switch viewType {
714            case Type<MetadataViews.NFTCollectionData>():
715                return MetadataViews.NFTCollectionData(
716                    storagePath: The_Next_Cartel_NFT.CollectionStoragePath,
717                    publicPath: The_Next_Cartel_NFT.CollectionPublicPath,
718                    publicCollection: Type<&The_Next_Cartel_NFT.Collection>(),
719                    publicLinkedType: Type<&The_Next_Cartel_NFT.Collection>(),
720                    createEmptyCollectionFunction: (fun (): @{NonFungibleToken.Collection} {
721                        return <-The_Next_Cartel_NFT.createEmptyCollection(nftType: Type<@The_Next_Cartel_NFT.NFT>())
722                    })
723                )
724            case Type<MetadataViews.NFTCollectionDisplay>():
725                let squareImage = MetadataViews.Media(
726                    file: MetadataViews.HTTPFile(
727                        url: "https://media-local.gigantik.io/the_next_cartel/square.png"
728                    ),
729                    mediaType: "image/png"
730                )
731                let bannerImage = MetadataViews.Media(
732                    file: MetadataViews.HTTPFile(
733                        url: "https://media-local.gigantik.io/the_next_cartel/banner.png"
734                    ),
735                    mediaType: "image/png"
736                )
737                var socials: {String: MetadataViews.ExternalURL} = {
738                    "twitter": MetadataViews.ExternalURL("https://x.com/gigantik_"),
739                    "discord": MetadataViews.ExternalURL("https://discord.gg/giglabs"),
740                    "instagram": MetadataViews.ExternalURL("")
741                }
742
743                return MetadataViews.NFTCollectionDisplay(
744                    name: "The_Next_Cartel shop",
745                    description: "This is a The_Next_Cartel shop",
746                    externalURL: MetadataViews.ExternalURL("https://The_Next_Cartel.shops.gigantik.io/"),
747                    squareImage: squareImage,
748                    bannerImage: bannerImage,
749                    socials: socials
750                )
751        }
752        return nil
753    }
754
755    // getAllSeries returns all the sets
756    //
757    // Returns: An array of all the series that have been created
758    access(all) fun getAllSeries(): [The_Next_Cartel_NFT.SeriesData] {
759        return The_Next_Cartel_NFT.seriesData.values
760    }
761
762    // getAllSets returns all the sets
763    //
764    // Returns: An array of all the sets that have been created
765    access(all) fun getAllSets(): [The_Next_Cartel_NFT.NFTSetData] {
766        return The_Next_Cartel_NFT.setData.values
767    }
768
769    // getSeriesMetadata returns the metadata that the specified Series
770    //            is associated with.
771    // 
772    // Parameters: seriesId: The id of the Series that is being searched
773    //
774    // Returns: The metadata as a String to String mapping optional
775    access(all) fun getSeriesMetadata(seriesId: UInt32): {String: String}? {
776        return The_Next_Cartel_NFT.seriesData[seriesId]?.getMetadata()
777    }
778
779    // getSetMaxEditions returns the the maximum number of NFT editions that can
780    //        be minted in this Set.
781    // 
782    // Parameters: setId: The id of the Set that is being searched
783    //
784    // Returns: The max number of NFT editions in this Set
785    access(all) view fun getSetMaxEditions(setId: UInt32): UInt32? {
786        return The_Next_Cartel_NFT.setData[setId]?.maxEditions
787    }
788
789    // getSetMetadata returns all the metadata associated with a specific Set
790    // 
791    // Parameters: setId: The id of the Set that is being searched
792    //
793    // Returns: The metadata as a String to String mapping optional
794    access(all) fun getSetMetadata(setId: UInt32): {String: String}? {
795        return The_Next_Cartel_NFT.setData[setId]?.getMetadata()
796    }
797
798    // getSetSeriesId returns the Series Id the Set belongs to
799    // 
800    // Parameters: setId: The id of the Set that is being searched
801    //
802    // Returns: The Series Id
803    access(all) fun getSetSeriesId(setId: UInt32): UInt32? {
804        return The_Next_Cartel_NFT.setData[setId]?.seriesId
805    }
806
807    // getSetMetadata returns all the ipfs hashes for each nft 
808    //     edition in the Set.
809    // 
810    // Parameters: setId: The id of the Set that is being searched
811    //
812    // Returns: The ipfs hashes of nft editions as a Array of Strings
813    access(all) fun getIpfsMetadataHashByNftEdition(setId: UInt32, editionNum: UInt32): String? {
814        // Don't force a revert if the setId or field is invalid
815        if let set = The_Next_Cartel_NFT.setData[setId] {
816            return set.getIpfsMetadataHash(editionNum: editionNum)
817        } else {
818            return nil
819        }
820    }
821
822    // getSetMetadataByField returns the metadata associated with a 
823    //                        specific field of the metadata
824    // 
825    // Parameters: setId: The id of the Set that is being searched
826    //             field: The field to search for
827    //
828    // Returns: The metadata field as a String Optional
829    access(all) fun getSetMetadataByField(setId: UInt32, field: String): String? {
830        // Don't force a revert if the setId or field is invalid
831        if let set = The_Next_Cartel_NFT.setData[setId] {
832            return set.getMetadataField(field: field)
833        } else {
834            return nil
835        }
836    }
837
838    // stringToAddress Converts a string to a Flow address
839    // 
840    // Parameters: input: The address as a String
841    //
842    // Returns: The flow address as an Address Optional
843	access(all) fun convertStringToAddress(_ input: String): Address? {
844		var address=input
845		if input.utf8[1] == 120 {
846			address = input.slice(from: 2, upTo: input.length)
847		}
848		var r:UInt64 = 0 
849		var bytes = address.decodeHex()
850
851		while bytes.length>0{
852			r = r  + (UInt64(bytes.removeFirst()) << UInt64(bytes.length * 8 ))
853		}
854
855		return Address(r)
856	}
857
858    // royaltyCutStringToUFix64 Converts a royalty cut string
859    //        to a UFix64
860    // 
861    // Parameters: royaltyCut: The cut value 0.0 - 1.0 as a String
862    //
863    // Returns: The royalty cut as a UFix64
864    access(all) fun royaltyCutStringToUFix64(_ royaltyCut: String): UFix64 {
865        var decimalPos = 0
866        if royaltyCut[0] == "." {
867            decimalPos = 1
868        } else if royaltyCut[1] == "." {
869            if royaltyCut[0] == "1" {
870                // "1" in the first postiion must be 1.0 i.e. 100% cut
871                return 1.0
872            } else if royaltyCut[0] == "0" {
873                decimalPos = 2
874            }
875        } else {
876            // Invalid royalty value
877            return 0.0
878        }
879
880        var royaltyCutStrLen = royaltyCut.length
881        if royaltyCut.length > (8 + decimalPos) {
882            // UFix64 is capped at 8 digits after the decimal
883            // so truncate excess decimal values from the string
884            royaltyCutStrLen = (8 + decimalPos)
885        }
886        let royaltyCutPercentValue = royaltyCut.slice(from: decimalPos, upTo: royaltyCutStrLen)
887        var bytes = royaltyCutPercentValue.utf8
888        var i = 0
889        var cutValueInteger: UInt64 = 0
890        var cutValueDivisor: UFix64 = 1.0
891        let zeroAsciiIntValue: UInt64 = 48
892        // First convert the string to a non-decimal Integer
893        while i < bytes.length {
894            cutValueInteger = (cutValueInteger * 10) + UInt64(bytes[i]) - zeroAsciiIntValue
895            cutValueDivisor = cutValueDivisor * 10.0
896            i = i + 1
897        }
898
899        // Convert the resulting Integer to a decimal in the range 0.0 - 0.99999999
900        return (UFix64(cutValueInteger) / cutValueDivisor)
901    }
902
903    // initializer
904    //
905	init() {
906        // Set named paths
907        self.CollectionStoragePath = /storage/The_Next_Cartel_NFTCollection
908        self.CollectionPublicPath = /public/The_Next_Cartel_NFTCollection
909        self.AdminStoragePath = /storage/The_Next_Cartel_NFTAdmin
910
911        self.setData = {}
912        self.seriesData = {}
913        self.series <- {}
914
915        // Put Admin in storage
916        self.account.storage.save(<-create Admin(), to: self.AdminStoragePath)
917
918        let collectionCap: Capability<&The_Next_Cartel_NFT.Collection> = self.account.capabilities.storage.issue<&The_Next_Cartel_NFT.Collection>(self.CollectionStoragePath)
919        self.account.capabilities.publish(collectionCap, at: self.CollectionPublicPath)
920	}
921}
922