Smart Contract

CricketMoments

A.f827352428f8f46b.CricketMoments

Deployed

1h ago
Feb 26, 2026, 07:30:11 PM UTC

Dependents

0 imports
1// SPDX-License-Identifier: UNLICENSED
2
3import NonFungibleToken from 0x631e88ae7f1d7c20
4
5/**
6
7# CricketMoments
8
9The main contract managing the cricket moments/NFTs created by Faze.
10
11## `NFT` Resource
12
13Each NFT created using this contract consists of - 
14- id : globally unique identifier for the NFT
15- momentId : The moment of which the NFT is a copy 
16- serial : serial number of this NFT
17- metadata : Extra metadata for the NFT. It contains a description and an IPFS url, which 
18contains the link to the video/image.
19
20
21## `Collection` resource
22
23Each account that owns cricket moments would need to have an instance
24of the Collection resource stored in their account storage.
25
26The Collection resource has methods that the owner and other users can call.
27
28## `CricketMomentsCollectionPublic` resource interfaces
29
30An Interface which is implemented by the collection resource. It contains 
31functions for depositing moments, borrowing moments and getting id's of all
32 the moments stored in the collection.  
33
34## Locking Moments 
35The NFTMinter resource can lock specific moments. After locking a particular moment, no more copies 
36of that moment can be minted. Whether a moment is locked or not can also be read easily 
37using isMomentLocked function. 
38
39*/
40access(all) contract CricketMoments: NonFungibleToken {
41
42    // Events
43    access(all) event ContractInitialized()
44    access(all) event Withdraw(id: UInt64, from: Address?)
45    access(all) event Deposit(id: UInt64, to: Address?)
46    access(all) event Minted(id: UInt64, momentId:UInt64, serial:UInt64, ipfs:String)
47
48    // Named Paths
49    access(all) let CollectionStoragePath: StoragePath
50    access(all) let CollectionPublicPath: PublicPath
51    access(all) let MinterStoragePath: StoragePath
52
53    // totalSupply, the total number of CricketMoments that have been minted
54    access(all) var totalSupply: UInt64
55
56    // The total number of unique moments that have been minted
57    access(all) var totalMomentIds: UInt64
58
59    // A dictionary to track the moments that have been locked. No more copies of a locked moment can be minted
60    access(self) var locked: {UInt64:Bool}
61
62    // A dictionary to store the next serial number to be minted for a particular moment Id. 
63    access(self) var nextSerial: {UInt64:UInt64}
64
65    access(all) view fun getContractViews(resourceType: Type?): [Type] {
66        return []
67    }
68
69    access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
70        return nil
71    }
72
73    // NFT
74    // A Moment as an NFT
75    access(all) resource NFT: NonFungibleToken.NFT {
76        // Token's ID
77        access(all) let id: UInt64
78
79        // Token's momentId to identify the moment
80        access(all) let momentId: UInt64
81
82        // Token's serial number 
83        access(all) let serial: UInt64
84
85        // Token's metadata as a string dictionary
86        access(self) let metadata: {String : String}
87
88        // initializer
89        init(id: UInt64, momentId: UInt64, serial: UInt64, metadata: {String : String}) {
90            self.id = id
91            self.momentId = momentId
92            self.serial = serial
93            self.metadata = metadata
94        }
95
96        // get complete metadata
97        access(all) fun getMetadata() : {String:String} {
98            return self.metadata;
99        }
100
101        // get metadata field by key
102        access(all) fun getMetadataField(key:String) : String? {
103            if let value = self.metadata[key] {
104                return value
105            }
106            return nil;
107        }
108
109        /// Same as getViews above, but on a specific NFT instead of a contract
110        access(all) view fun getViews(): [Type] {
111            return []
112        }
113
114        /// Same as resolveView above, but on a specific NFT instead of a contract
115        access(all) fun resolveView(_ view: Type): AnyStruct? {
116            return nil
117        }
118    
119
120        /// createEmptyCollection creates an empty Collection
121        /// and returns it to the caller so that they can own NFTs
122        /// @{NonFungibleToken.Collection}
123        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
124            return <-CricketMoments.createEmptyCollection(nftType: Type<@CricketMoments.NFT>())
125        }
126    }
127
128    // This is the interface that users can cast their CricketMoments Collection as
129    // to allow others to deposit CricketMoments into their Collection. It also allows for reading
130    // the details of CricketMoments in the Collection.
131    access(all) resource interface CricketMomentsCollectionPublic {
132        access(all) fun deposit(token: @{NonFungibleToken.NFT})
133        access(all) view fun getIDs(): [UInt64]
134        access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}?
135        access(all) view fun borrowCricketMoment(id: UInt64): &CricketMoments.NFT? {
136            // If the result isn't nil, the id of the returned reference
137            // should be the same as the argument to the function
138            post {
139                (result == nil) || (result?.id == id):
140                    "Cannot borrow Moment reference: The Id of the returned reference is incorrect"
141            }
142        }
143    }
144
145    // Collection
146    // A collection of Moment NFTs owned by an account
147    //
148    access(all) resource Collection: NonFungibleToken.Collection, CricketMomentsCollectionPublic {
149        // dictionary of NFT conforming tokens
150        // NFT is a resource type with an `UInt64` ID field
151        access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
152
153        /// getSupportedNFTTypes returns a list of NFT types that this receiver accepts
154        access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
155            let supportedTypes: {Type: Bool} = {}
156            supportedTypes[Type<@CricketMoments.NFT>()] = true
157            return supportedTypes
158        }
159
160        /// Returns whether or not the given type is accepted by the collection
161        /// A collection that can accept any type should just return true by default
162        access(all) view fun isSupportedNFTType(type: Type): Bool {
163            return type == Type<@CricketMoments.NFT>()
164        }
165
166        // withdraw
167        // Removes an NFT from the collection and moves it to the caller
168        access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
169            let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
170
171            emit Withdraw(id: token.id, from: self.owner?.address)
172
173            return <-token
174        }
175
176        // deposit
177        // Takes a NFT and adds it to the collections dictionary
178        access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
179            let token <- token as! @CricketMoments.NFT
180
181            let id: UInt64 = token.id
182
183            // add the new token to the dictionary which removes the old one
184            let oldToken <- self.ownedNFTs[id] <- token
185
186            emit Deposit(id: id, to: self.owner?.address)
187
188            destroy oldToken
189        }
190
191        // getIDs
192        // Returns an array of the IDs that are in the collection
193        //
194        access(all) view fun getIDs(): [UInt64] {
195            return self.ownedNFTs.keys
196        }
197
198        // borrowNFT
199        // Gets a reference to an NFT in the collection
200        // so that the caller can read its id
201        //
202        access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
203            return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)
204        }
205
206        // borrowCricketMoment
207        // Gets a reference to an NFT in the collection as a Moment,
208        // exposing all of its fields (including the momentId, serial, and metadata).
209        // This is safe as there are no functions that can be called on the Moment.
210        // Metadata is also a private field, therefore can't be changed using borrowed object.
211        //
212        access(all) view fun borrowCricketMoment(id: UInt64): &CricketMoments.NFT? {
213            if self.ownedNFTs[id] != nil {
214                let ref = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
215                return ref as! &CricketMoments.NFT
216            } else {
217                return nil
218            }
219        }
220
221        /// createEmptyCollection creates an empty Collection of the same type
222        /// and returns it to the caller
223        /// @return A an empty collection of the same type
224        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
225            return <-CricketMoments.createEmptyCollection(nftType: Type<@CricketMoments.NFT>())
226        }
227
228        // initializer
229        //
230        init () {
231            self.ownedNFTs <- {}
232        }
233    }
234
235    /// createEmptyCollection creates an empty Collection for the specified NFT type
236    /// and returns it to the caller so that they can own NFTs
237    access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
238        return <- create Collection()
239    }
240
241    // NFTMinter
242    // Resource that an admin or something similar would own to be
243    // able to mint new NFTs
244    //
245    access(all) resource NFTMinter {
246
247        // mintNFTs
248        // Mints multiple new NFTs with the same momentId
249        // Increments serial number
250        // deposits all in the recipients collection using their collection reference
251        //
252        access(all) fun mintNewNFTs(recipient: &{NonFungibleToken.CollectionPublic}, serialQuantity: UInt64, metadata: {String : String}) {
253
254
255            var serialNumber = 1 as UInt64
256            var ipfs: String = metadata["ipfs"] ?? panic("IPFS url not available")
257            while serialNumber <= serialQuantity {
258                emit Minted(
259                    id: CricketMoments.totalSupply, 
260                    momentId:CricketMoments.totalMomentIds, 
261                    serial:serialNumber,
262                    ipfs:ipfs
263                    )
264
265                // create NFT and deposit it in the recipient's account using their reference
266                recipient.deposit(token: <-create CricketMoments.NFT(
267                    id: CricketMoments.totalSupply, 
268                    momentId: CricketMoments.totalMomentIds, 
269                    serial: serialNumber, 
270                    metadata: metadata
271                ))
272
273                serialNumber = serialNumber + (1 as UInt64)
274
275                CricketMoments.totalSupply = CricketMoments.totalSupply + (1 as UInt64)
276            }
277            // Save current serial number so that next copies of the same moment can start from here
278            CricketMoments.nextSerial[CricketMoments.totalMomentIds] = (serialNumber as UInt64)
279            // Initialize locked as false for a new Moment.
280            CricketMoments.locked[CricketMoments.totalMomentIds] = false
281            // Increment totalMomentIds
282            CricketMoments.totalMomentIds = CricketMoments.totalMomentIds + (1 as UInt64)
283        }
284
285        // Mint more NFTs for a particular momentId that already exists
286        access(all) fun mintOldNFTs(recipient: &{NonFungibleToken.CollectionPublic}, momentId:UInt64, serialQuantity: UInt64, metadata: {String : String}) {
287
288            var ipfs: String = metadata["ipfs"] ?? panic("IPFS url not available")
289            var serialNumber = CricketMoments.nextSerial[momentId] ?? panic("momentId not present")
290            var isLocked = CricketMoments.locked[momentId] ?? panic("momentId not present")
291            if (isLocked==true){
292                panic("Moment already locked. Can't mint any more NFTs for this momentId")
293            }
294
295            var i = 1 as UInt64
296            while i <= serialQuantity {
297                emit Minted(
298                    id: CricketMoments.totalSupply, 
299                    momentId:momentId, 
300                    serial:serialNumber,
301                    ipfs:ipfs
302                    )
303
304                // deposit it in the recipient's account using their reference
305                recipient.deposit(token: <-create CricketMoments.NFT(
306                    id: CricketMoments.totalSupply, 
307                    momentId: momentId, 
308                    serial: serialNumber, 
309                    metadata: metadata
310                ))
311
312                serialNumber = serialNumber + (1 as UInt64)
313                i = i + 1 as UInt64
314                CricketMoments.totalSupply = CricketMoments.totalSupply + (1 as UInt64)
315            }
316            // Save current serial number so that next copies of the same moment can start from here
317            // No need to increment totalMomentIds
318            CricketMoments.nextSerial[momentId] = serialNumber
319
320        }
321
322        // Lock a particular momentId, so that no more moments with this momentId can be minted. 
323        access(all) fun lockMoment(momentId:UInt64) {
324
325            if (CricketMoments.locked[momentId]==nil) {
326                panic("Moment not minted yet")
327            }
328            CricketMoments.locked[momentId]=true
329        }
330    }
331
332    // fetch
333    // Get a reference to a CricketMoments from an account's Collection, if available.
334    // If an account does not have a CricketMoments.Collection, panic.
335    // If it has a collection but does not contain the itemID, return nil.
336    // If it has a collection and that collection contains the itemID, return a reference to that.
337    //
338    access(all) fun fetch(_ from: Address, id: UInt64): &CricketMoments.NFT? {
339        let collection = getAccount(from).capabilities
340            .borrow<&CricketMoments.Collection>(CricketMoments.CollectionPublicPath)
341            ?? panic("Couldn't get collection")
342        // We trust CricketMoments.Collection.borrowMoment to get the correct itemID
343        // (it checks it before returning it).
344        return collection.borrowCricketMoment(id: id)
345    }
346
347    // get next serial for a momentId (nextSerial -1 indicates number of copies minted for this momentId)
348    access(all) fun getNextSerial(momentId:UInt64): UInt64? {
349        
350        if let nextSerial = self.nextSerial[momentId] {
351            return nextSerial
352        }
353        return nil
354    }
355
356    // check if a moment is locked. No more copies of a locked moment can be minted 
357    access(all) fun isMomentLocked(momentId:UInt64): Bool? {
358        
359        if let isLocked = self.locked[momentId] {
360            return isLocked
361        }
362        return nil
363    }
364
365    // initializer
366    //
367    init() {
368        // Set our named paths
369        self.CollectionStoragePath = /storage/CricketMomentsCollection
370        self.CollectionPublicPath = /public/CricketMomentsCollection
371        self.MinterStoragePath = /storage/CricketMomentsMinter
372        
373        // Initialize the total supply
374        self.totalSupply = 0
375
376        // Initialize the total Moment IDs
377        self.totalMomentIds = 0
378        
379        // Initialize locked and nextSerial as empty dictionaries
380        self.locked = {}
381        self.nextSerial = {}
382
383        // Create a Minter resource and save it to storage
384        let minter <- create NFTMinter()
385        self.account.storage.save(<-minter, to: self.MinterStoragePath)
386
387        emit ContractInitialized()
388    }
389}
390