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