DeploySEALED

&○□!○~@$~&#■@◆▓●~?◇╱◆▪▫●@!○%$*$░○!░○█╲▫╱!○▓◆░?▓*▒░●%◆●◇*@?!◇~□▫▫

Transaction ID

Timestamp

Sep 03, 2025, 12:16:02 AM UTC
5mo ago

Block Height

125,008,172

Computation

0

Execution Fee

0.00000724 FLOW

Transaction Summary

Deploy

Contract deployment

Contract deployment

Script Arguments

0nameString
Crowdflix
1codeString
/* Description: Central Smart Contract for Crowdflix NFT Platform This smart contract defines the primary logic for the Crowdflix platform — a Flow-based NFT marketplace for collectible cinematic moments from movies, series and more. The contract manages metadata and minting rights for MovieScenes and Sets, which act as templates for minting collectible Moments (NFTs). When a new MovieScene wants to be added to the records, an Admin creates a new MovieScene struct that is stored in the smart contract. Then an Admin can create new Sets. Sets consist of a public struct that contains public information about a set, and a private resource used to mint new moments based off of movieScenes that have been linked to the Set. The admin resource has the power to do all of the important actions in the smart contract. When admins want to call functions in a set, they call their borrowSet function to get a reference to a set in the contract. Then, they can call functions on the set using that reference. In this way, the smart contract and its defined resources interact with great teamwork. When moments are minted, they are initialized with a MomentData struct and are returned by the minter. The contract also defines a Collection resource. This is an object that every Crowdflix NFT owner will store in their account to manage their NFT collection. The main Crowdflix account will also have its own Moment collections it can use to hold its own moments that have not yet been sent to a user. Note: All state changing functions will panic if an invalid argument is provided or one of its pre-conditions or post conditions aren't met. Functions that don't modify state will simply return 0 or nil and those cases need to be handled by the caller. */ // Testnet // import FungibleToken from 0x9a0766d93b6608b7 // import NonFungibleToken from 0x631e88ae7f1d7c20 // import MetadataViews from 0x631e88ae7f1d7c20 // import ViewResolver from 0x631e88ae7f1d7c20 // Production import FungibleToken from 0xf233dcee88fe0abe import NonFungibleToken from 0x1d7e57aa55817448 import MetadataViews from 0x1d7e57aa55817448 import ViewResolver from 0x1d7e57aa55817448 access(all) contract Crowdflix: NonFungibleToken { // ----------------------------------------------------------------------- // Crowdflix deployment variables // ----------------------------------------------------------------------- // The network the contract is deployed on access(all) view fun Network(): String { return "production" } // The address to which royalties should be deposited access(all) view fun RoyaltyAddress(): Address { return 0x474b92dbf17fda56 } // The path to the Subedition Admin resource belonging to the Account // which the contract is deployed on access(all) view fun SubeditionAdminStoragePath(): StoragePath { return /storage/CrowdflixSubeditionAdmin} // ----------------------------------------------------------------------- // Crowdflix contract Events // ----------------------------------------------------------------------- // Emitted when a new MovieScene struct is created access(all) event MovieSceneCreated(id: UInt32, metadata: {String: String}) // Emitted when a new series has been triggered by an admin access(all) event NewSeriesStarted(newCurrentSeries: UInt32) // Events for Set-Related actions // // Emitted when a new Set is created access(all) event SetCreated(setID: UInt32, series: UInt32) // Emitted when a new MovieScene is added to a Set access(all) event MovieSceneAddedToSet(setID: UInt32, movieSceneID: UInt32) // Emitted when a MovieScene is retired from a Set and cannot be used to mint access(all) event MovieSceneRetiredFromSet(setID: UInt32, movieSceneID: UInt32, numMoments: UInt32) // Emitted when a Set is locked, meaning MovieScenes cannot be added access(all) event SetLocked(setID: UInt32) // Emitted when a Moment is minted from a Set access(all) event MomentMinted(momentID: UInt64, movieSceneID: UInt32, setID: UInt32, serialNumber: UInt32, subeditionID: UInt32) // Events for Collection-related actions // // Emitted when a moment is withdrawn from a Collection access(all) event Withdraw(id: UInt64, from: Address?) // Emitted when a moment is deposited into a Collection access(all) event Deposit(id: UInt64, to: Address?) // Emitted when a Moment is destroyed access(all) event MomentDestroyed(id: UInt64) // Emitted when a Subedition is created access(all) event SubeditionCreated(subeditionID: UInt32, name: String, metadata: {String: String}) // Emitted when a Subedition is linked to the specific Moment access(all) event SubeditionAddedToMoment(momentID: UInt64, subeditionID: UInt32, setID: UInt32, movieSceneID: UInt32) // ----------------------------------------------------------------------- // Crowdflix contract-level fields. // These contain actual values that are stored in the smart contract. // ----------------------------------------------------------------------- // Series that this Set belongs to. // Series is a concept that indicates a group of Sets through time. // Many Sets can exist at a time, but only one series. access(all) var currentSeries: UInt32 // Variable size dictionary of MovieScene structs access(self) var movieSceneDatas: {UInt32: MovieScene} // Variable size dictionary of SetData structs access(self) var setDatas: {UInt32: SetData} // Variable size dictionary of Set resources access(self) var sets: @{UInt32: Set} // The ID that is used to create MovieScenes. // Every time a MovieScene is created, movieSceneID is assigned // to the new MovieScene's ID and then is incremented by 1. access(all) var nextMovieSceneID: UInt32 // The ID that is used to create Sets. Every time a Set is created // setID is assigned to the new set's ID and then is incremented by 1. access(all) var nextSetID: UInt32 // The total number of Crowdflix NFTs that have been created // Because NFTs can be destroyed, it doesn't necessarily mean that this // reflects the total number of NFTs in existence, just the number that // have been minted to date. Also used as global moment IDs for minting. access(all) var totalSupply: UInt64 // ----------------------------------------------------------------------- // Crowdflix contract-level Composite Type definitions // ----------------------------------------------------------------------- // These are just *definitions* for Types that this contract // and other accounts can use. These definitions do not contain // actual stored values, but an instance (or object) of one of these Types // can be created by this contract that contains stored values. // ----------------------------------------------------------------------- // MovieScene is a Struct that holds metadata associated // with a specific movie scene, like the legendary scene when // Iron Man snapped his fingers in Avengers Endgame // or when Neo chose the red pill in The Matrix. // // Moment NFTs will all reference a single MovieScene as the owner of // its metadata. The MovieScene are publicly accessible, so anyone can // read the metadata associated with a specific MovieScene ID // access(all) struct MovieScene { // The unique ID for the MovieScene access(all) let movieSceneID: UInt32 // Stores all the metadata about the MovieScene as a string mapping // This is not the long term way NFT metadata will be stored. It's a temporary // construct while we figure out a better way to do metadata. // access(all) let metadata: {String: String} init(metadata: {String: String}) { pre { metadata.length != 0: "New MovieScene metadata cannot be empty" } self.movieSceneID = Crowdflix.nextMovieSceneID self.metadata = metadata } } // A Set is a grouping of MovieScenes that have occured in the real world // that make up a related group of collectibles, like sets of baseball // or Magic cards. A MovieScene can exist in multiple different sets. // // SetData is a struct that is stored in a field of the contract. // Anyone can query the constant information // about a set by calling various getters located // at the end of the contract. Only the admin has the ability // to modify any data in the private Set resource. // access(all) struct SetData { // Unique ID for the Set access(all) let setID: UInt32 // Name of the Set // ex. "Times when Marvel heroes made terrible decisions" access(all) let name: String // Series that this Set belongs to. // Series is a concept that indicates a group of Sets through time. // Many Sets can exist at a time, but only one series. access(all) let series: UInt32 init(name: String) { pre { name.length > 0: "New Set name cannot be empty" } self.setID = Crowdflix.nextSetID self.name = name self.series = Crowdflix.currentSeries } } // Set is a resource type that contains the functions to add and remove // MovieScenes from a set and mint Moments. // // It is stored in a private field in the contract so that // the admin resource can call its methods. // // The admin can add MovieScenes to a Set so that the set can mint Moments // that reference that playdata. // The Moments that are minted by a Set will be listed as belonging to // the Set that minted it, as well as the MovieScenes it references. // // Admin can also retire MovieScenes from the Set, meaning that the retired // MovieScene can no longer have Moments minted from it. // // If the admin locks the Set, no more MovieScenes can be added to it, but // Moments can still be minted. // // If retireAll() and lock() are called back-to-back, // the Set is closed off forever and nothing more can be done with it. access(all) resource Set { // Unique ID for the set access(all) let setID: UInt32 // Array of MovieScenes that are a part of this set. // When a MovieScene is added to the set, its ID gets appended here. // The ID does not get removed from this array when a MovieScene is retired. access(contract) var movieScenes: [UInt32] // Map of MovieScene IDs that Indicates if a MovieScene in this Set can be minted. // When a MovieScene is added to a Set, it is mapped to false (not retired). // When a MovieScene is retired, this is set to true and cannot be changed. access(contract) var retired: {UInt32: Bool} // Indicates if the Set is currently locked. // When a Set is created, it is unlocked // and MovieScenes are allowed to be added to it. // When a set is locked, MovieScenes cannot be added. // A Set can never be changed from locked to unlocked, // the decision to lock a Set it is final. // If a Set is locked, MovieScenes cannot be added, but // Moments can still be minted from MovieScenes // that exist in the Set. access(all) var locked: Bool // Mapping of MovieScene IDs that indicates the number of Moments // that have been minted for specific MovieScenes in this Set. // When a Moment is minted, this value is stored in the Moment to // show its place in the Set, eg. 13 of 60. access(contract) var numberMintedPerMovieScene: {UInt32: UInt32} init(name: String) { self.setID = Crowdflix.nextSetID self.movieScenes = [] self.retired = {} self.locked = false self.numberMintedPerMovieScene = {} // Create a new SetData for this Set and store it in contract storage Crowdflix.setDatas[self.setID] = SetData(name: name) } // addMovieScene adds a MovieScene to the set // // Parameters: movieSceneID: The ID of the MovieScene that is being added // // Pre-Conditions: // The MovieScene needs to be an existing // The Set needs to be not locked // The MovieScene can't have already been added to the Set // access(all) fun addMovieScene(movieSceneID: UInt32) { pre { Crowdflix.movieSceneDatas[movieSceneID] != nil: "Cannot add the MovieScene to Set: MovieScene doesn't exist." !self.locked: "Cannot add the MovieScene to the Set after the set has been locked." self.numberMintedPerMovieScene[movieSceneID] == nil: "The MovieScene has already beed added to the set." } // Add the MovieScene to the array of Plays self.movieScenes.append(movieSceneID) // Open the MovieScene up for minting self.retired[movieSceneID] = false // Initialize the Moment count to zero self.numberMintedPerMovieScene[movieSceneID] = 0 emit MovieSceneAddedToSet(setID: self.setID, movieSceneID: movieSceneID) } // addMovieScenes adds multiple MovieScenes to the Set // // Parameters: movieSceneIDs: The IDs of the MovieScenes that are being added // as an array // access(all) fun addMovieScenes(movieSceneIDs: [UInt32]) { for movieScene in movieSceneIDs { self.addMovieScene(movieSceneID: movieScene) } } // retireMovieScene retires a MovieScene from the Set so that it can't mint new Moments // // Parameters: movieSceneID: The ID of the MovieScene that is being retired // // Pre-Conditions: // The MovieScene is part of the Set and not retired (available for minting). // access(all) fun retireMovieScene(movieSceneID: UInt32) { pre { self.retired[movieSceneID] != nil: "Cannot retire the MovieScene: MovieScene doesn't exist in this set!" } if !self.retired[movieSceneID]! { self.retired[movieSceneID] = true emit MovieSceneRetiredFromSet(setID: self.setID, movieSceneID: movieSceneID, numMoments: self.numberMintedPerMovieScene[movieSceneID]!) } } // retireAll retires all the MovieScenes in the Set // Afterwards, none of the retired MovieScenes will be able to mint new Moments // access(all) fun retireAll() { for movieScene in self.movieScenes { self.retireMovieScene(movieSceneID: movieScene) } } // lock() locks the Set so that no more MovieScenes can be added to it // // Pre-Conditions: // The Set should not be locked access(all) fun lock() { if !self.locked { self.locked = true emit SetLocked(setID: self.setID) } } // mintMoment mints a new Moment and returns the newly minted Moment // // Parameters: movieSceneID: The ID of the MovieScene that the Moment references // // Pre-Conditions: // The MovieScene must exist in the Set and be allowed to mint new Moments // // Returns: The NFT that was minted // access(all) fun mintMoment(movieSceneID: UInt32): @NFT { pre { self.retired[movieSceneID] != nil: "Cannot mint the moment: This MovieScene doesn't exist." !self.retired[movieSceneID]!: "Cannot mint the moment from this MovieScene: This MovieScene has been retired." } // Gets the number of Moments that have been minted for this MovieScene // to use as this Moment's serial number let numInMovieScene = self.numberMintedPerMovieScene[movieSceneID]! // Mint the new moment let newMoment: @NFT <- create NFT( serialNumber: numInMovieScene + UInt32(1), movieSceneID: movieSceneID, setID: self.setID, subeditionID: 0 ) // Increment the count of Moments minted for this MovieScene self.numberMintedPerMovieScene[movieSceneID] = numInMovieScene + UInt32(1) return <-newMoment } // batchMintMoment mints an arbitrary quantity of Moments // and returns them as a Collection // // Parameters: movieSceneID: the ID of the MovieScene that the Moments are minted for // quantity: The quantity of Moments to be minted // // Returns: Collection object that contains all the Moments that were minted // access(all) fun batchMintMoment(movieSceneID: UInt32, quantity: UInt64): @Collection { let newCollection <- create Collection() var i: UInt64 = 0 while i < quantity { newCollection.deposit(token: <-self.mintMoment(movieSceneID: movieSceneID)) i = i + UInt64(1) } return <- newCollection } // mintMomentWithSubedition mints a new Moment with subedition and returns the newly minted Moment // // Parameters: movieSceneID: The ID of the MovieScene that the Moment references // subeditionID: The ID of the subedition within Edition that the Moment references // // Pre-Conditions: // The MovieScene must exist in the Set and be allowed to mint new Moments // // Returns: The NFT that was minted // access(all) fun mintMomentWithSubedition(movieSceneID: UInt32, subeditionID: UInt32): @NFT { pre { self.retired[movieSceneID] != nil: "Cannot mint the moment: This MovieScene doesn't exist." !self.retired[movieSceneID]!: "Cannot mint the moment from this MovieScene: This MovieScene has been retired." } // Gets the number of Moments that have been minted for this subedition // to use as this Moment's serial number let subeditionRef = Crowdflix.account.storage.borrow<&SubeditionAdmin>(from: Crowdflix.SubeditionAdminStoragePath()) ?? panic("No subedition admin resource in storage") let numInSubedition = subeditionRef.getNumberMintedPerSubedition( setID: self.setID, movieSceneID: movieSceneID, subeditionID: subeditionID ) // Mint the new moment let newMoment: @NFT <- create NFT( serialNumber: numInSubedition + UInt32(1), movieSceneID: movieSceneID, setID: self.setID, subeditionID: subeditionID ) // Increment the count of Moments minted for this subedition subeditionRef.addToNumberMintedPerSubedition( setID: self.setID, movieSceneID: movieSceneID, subeditionID: subeditionID ) subeditionRef.setMomentsSubedition(nftID: newMoment.id, subeditionID: subeditionID, setID: self.setID, movieSceneID: movieSceneID) self.numberMintedPerMovieScene[movieSceneID] = self.numberMintedPerMovieScene[movieSceneID]! + UInt32(1) return <- newMoment } // batchMintMomentWithSubedition mints an arbitrary quantity of Moments with subedition // and returns them as a Collection // // Parameters: movieSceneID: the ID of the MovieScene that the Moments are minted for // quantity: The quantity of Moments to be minted // subeditionID: The ID of the subedition within Edition that the Moments references // // Returns: Collection object that contains all the Moments that were minted // access(all) fun batchMintMomentWithSubedition(movieSceneID: UInt32, quantity: UInt64, subeditionID: UInt32): @Collection { let newCollection <- create Collection() var i: UInt64 = 0 while i < quantity { newCollection.deposit(token: <-self.mintMomentWithSubedition(movieSceneID: movieSceneID, subeditionID: subeditionID)) i = i + UInt64(1) } return <-newCollection } access(all) view fun getMovieScenes(): [UInt32] { return self.movieScenes } access(all) view fun getRetired(): {UInt32: Bool} { return self.retired } access(all) view fun getNumberMintedPerMovieScene(): {UInt32: UInt32} { return self.numberMintedPerMovieScene } } // Struct that contains all of the important data about a set // Can be easily queried by instantiating the `QuerySetData` object // with the desired set ID // let setData = Crowdflix.QuerySetData(setID: 12) // access(all) struct QuerySetData { access(all) let setID: UInt32 access(all) let name: String access(all) let series: UInt32 access(self) var movieScenes: [UInt32] access(self) var retired: {UInt32: Bool} access(all) var locked: Bool access(self) var numberMintedPerMovieScene: {UInt32: UInt32} init(setID: UInt32) { pre { Crowdflix.sets[setID] != nil: "The set with the provided ID does not exist" } let set = (&Crowdflix.sets[setID] as &Set?)! let setData = Crowdflix.setDatas[setID]! self.setID = setID self.name = setData.name self.series = setData.series self.movieScenes = set.getMovieScenes() self.retired = set.getRetired() self.locked = set.locked self.numberMintedPerMovieScene = set.getNumberMintedPerMovieScene() } access(all) view fun getMovieScenes(): [UInt32] { return self.movieScenes } access(all) view fun getRetired(): {UInt32: Bool} { return self.retired } access(all) view fun getNumberMintedPerMovieScene(): {UInt32: UInt32} { return self.numberMintedPerMovieScene } } access(all) struct MomentData { // The ID of the Set that the Moment comes from access(all) let setID: UInt32 // The ID of the MovieScene that the Moment references access(all) let movieSceneID: UInt32 // The place in the edition that this Moment was minted // Otherwise know as the serial number access(all) let serialNumber: UInt32 init(setID: UInt32, movieSceneID: UInt32, serialNumber: UInt32) { self.setID = setID self.movieSceneID = movieSceneID self.serialNumber = serialNumber } } // This is an implementation of a custom metadata view for MovieSceneMoment. // This view contains the MovieScene metadata. // access(all) struct MovieSceneMomentMetadataView { access(all) let title: String? access(all) let sceneDescription: String? access(all) let universe: String? access(all) let studio: String? access(all) let contentType: String? access(all) let characterNames: String? access(all) let actorNames: String? access(all) let directorOrCreator: String? access(all) let releaseYear: String? access(all) let genre: String? access(all) let dropDate: String? access(all) let dateOfMoment: String? access(all) let sceneCategory: String? access(all) let sceneType: String? access(all) let seriesNumber: UInt32? access(all) let setName: String? access(all) let serialNumber: UInt32 access(all) let movieSceneID: UInt32 access(all) let setID: UInt32 access(all) let numMomentsInEdition: UInt32? init( title: String?, sceneDescription: String?, universe: String?, studio: String?, contentType: String?, characterNames: String?, actorNames: String?, directorOrCreator: String?, releaseYear: String?, genre: String?, dropDate: String?, dateOfMoment: String?, sceneCategory: String?, sceneType: String?, seriesNumber: UInt32?, setName: String?, serialNumber: UInt32, movieSceneID: UInt32, setID: UInt32, numMomentsInEdition: UInt32? ) { self.title = title self.sceneDescription = sceneDescription self.universe = universe self.studio = studio self.contentType = contentType self.characterNames = characterNames self.actorNames = actorNames self.directorOrCreator = directorOrCreator self.releaseYear = releaseYear self.genre = genre self.dropDate = dropDate self.dateOfMoment = dateOfMoment self.sceneCategory = sceneCategory self.sceneType = sceneType self.seriesNumber = seriesNumber self.setName = setName self.serialNumber = serialNumber self.movieSceneID = movieSceneID self.setID = setID self.numMomentsInEdition = numMomentsInEdition } } // The resource that represents the Moment NFTs // access(all) resource NFT: NonFungibleToken.NFT { // Global unique moment ID access(all) let id: UInt64 // Struct of Moment metadata access(all) let data: MomentData init(serialNumber: UInt32, movieSceneID: UInt32, setID: UInt32, subeditionID: UInt32) { // Increment the global Moment IDs Crowdflix.totalSupply = Crowdflix.totalSupply + UInt64(1) self.id = Crowdflix.totalSupply // Set the metadata struct self.data = MomentData(setID: setID, movieSceneID: movieSceneID, serialNumber: serialNumber) emit MomentMinted( momentID: self.id, movieSceneID: movieSceneID, setID: self.data.setID, serialNumber: self.data.serialNumber, subeditionID: subeditionID ) } // If the Moment is destroyed, emit an event to indicate // to outside observers that it has been destroyed access(all) event ResourceDestroyed( id: UInt64 = self.id, serialNumber: UInt32 = self.data.serialNumber, movieSceneID: UInt32 = self.data.movieSceneID, setID: UInt32 = self.data.setID ) access(all) view fun name(): String { let title: String = Crowdflix.getMovieSceneMetaDataByField(movieSceneID: self.data.movieSceneID, field: "Title") ?? "" let sceneType: String = Crowdflix.getMovieSceneMetaDataByField(movieSceneID: self.data.movieSceneID, field: "SceneType") ?? "" return title .concat(" ") .concat(sceneType) } // The description of the Moment. // build the description using set, series, and serial number. access(all) view fun description(): String { // Build the description using set name, series number, and serial number let setName: String = Crowdflix.getSetName(setID: self.data.setID) ?? "" let serialNumber: String = self.data.serialNumber.toString() let seriesNumber: String = Crowdflix.getSetSeries(setID: self.data.setID)?.toString() ?? "" return "A series " .concat(seriesNumber) .concat(" ") .concat(setName) .concat(" moment with serial number ") .concat(serialNumber) } // All supported metadata views for the Moment including the Core NFT Views access(all) view fun getViews(): [Type] { return [ Type<MetadataViews.Display>(), Type<MovieSceneMomentMetadataView>(), Type<MetadataViews.Royalties>(), Type<MetadataViews.Editions>(), Type<MetadataViews.ExternalURL>(), Type<MetadataViews.NFTCollectionData>(), Type<MetadataViews.NFTCollectionDisplay>(), Type<MetadataViews.Serial>(), Type<MetadataViews.Traits>(), Type<MetadataViews.Medias>() ] } // resolves the view with the given type for the NFT access(all) fun resolveView(_ view: Type): AnyStruct? { switch view { case Type<MetadataViews.Display>(): return MetadataViews.Display( name: self.name(), description: self.description(), thumbnail: MetadataViews.HTTPFile(url: self.posterimage()) ) // Custom metadata view unique to Crowdflix Moments case Type<MovieSceneMomentMetadataView>(): return MovieSceneMomentMetadataView( title: Crowdflix.getMovieSceneMetaDataByField(movieSceneID: self.data.movieSceneID, field: "Title"), sceneDescription: Crowdflix.getMovieSceneMetaDataByField(movieSceneID: self.data.movieSceneID, field: "SceneDescription"), universe: Crowdflix.getMovieSceneMetaDataByField(movieSceneID: self.data.movieSceneID, field: "Universe"), studio: Crowdflix.getMovieSceneMetaDataByField(movieSceneID: self.data.movieSceneID, field: "Studio"), contentType: Crowdflix.getMovieSceneMetaDataByField(movieSceneID: self.data.movieSceneID, field: "ContentType"), characterNames: Crowdflix.getMovieSceneMetaDataByField(movieSceneID: self.data.movieSceneID, field: "CharacterNames"), actorNames: Crowdflix.getMovieSceneMetaDataByField(movieSceneID: self.data.movieSceneID, field: "ActorNames"), directorOrCreator: Crowdflix.getMovieSceneMetaDataByField(movieSceneID: self.data.movieSceneID, field: "DirectorOrCreator"), releaseYear: Crowdflix.getMovieSceneMetaDataByField(movieSceneID: self.data.movieSceneID, field: "ReleaseYear"), genre: Crowdflix.getMovieSceneMetaDataByField(movieSceneID: self.data.movieSceneID, field: "Genre"), dropDate: Crowdflix.getMovieSceneMetaDataByField(movieSceneID: self.data.movieSceneID, field: "DropDate"), dateOfMoment: Crowdflix.getMovieSceneMetaDataByField(movieSceneID: self.data.movieSceneID, field: "DateOfMoment"), sceneCategory: Crowdflix.getMovieSceneMetaDataByField(movieSceneID: self.data.movieSceneID, field: "SceneCategory"), sceneType: Crowdflix.getMovieSceneMetaDataByField(movieSceneID: self.data.movieSceneID, field: "SceneType"), seriesNumber: Crowdflix.getSetSeries(setID: self.data.setID), setName: Crowdflix.getSetName(setID: self.data.setID), serialNumber: self.data.serialNumber, movieSceneID: self.data.movieSceneID, setID: self.data.setID, numMomentsInEdition: Crowdflix.getNumMomentsInEdition(setID: self.data.setID, movieSceneID: self.data.movieSceneID) ) case Type<MetadataViews.Editions>(): let name = self.getEditionName() let max = Crowdflix.getNumMomentsInEdition(setID: self.data.setID, movieSceneID: self.data.movieSceneID) ?? 0 let editionInfo = MetadataViews.Edition(name: name, number: UInt64(self.data.serialNumber), max: max > 0 ? UInt64(max) : nil) let editionList: [MetadataViews.Edition] = [editionInfo] return MetadataViews.Editions( editionList ) case Type<MetadataViews.Serial>(): return MetadataViews.Serial( UInt64(self.data.serialNumber) ) case Type<MetadataViews.Royalties>(): return Crowdflix.resolveContractView(resourceType: nil, viewType: Type<MetadataViews.Royalties>()) case Type<MetadataViews.ExternalURL>(): return MetadataViews.ExternalURL(self.getMomentURL()) case Type<MetadataViews.NFTCollectionData>(): return Crowdflix.resolveContractView(resourceType: nil, viewType: Type<MetadataViews.NFTCollectionData>()) case Type<MetadataViews.NFTCollectionDisplay>(): return Crowdflix.resolveContractView(resourceType: nil, viewType: Type<MetadataViews.NFTCollectionDisplay>()) case Type<MetadataViews.Traits>(): return self.resolveTraitsView() case Type<MetadataViews.Medias>(): return MetadataViews.Medias( [ MetadataViews.Media( file: MetadataViews.HTTPFile( url: self.posterimage() ), mediaType: "image/jpeg" ), MetadataViews.Media( file: MetadataViews.HTTPFile( url: self.video() ), mediaType: "video/mp4" ) ] ) } return nil } // resolves this NFT's Traits view access(all) fun resolveTraitsView(): MetadataViews.Traits { // sports radar team id let excludedNames: [String] = [] // Get subedition let subedition = Crowdflix.getSubeditionByNFTID(self.id) // Create a dictionary of this NFT's traits with default metadata var traits: {String: AnyStruct} = { "SeriesNumber": Crowdflix.getSetSeries(setID: self.data.setID), "SetName": Crowdflix.getSetName(setID: self.data.setID), "SerialNumber": self.data.serialNumber, "Subedition": subedition?.name ?? "Standard", "SubeditionID": subedition?.subeditionID ?? 0 } // Add MovieScene specific data traits = self.mapMovieSceneData(dict: traits) return MetadataViews.dictToTraits(dict: traits, excludedNames: excludedNames) } // Functions used for computing MetadataViews // mapMovieSceneData helps build our trait map from MovieScene metadata // Returns: The trait map with all non-empty fields from MovieScene data added access(all) fun mapMovieSceneData(dict: {String: AnyStruct}) : {String: AnyStruct} { let movieSceneMetadata = Crowdflix.getMovieSceneMetaData(movieSceneID: self.data.movieSceneID) ?? {} for name in movieSceneMetadata.keys { let value = movieSceneMetadata[name] ?? "" if value != "" { dict.insert(key: name, value) } } return dict } // getMomentURL // Returns: The computed external url of the moment access(all) view fun getMomentURL(): String { return "https://marketplace.crowdflix.io/moment/".concat(self.id.toString()) } // getEditionName Moment's edition name is a combination of the Moment's setName and movieSceneID // `setName: #movieSceneID` access(all) view fun getEditionName(): String { let setName: String = Crowdflix.getSetName(setID: self.data.setID) ?? "" let editionName = setName.concat(": #").concat(self.data.movieSceneID.toString()) return editionName } access(all) view fun assetPath(): String { return "https://assets.crowdflix.io/media/".concat(self.id.toString()) } // returns a url to display poster image access(all) view fun posterimage(): String { return self.appendOptionalParams(url: self.assetPath().concat("/poster"), firstDelim: "?") } // a url to display a video associated with the moment access(all) view fun video(): String { return self.appendOptionalParams(url: self.assetPath().concat("/video"), firstDelim: "?") } // appends and optional network param needed to resolve the media access(all) view fun appendOptionalParams(url: String, firstDelim: String): String { if Crowdflix.Network() == "testnet" { return url.concat(firstDelim).concat("testnet") } return url } // Create an empty Collection for Crowdflix NFTs and return it to the caller access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} { return <- Crowdflix.createEmptyCollection(nftType: Type<@NFT>()) } } // Admin is a special authorization resource that // allows the owner to perform important functions to modify the // various aspects of the Plays, Sets, and Moments // access(all) resource Admin { // createMovieScene creates a new MovieScene struct // and stores it in the MovieScenes dictionary in the Crowdflix smart contract // // Parameters: metadata: A dictionary mapping metadata titles to their data // example: {"Title": "Avengers: Endgame", "sceneDescription": "Iron Man's iconic snap defeating Thanos"} // // Returns: the ID of the new MovieScene object // access(all) fun createMovieScene(metadata: {String: String}): UInt32 { // Create the new MovieScene var newMovieScene = MovieScene(metadata: metadata) let newID = newMovieScene.movieSceneID // Increment the ID so that it isn't used again Crowdflix.nextMovieSceneID = Crowdflix.nextMovieSceneID + UInt32(1) emit MovieSceneCreated(id: newMovieScene.movieSceneID, metadata: metadata) // Store it in the contract storage Crowdflix.movieSceneDatas[newID] = newMovieScene return newID } // createSet creates a new Set resource and stores it // in the sets mapping in the Crowdflix contract // // Parameters: name: The name of the Set // // Returns: The ID of the created set access(all) fun createSet(name: String): UInt32 { // Create the new Set var newSet <- create Set(name: name) // Increment the setID so that it isn't used again Crowdflix.nextSetID = Crowdflix.nextSetID + UInt32(1) let newID = newSet.setID emit SetCreated(setID: newSet.setID, series: Crowdflix.currentSeries) // Store it in the sets mapping field Crowdflix.sets[newID] <-! newSet return newID } // borrowSet returns a reference to a set in the Crowdflix // contract so that the admin can call methods on it // // Parameters: setID: The ID of the Set that you want to // get a reference to // // Returns: A reference to the Set with all of the fields // and methods exposed // access(all) view fun borrowSet(setID: UInt32): &Set { pre { Crowdflix.sets[setID] != nil: "Cannot borrow Set: The Set doesn't exist" } // Get a reference to the Set and return it // use `&` to indicate the reference to the object and type return (&Crowdflix.sets[setID] as &Set?)! } // startNewSeries ends the current series by incrementing // the series number, meaning that Moments minted after this // will use the new series number // // Returns: The new series number // access(all) fun startNewSeries(): UInt32 { // End the current series and start a new one // by incrementing the Crowdflix series number Crowdflix.currentSeries = Crowdflix.currentSeries + UInt32(1) emit NewSeriesStarted(newCurrentSeries: Crowdflix.currentSeries) return Crowdflix.currentSeries } // createSubeditionResource creates new SubeditionMap resource that // will be used to mint Moments with Subeditions access(all) fun createSubeditionAdminResource() { Crowdflix.account.storage.save<@SubeditionAdmin>(<- create SubeditionAdmin(), to: Crowdflix.SubeditionAdminStoragePath()) } // setMomentsSubedition saves which Subedition the Moment belongs to // // Parameters: nftID: The ID of the NFT // subeditionID: The ID of the Subedition the Moment belongs to // setID: The ID of the Set that the Moment references // movieSceneID: The ID of the MovieScene that the Moment references // access(all) fun setMomentsSubedition(nftID: UInt64, subeditionID: UInt32, setID: UInt32, movieSceneID: UInt32) { let subeditionAdmin = Crowdflix.account.storage.borrow<&SubeditionAdmin>(from: Crowdflix.SubeditionAdminStoragePath()) ?? panic("No subedition admin resource in storage") subeditionAdmin.setMomentsSubedition(nftID: nftID, subeditionID: subeditionID, setID: setID, movieSceneID: movieSceneID) } // createSubedition creates a new Subedition struct // and stores it in the Subeditions dictionary in the SubeditionAdmin resource // // Parameters: name: The name of the Subedition // metadata: A dictionary mapping metadata titles to their data // // Returns: the ID of the new Subedition object // access(all) fun createSubedition(name: String, metadata: {String: String}): UInt32 { let subeditionAdmin = Crowdflix.account.storage.borrow<&SubeditionAdmin>(from: Crowdflix.SubeditionAdminStoragePath()) ?? panic("No subedition admin resource in storage") return subeditionAdmin.createSubedition(name:name, metadata:metadata) } // createNewAdmin creates a new Admin resource // access(all) fun createNewAdmin(): @Admin { return <- create Admin() } } // This is the interface that users can cast their Moment Collection as // to allow others to deposit Moments into their Collection. It also allows for reading // the IDs of Moments in the Collection. /// Deprecated: This is no longer used for defining access control anymore. access(all) resource interface MomentCollectionPublic : NonFungibleToken.CollectionPublic { access(all) fun batchDeposit(tokens: @{NonFungibleToken.Collection}) access(all) fun borrowMoment(id: UInt64): &NFT? { // If the result isn't nil, the id of the returned reference // should be the same as the argument to the function post { (result == nil) || (result?.id == id): "Cannot borrow Moment reference: The ID of the returned reference is incorrect" } } } // Collection is a resource that every user who owns NFTs // will store in their account to manage their NFTS // access(all) resource Collection: MomentCollectionPublic, NonFungibleToken.Collection { // Dictionary of Moment conforming tokens // NFT is a resource type with a UInt64 ID field access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}} init() { self.ownedNFTs <- {} } // Return a list of NFT types that this receiver accepts access(all) view fun getSupportedNFTTypes(): {Type: Bool} { let supportedTypes: {Type: Bool} = {} supportedTypes[Type<@NFT>()] = true return supportedTypes } // Return whether or not the given type is accepted by the collection // A collection that can accept any type should just return true by default access(all) view fun isSupportedNFTType(type: Type): Bool { if type == Type<@NFT>() { return true } return false } // Return the amount of NFTs stored in the collection access(all) view fun getLength(): Int { return self.ownedNFTs.length } // Create an empty Collection for Crowdflix NFTs and return it to the caller access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} { return <- Crowdflix.createEmptyCollection(nftType: Type<@NFT>()) } // withdraw removes an Moment from the Collection and moves it to the caller // // Parameters: withdrawID: The ID of the NFT // that is to be removed from the Collection // // returns: @NonFungibleToken.NFT the token that was withdrawn access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} { // Borrow nft and check if locked let nft = self.borrowNFT(withdrawID) ?? panic("Cannot borrow: empty reference") // Remove the nft from the Collection let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("Cannot withdraw: Moment does not exist in the collection") emit Withdraw(id: token.id, from: self.owner?.address) // Return the withdrawn token return <- token } // batchWithdraw withdraws multiple tokens and returns them as a Collection // // Parameters: ids: An array of IDs to withdraw // // Returns: @NonFungibleToken.Collection: A collection that contains // the withdrawn moments // access(NonFungibleToken.Withdraw) fun batchWithdraw(ids: [UInt64]): @{NonFungibleToken.Collection} { // Create a new empty Collection var batchCollection <- create Collection() // Iterate through the ids and withdraw them from the Collection for id in ids { batchCollection.deposit(token: <- self.withdraw(withdrawID: id)) } // Return the withdrawn tokens return <- batchCollection } // deposit takes a Moment and adds it to the Collections dictionary // // Paramters: token: the NFT to be deposited in the collection // access(all) fun deposit(token: @{NonFungibleToken.NFT}) { // Cast the deposited token as a Crowdflix NFT to make sure // it is the correct type let token <- token as! @NFT // Get the token's ID let id = token.id // Add the new token to the dictionary let oldToken <- self.ownedNFTs[id] <- token // Only emit a deposit event if the Collection // is in an account's storage if self.owner?.address != nil { emit Deposit(id: id, to: self.owner?.address) } // Destroy the empty old token that was "removed" destroy oldToken } // batchDeposit takes a Collection object as an argument // and deposits each contained NFT into this Collection access(all) fun batchDeposit(tokens: @{NonFungibleToken.Collection}) { // Get an array of the IDs to be deposited let keys = tokens.getIDs() // Iterate through the keys in the collection and deposit each one for key in keys { self.deposit(token: <- tokens.withdraw(withdrawID: key)) } // Destroy the empty Collection destroy tokens } // destroyMoments destroys moments in this collection // unlocks the moments if they are locked // // Parameters: ids: An array of NFT IDs // to be destroyed from the Collection access(NonFungibleToken.Update) fun destroyMoments(ids: [UInt64]) { for id in ids { // Remove the nft from the Collection let token <- self.ownedNFTs.remove(key: id) ?? panic("Cannot destroy: Moment does not exist in collection: ".concat(id.toString())) // Emit a withdraw event here so that platforms do not have to understand Crowdflix-specific events to see ownership change // A withdraw without a corresponding deposit means the NFT in question has no owner address emit Withdraw(id: id, from: self.owner?.address) destroy token } } // getIDs returns an array of the IDs that are in the Collection access(all) view fun getIDs(): [UInt64] { return self.ownedNFTs.keys } // borrowNFT Returns a borrowed reference to a Moment in the Collection // so that the caller can read its ID // // Parameters: id: The ID of the NFT to get the reference for // // Returns: A reference to the NFT // // Note: This only allows the caller to read the ID of the NFT, // not any crowdflix specific data. Please use borrowMoment to // read Moment data. // access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? { return &self.ownedNFTs[id] } // borrowMoment returns a borrowed reference to a Moment // so that the caller can read data and call methods from it. // They can use this to read its setID, movieSceneID, serialNumber, // or any of the setData or MovieScene data associated with it by // getting the setID or movieSceneID and reading those fields from // the smart contract. // // Parameters: id: The ID of the NFT to get the reference for // // Returns: A reference to the NFT access(all) view fun borrowMoment(id: UInt64): &NFT? { return self.borrowNFT(id) as! &NFT? } access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? { if let nft = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? { return nft as &{ViewResolver.Resolver} } return nil } } // ----------------------------------------------------------------------- // Crowdflix contract-level function definitions // ----------------------------------------------------------------------- // createEmptyCollection creates a new, empty Collection object so that // a user can store it in their account storage. // Once they have a Collection in their storage, they are able to receive // Moments in transactions. // access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} { if nftType != Type<@NFT>() { panic("NFT type is not supported") } return <- create Crowdflix.Collection() } // getAllMovieScenes returns all the movieScenes in crowdflix // // Returns: An array of all the movieScenes that have been created access(all) view fun getAllMovieScenes(): [MovieScene] { return Crowdflix.movieSceneDatas.values } // getMovieSceneMetaData returns all the metadata associated with a specific MovieScene // // Parameters: movieSceneID: The id of the MovieScene that is being searched // // Returns: The metadata as a String to String mapping optional access(all) view fun getMovieSceneMetaData(movieSceneID: UInt32): {String: String}? { return self.movieSceneDatas[movieSceneID]?.metadata } // getMovieSceneMetaDataByField returns the metadata associated with a // specific field of the metadata // Ex: field: "contentType" will return something // like "movie" or "tv show" // // Parameters: movieSceneID: The id of the MovieScene that is being searched // field: The field to search for // // Returns: The metadata field as a String Optional access(all) view fun getMovieSceneMetaDataByField(movieSceneID: UInt32, field: String): String? { // Don't force a revert if the movieSceneID or field is invalid if let movieScene = Crowdflix.movieSceneDatas[movieSceneID] { return movieScene.metadata[field] } return nil } // getSetData returns the data that the specified Set // is associated with. // // Parameters: setID: The id of the Set that is being searched // // Returns: The QuerySetData struct that has all the important information about the set access(all) fun getSetData(setID: UInt32): QuerySetData? { if Crowdflix.sets[setID] == nil { return nil } return QuerySetData(setID: setID) } // getSetName returns the name that the specified Set // is associated with. // // Parameters: setID: The id of the Set that is being searched // // Returns: The name of the Set access(all) view fun getSetName(setID: UInt32): String? { // Don't force a revert if the setID is invalid return Crowdflix.setDatas[setID]?.name } // getSetSeries returns the series that the specified Set // is associated with. // // Parameters: setID: The id of the Set that is being searched // // Returns: The series that the Set belongs to access(all) view fun getSetSeries(setID: UInt32): UInt32? { // Don't force a revert if the setID is invalid return Crowdflix.setDatas[setID]?.series } // getSetIDsByName returns the IDs that the specified Set name // is associated with. // // Parameters: setName: The name of the Set that is being searched // // Returns: An array of the IDs of the Set if it exists, or nil if doesn't access(all) fun getSetIDsByName(setName: String): [UInt32]? { var setIDs: [UInt32] = [] // Iterate through all the setDatas and search for the name for setData in Crowdflix.setDatas.values { if setName == setData.name { // If the name is found, return the ID setIDs.append(setData.setID) } } // If the name isn't found, return nil // Don't force a revert if the setName is invalid if setIDs.length == 0 { return nil } return setIDs } // getMovieScenesInSet returns the list of MovieScene IDs that are in the Set // // Parameters: setID: The id of the Set that is being searched // // Returns: An array of MovieScene IDs access(all) view fun getMovieScenesInSet(setID: UInt32): [UInt32]? { // Don't force a revert if the setID is invalid return Crowdflix.sets[setID]?.movieScenes } // isEditionRetired returns a boolean that indicates if a Set/MovieScene combo // (otherwise known as an edition) is retired. // If an edition is retired, it still remains in the Set, // but Moments can no longer be minted from it. // // Parameters: setID: The id of the Set that is being searched // movieSceneID: The id of the MovieScene that is being searched // // Returns: Boolean indicating if the edition is retired or not access(all) fun isEditionRetired(setID: UInt32, movieSceneID: UInt32): Bool? { // Return the retired status for the MovieScene in the set if it exists if let setdata = self.getSetData(setID: setID) { return setdata.getRetired()[movieSceneID] } return nil } // isSetLocked returns a boolean that indicates if a Set // is locked. If it's locked, // new MovieScenes can no longer be added to it, // but Moments can still be minted from MovieScenes the set contains. // // Parameters: setID: The id of the Set that is being searched // // Returns: Boolean indicating if the Set is locked or not access(all) view fun isSetLocked(setID: UInt32): Bool? { // Don't force a revert if the setID is invalid return Crowdflix.sets[setID]?.locked } // getNumMomentsInEdition return the number of Moments that have been // minted from a certain edition. // // Parameters: setID: The id of the Set that is being searched // movieSceneID: The id of the MovieScene that is being searched // // Returns: The total number of Moments // that have been minted from an edition access(all) fun getNumMomentsInEdition(setID: UInt32, movieSceneID: UInt32): UInt32? { // Return the number of moments minted for the MovieScene in the set if it exists if let setdata = self.getSetData(setID: setID) { return setdata.getNumberMintedPerMovieScene()[movieSceneID] } return nil } // getMomentsSubedition returns the Subedition the Moment belongs to // // Parameters: nftID: The ID of the NFT // // returns: UInt32? Subedition's ID if exists // access(all) view fun getMomentsSubedition(nftID: UInt64): UInt32? { let subeditionAdmin = self.account.storage.borrow<&SubeditionAdmin>(from: Crowdflix.SubeditionAdminStoragePath()) ?? panic("No subedition admin resource in storage") return subeditionAdmin.getMomentsSubedition(nftID: nftID) } // getAllSubeditions returns all the subeditions in crowdflix subeditionAdmin resource // // Returns: An array of all the subeditions that have been created access(all) view fun getAllSubeditions(): &[Subedition] { let subeditionAdmin = self.account.storage.borrow<&SubeditionAdmin>(from: Crowdflix.SubeditionAdminStoragePath()) ?? panic("No subedition admin resource in storage") return subeditionAdmin.subeditionDatas.values } // getSubeditionByID returns the subedition struct entity // // Parameters: subeditionID: The id of the Subedition that is being searched // // Returns: The Subedition struct access(all) view fun getSubeditionByID(subeditionID: UInt32): &Subedition { let subeditionAdmin = self.account.storage.borrow<&SubeditionAdmin>(from: Crowdflix.SubeditionAdminStoragePath()) ?? panic("No subedition admin resource in storage") return subeditionAdmin.subeditionDatas[subeditionID]! } // getSubeditionByNFTID returns the subedition struct that the NFT belongs to // // Parameters: nftID: The id of the NFT that is being searched // // Returns: The subedition struct that the NFT belongs to access(all) view fun getSubeditionByNFTID(_ nftID: UInt64): &Subedition? { if let subeditionAdmin = self.account.storage.borrow<&SubeditionAdmin>(from: Crowdflix.SubeditionAdminStoragePath()) { if let subeditionID = subeditionAdmin.getMomentsSubedition(nftID: nftID) { return subeditionAdmin.subeditionDatas[subeditionID] } } return nil } // This script reads the public nextSubeditionID from the SubeditionAdmin resource and // returns that number to the caller // // Returns: UInt32 // the next number in nextSubeditionID from the SubeditionAdmin resource access(all) view fun getNextSubeditionID(): UInt32 { let subeditionAdmin = self.account.storage.borrow<&SubeditionAdmin>(from: Crowdflix.SubeditionAdminStoragePath()) ?? panic("No subedition admin resource in storage") return subeditionAdmin.nextSubeditionID } // SubeditionAdmin is a resource that allows Set to mint Moments with Subeditions // access(all) struct Subedition { access(all) let subeditionID: UInt32 access(all) let name: String access(all) let metadata: {String: String} init(subeditionID: UInt32, name: String, metadata: {String: String}) { pre { name.length != 0: "New Subedition name cannot be empty" } self.subeditionID = subeditionID self.name = name self.metadata = metadata } } access(all) resource SubeditionAdmin { // Map of number of already minted Moments using Subedition. // When a new Moment with Subedition is minted, 1 is added to the // number in this map by the key, formed by concatinating of // SetID, MovieSceneID and SubeditionID access(contract) let numberMintedPerSubedition: {String: UInt32} // Map of Subedition which the Moment belongs to. // This map updates after each minting. access(contract) let momentsSubedition: {UInt64: UInt32} // The ID that is used to create Subeditions. // Every time a Subeditions is created, subeditionID is assigned // to the new Subedition's ID and then is incremented by 1. access(contract) var nextSubeditionID: UInt32 // Variable size dictionary of Subedition structs access(contract) let subeditionDatas: {UInt32: Subedition} // createSubedition creates a new Subedition struct // and stores it in the Subeditions dictionary in the SubeditionAdmin resource // // Parameters: name: The name of the Subedition // metadata: A dictionary mapping metadata titles to their data // // Returns: the ID of the new Subedition object // access(all) fun createSubedition(name: String, metadata: {String: String}): UInt32 { let newID = self.nextSubeditionID var newSubedition = Subedition(subeditionID: newID, name: name, metadata: metadata) self.nextSubeditionID = self.nextSubeditionID + UInt32(1) self.subeditionDatas[newID] = newSubedition emit SubeditionCreated(subeditionID: newID, name: name, metadata: metadata) return newID } // getMomentsSubedition function that return's wich Subedition the Moment belongs to // // Parameters: nftID: The ID of the NFT // // returns: UInt32? Subedition's ID if exists // access(all) view fun getMomentsSubedition(nftID: UInt64): UInt32? { return self.momentsSubedition[nftID] } // getNumberMintedPerSubedition function that return's // the number of Moments that have been minted for this subedition // to use as this Moment's serial number // // Parameters: setID: The ID of the Set Moment will be minted from // movieSceneID: The ID of the MovieScene Moment will be minted from // subeditionID: The ID of the Subedition using which moment will be minted // // returns: UInt32 Number of Moments, already minted for this Subedition // access(all) fun getNumberMintedPerSubedition(setID: UInt32, movieSceneID: UInt32, subeditionID: UInt32): UInt32 { let setPlaySubedition = self.getSetPlaySubeditionString(setID, movieSceneID, subeditionID) if !self.numberMintedPerSubedition.containsKey(setPlaySubedition) { self.numberMintedPerSubedition.insert(key: setPlaySubedition, UInt32(0)) return UInt32(0) } return self.numberMintedPerSubedition[setPlaySubedition]! } // addToNumberMintedPerSubedition function that increments 1 to the // number of Moments that have been minted for this subedition // // Parameters: setID: The ID of the Set Moment will be minted from // movieSceneID: The ID of the MovieScene Moment will be minted from // subeditionID: The ID of the Subedition using which moment will be minted // // access(contract) fun addToNumberMintedPerSubedition(setID: UInt32, movieSceneID: UInt32, subeditionID: UInt32) { let setPlaySubedition = self.getSetPlaySubeditionString(setID, movieSceneID, subeditionID) // Get number of moments minted for this subedition let numberMinted = self.numberMintedPerSubedition[setPlaySubedition] ?? panic("Could not find number of moments minted for specified Subedition!") // Increment the number of moments minted for this subedition self.numberMintedPerSubedition[setPlaySubedition] = numberMinted + UInt32(1) } // getSetPlaySubeditionString builds a string that is used as a key in the numberMintedPerSubedition map access(self) view fun getSetPlaySubeditionString(_ setID: UInt32, _ movieSceneID: UInt32, _ subeditionID: UInt32): String { return setID.toString().concat(movieSceneID.toString()).concat(subeditionID.toString()) } // setMomentsSubedition saves which Subedition the Moment belongs to // // Parameters: nftID: The ID of the NFT // subeditionID: The ID of the Subedition the Moment belongs to // setID: The ID of the Set that the Moment references // movieSceneID: The ID of the MovieScene that the Moment references // access(all) fun setMomentsSubedition(nftID: UInt64, subeditionID: UInt32, setID: UInt32, movieSceneID: UInt32) { pre { !self.momentsSubedition.containsKey(nftID) : "Subedition for this moment already exists!" } self.momentsSubedition.insert(key: nftID, subeditionID) emit SubeditionAddedToMoment(momentID: nftID, subeditionID: subeditionID, setID: setID, movieSceneID: movieSceneID) } init() { self.momentsSubedition = {} self.numberMintedPerSubedition = {} self.subeditionDatas = {} self.nextSubeditionID = 1 } } //------------------------------------------------------------ // Contract MetadataViews //------------------------------------------------------------ // getContractViews returns the metadata view types available for this contract access(all) view fun getContractViews(resourceType: Type?): [Type] { return [ Type<MetadataViews.NFTCollectionData>(), Type<MetadataViews.NFTCollectionDisplay>(), Type<MetadataViews.Royalties>() ] } // resolveContractView resolves this contract's metadata views access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? { post { result == nil || result!.getType() == viewType: "The returned view must be of the given type or nil" } switch viewType { case Type<MetadataViews.NFTCollectionData>(): return MetadataViews.NFTCollectionData( storagePath: /storage/MomentCollection, publicPath: /public/MomentCollection, publicCollection: Type<&Collection>(), publicLinkedType: Type<&Collection>(), createEmptyCollectionFunction: (fun (): @{NonFungibleToken.Collection} { return <- Crowdflix.createEmptyCollection(nftType: Type<@NFT>()) }) ) case Type<MetadataViews.NFTCollectionDisplay>(): let bannerImage = MetadataViews.Media( file: MetadataViews.HTTPFile( url: "https://marketplace.crowdflix.io/images/crowdflix-logo-horizontal-white.svg" ), mediaType: "image/svg+xml" ) let squareImage = MetadataViews.Media( file: MetadataViews.HTTPFile( url: "https://marketplace.crowdflix.io/images/favicon.svg" ), mediaType: "image/svg+xml" ) return MetadataViews.NFTCollectionDisplay( name: "Crowdflix", description: "Crowdflix is your gateway to own, trade, and collect iconic moments from movies, TV shows, anime, and more — all as limited-edition digital collectibles. Relive legendary scenes and build your cinematic NFT collection.", externalURL: MetadataViews.ExternalURL("https://marketplace.crowdflix.io"), squareImage: squareImage, bannerImage: bannerImage, socials: { "twitter": MetadataViews.ExternalURL("https://twitter.com/crowdflix") } ) case Type<MetadataViews.Royalties>(): let royaltyReceiver: Capability<&{FungibleToken.Receiver}> = getAccount(Crowdflix.RoyaltyAddress()).capabilities.get<&{FungibleToken.Receiver}>(MetadataViews.getRoyaltyReceiverPublicPath())! return MetadataViews.Royalties( [ MetadataViews.Royalty( receiver: royaltyReceiver, cut: 0.05, description: "Crowdflix marketplace royalty" ) ] ) } return nil } // ----------------------------------------------------------------------- // Crowdflix initialization function // ----------------------------------------------------------------------- // init() { // Initialize contract fields self.currentSeries = 0 self.movieSceneDatas = {} self.setDatas = {} self.sets <- {} self.nextMovieSceneID = 1 self.nextSetID = 1 self.totalSupply = 0 // Put a new Collection in storage self.account.storage.save<@Collection>(<- create Collection(), to: /storage/MomentCollection) // Create and publish a capability for the collection self.account.capabilities.publish( self.account.capabilities.storage.issue<&Collection>(/storage/MomentCollection), at: /public/MomentCollection ) // Put the Minter in storage self.account.storage.save<@Admin>(<- create Admin(), to: /storage/CrowdflixAdmin) } }

Cadence Script

1transaction(name: String, code: String ) {
2		prepare(signer: auth(AddContract) &Account) {
3			signer.contracts.add(name: name, code: code.utf8 )
4		}
5	}