Smart Contract

xinren_NFT

A.6a67a3353b481928.xinren_NFT

Valid From

86,009,012

Deployed

3d ago
Feb 24, 2026, 11:58:07 PM UTC

Dependents

2 imports
1
2import FungibleToken from 0xf233dcee88fe0abe
3import NonFungibleToken from 0x1d7e57aa55817448
4import MetadataViews from 0x1d7e57aa55817448
5import ViewResolver from 0x1d7e57aa55817448
6
7access(all) contract xinren_NFT: NonFungibleToken {
8
9    // An entitlement to perform Admin actions on this contract
10    access(all) entitlement Administrator
11
12    // xinren_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 xinren_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            xinren_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            xinren_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            xinren_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            xinren_NFT.setData[setId] = newSetMetadata
234
235            emit SetMetadataUpdated(seriesId: self.seriesId, setId: setId)
236        }
237
238		// mintxinren_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 mintxinren_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]! <= xinren_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 xinren_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        // mintEditionxinren_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 mintEditionxinren_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]! <= xinren_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 xinren_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        // batchMintxinren_NFT
296        // Mints multiple new NFTs given and deposits the NFTs
297        // into the recipients collection using their collection reference
298		access(all) fun batchMintxinren_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.mintxinren_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 xinren_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 = xinren_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 <- xinren_NFT.createEmptyCollection(nftType: Type<@xinren_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: xinren_NFT.getSetMetadataByField(setId: self.setId, field: "name")!,
381                        description: xinren_NFT.getSetMetadataByField(setId: self.setId, field: "description")!,
382                        thumbnail: MetadataViews.HTTPFile(
383                            url: xinren_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 = xinren_NFT.setData[self.setId]?.maxEditions ?? 0
392                    let editionName = xinren_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 = xinren_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 xinren_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 = xinren_NFT.convertStringToAddress(xinren_NFT.getSetMetadataByField(setId: self.setId, field: "royalty_addr_".concat(royaltyName))!)!
415                                let royaltyReceiver: PublicPath = PublicPath(identifier: xinren_NFT.getSetMetadataByField(setId: self.setId, field: "royalty_rcv_".concat(royaltyName))!)!
416                                let royaltyCut = xinren_NFT.getSetMetadataByField(setId: self.setId, field: "royalty_cut_".concat(royaltyName))!
417                                let cutValue: UFix64 = xinren_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: xinren_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 xinren_NFT.resolveContractView(resourceType: nil, viewType: Type<MetadataViews.NFTCollectionData>())
432                case Type<MetadataViews.NFTCollectionDisplay>():
433                    return xinren_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 xinren_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), xinren_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: xinren_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 = xinren_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                xinren_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            xinren_NFT.series[seriesId] <-! newSeries
530        }
531
532        access(all) fun borrowSeries(seriesId: UInt32): &Series  {
533            pre {
534                xinren_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 (&xinren_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 xinren_NFT into their Collection. It also allows for reading
550    // the details of xinren_NFT in the Collection.
551    access(all) resource interface xinren_NFTCollectionPublic : NonFungibleToken.CollectionPublic {
552        access(all) fun batchDeposit(tokens: @{NonFungibleToken.Collection})
553        access(all) fun borrowxinren_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 xinren_NFT reference: The ID of the returned reference is incorrect"
559            }
560        }
561    }
562
563    // Collection
564    // A collection of xinren_NFT NFTs owned by an account
565    //
566    access(all) resource Collection: xinren_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<@xinren_NFT.NFT>()] = true
575            return supportedType
576        }
577
578        access(all) view fun isSupportedNFTType(type: Type): Bool {
579            if type == Type<@xinren_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! @xinren_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        // borrowxinren_NFT
665        // Gets a reference to an NFT in the collection as a xinren_NFT,
666        // exposing all of its fields.
667        // This is safe as there are no functions that can be called on the xinren_NFT.
668        //
669        access(all) fun borrowxinren_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: xinren_NFT.CollectionStoragePath,
717                    publicPath: xinren_NFT.CollectionPublicPath,
718                    publicCollection: Type<&xinren_NFT.Collection>(),
719                    publicLinkedType: Type<&xinren_NFT.Collection>(),
720                    createEmptyCollectionFunction: (fun (): @{NonFungibleToken.Collection} {
721                        return <-xinren_NFT.createEmptyCollection(nftType: Type<@xinren_NFT.NFT>())
722                    })
723                )
724            case Type<MetadataViews.NFTCollectionDisplay>():
725                let squareImage = MetadataViews.Media(
726                    file: MetadataViews.HTTPFile(
727                        url: "undefined/xinren/square.png"
728                    ),
729                    mediaType: "image/png"
730                )
731                let bannerImage = MetadataViews.Media(
732                    file: MetadataViews.HTTPFile(
733                        url: "undefined/xinren/banner.png"
734                    ),
735                    mediaType: "image/png"
736                )
737                var socials: {String: MetadataViews.ExternalURL} = {}
738                //TODO: Hardcode socials here or add way to string replace during
739                // contract code generation
740                //
741                //    socials.insert(key: "twitter", MetadataViews.ExternalURL("https://<TWITTER_URL>"))
742                //    socials.insert(key: "facebook", MetadataViews.ExternalURL("https://<FACEBOOK_URL>"))
743
744                return MetadataViews.NFTCollectionDisplay(
745                    name: "xinren_DISPLAY_NAME",
746                    description: "xinren_DISPLAY_DESCRIPTION",
747                    externalURL: MetadataViews.ExternalURL("xinren_EXTERNAL_URL"),
748                    squareImage: squareImage,
749                    bannerImage: bannerImage,
750                    socials: socials
751                )
752        }
753        return nil
754    }
755
756    // getAllSeries returns all the sets
757    //
758    // Returns: An array of all the series that have been created
759    access(all) fun getAllSeries(): [xinren_NFT.SeriesData] {
760        return xinren_NFT.seriesData.values
761    }
762
763    // getAllSets returns all the sets
764    //
765    // Returns: An array of all the sets that have been created
766    access(all) fun getAllSets(): [xinren_NFT.NFTSetData] {
767        return xinren_NFT.setData.values
768    }
769
770    // getSeriesMetadata returns the metadata that the specified Series
771    //            is associated with.
772    // 
773    // Parameters: seriesId: The id of the Series that is being searched
774    //
775    // Returns: The metadata as a String to String mapping optional
776    access(all) fun getSeriesMetadata(seriesId: UInt32): {String: String}? {
777        return xinren_NFT.seriesData[seriesId]?.getMetadata()
778    }
779
780    // getSetMaxEditions returns the the maximum number of NFT editions that can
781    //        be minted in this Set.
782    // 
783    // Parameters: setId: The id of the Set that is being searched
784    //
785    // Returns: The max number of NFT editions in this Set
786    access(all) view fun getSetMaxEditions(setId: UInt32): UInt32? {
787        return xinren_NFT.setData[setId]?.maxEditions
788    }
789
790    // getSetMetadata returns all the metadata associated with a specific Set
791    // 
792    // Parameters: setId: The id of the Set that is being searched
793    //
794    // Returns: The metadata as a String to String mapping optional
795    access(all) fun getSetMetadata(setId: UInt32): {String: String}? {
796        return xinren_NFT.setData[setId]?.getMetadata()
797    }
798
799    // getSetSeriesId returns the Series Id the Set belongs to
800    // 
801    // Parameters: setId: The id of the Set that is being searched
802    //
803    // Returns: The Series Id
804    access(all) fun getSetSeriesId(setId: UInt32): UInt32? {
805        return xinren_NFT.setData[setId]?.seriesId
806    }
807
808    // getSetMetadata returns all the ipfs hashes for each nft 
809    //     edition in the Set.
810    // 
811    // Parameters: setId: The id of the Set that is being searched
812    //
813    // Returns: The ipfs hashes of nft editions as a Array of Strings
814    access(all) fun getIpfsMetadataHashByNftEdition(setId: UInt32, editionNum: UInt32): String? {
815        // Don't force a revert if the setId or field is invalid
816        if let set = xinren_NFT.setData[setId] {
817            return set.getIpfsMetadataHash(editionNum: editionNum)
818        } else {
819            return nil
820        }
821    }
822
823    // getSetMetadataByField returns the metadata associated with a 
824    //                        specific field of the metadata
825    // 
826    // Parameters: setId: The id of the Set that is being searched
827    //             field: The field to search for
828    //
829    // Returns: The metadata field as a String Optional
830    access(all) fun getSetMetadataByField(setId: UInt32, field: String): String? {
831        // Don't force a revert if the setId or field is invalid
832        if let set = xinren_NFT.setData[setId] {
833            return set.getMetadataField(field: field)
834        } else {
835            return nil
836        }
837    }
838
839    // stringToAddress Converts a string to a Flow address
840    // 
841    // Parameters: input: The address as a String
842    //
843    // Returns: The flow address as an Address Optional
844	access(all) fun convertStringToAddress(_ input: String): Address? {
845		var address=input
846		if input.utf8[1] == 120 {
847			address = input.slice(from: 2, upTo: input.length)
848		}
849		var r:UInt64 = 0 
850		var bytes = address.decodeHex()
851
852		while bytes.length>0{
853			r = r  + (UInt64(bytes.removeFirst()) << UInt64(bytes.length * 8 ))
854		}
855
856		return Address(r)
857	}
858
859    // royaltyCutStringToUFix64 Converts a royalty cut string
860    //        to a UFix64
861    // 
862    // Parameters: royaltyCut: The cut value 0.0 - 1.0 as a String
863    //
864    // Returns: The royalty cut as a UFix64
865    access(all) fun royaltyCutStringToUFix64(_ royaltyCut: String): UFix64 {
866        var decimalPos = 0
867        if royaltyCut[0] == "." {
868            decimalPos = 1
869        } else if royaltyCut[1] == "." {
870            if royaltyCut[0] == "1" {
871                // "1" in the first postiion must be 1.0 i.e. 100% cut
872                return 1.0
873            } else if royaltyCut[0] == "0" {
874                decimalPos = 2
875            }
876        } else {
877            // Invalid royalty value
878            return 0.0
879        }
880
881        var royaltyCutStrLen = royaltyCut.length
882        if royaltyCut.length > (8 + decimalPos) {
883            // UFix64 is capped at 8 digits after the decimal
884            // so truncate excess decimal values from the string
885            royaltyCutStrLen = (8 + decimalPos)
886        }
887        let royaltyCutPercentValue = royaltyCut.slice(from: decimalPos, upTo: royaltyCutStrLen)
888        var bytes = royaltyCutPercentValue.utf8
889        var i = 0
890        var cutValueInteger: UInt64 = 0
891        var cutValueDivisor: UFix64 = 1.0
892        let zeroAsciiIntValue: UInt64 = 48
893        // First convert the string to a non-decimal Integer
894        while i < bytes.length {
895            cutValueInteger = (cutValueInteger * 10) + UInt64(bytes[i]) - zeroAsciiIntValue
896            cutValueDivisor = cutValueDivisor * 10.0
897            i = i + 1
898        }
899
900        // Convert the resulting Integer to a decimal in the range 0.0 - 0.99999999
901        return (UFix64(cutValueInteger) / cutValueDivisor)
902    }
903
904    // initializer
905    //
906	init() {
907        // Set named paths
908        self.CollectionStoragePath = /storage/xinren_NFTCollection
909        self.CollectionPublicPath = /public/xinren_NFTCollection
910        self.AdminStoragePath = /storage/xinren_NFTAdmin
911
912        self.setData = {}
913        self.seriesData = {}
914        self.series <- {}
915
916        // Put Admin in storage
917        self.account.storage.save(<-create Admin(), to: self.AdminStoragePath)
918
919        let collectionCap: Capability<&xinren_NFT.Collection> = self.account.capabilities.storage.issue<&xinren_NFT.Collection>(self.CollectionStoragePath)
920        self.account.capabilities.publish(collectionCap, at: self.CollectionPublicPath)
921	}
922}
923