Smart Contract

CricketMomentsSOTM

A.4eded0de73020ca5.CricketMomentsSOTM

Valid From

118,545,691

Deployed

1w ago
Feb 19, 2026, 08:41:48 AM UTC

Dependents

1 imports
1// SPDX-License-Identifier: UNLICENSED
2
3import NonFungibleToken from 0x1d7e57aa55817448
4
5/**
6
7# CricketMomentsSOTM
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 CricketMomentsSOTM: 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 CricketMomentsSOTM 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 <-CricketMomentsSOTM.createEmptyCollection(nftType: Type<@CricketMomentsSOTM.NFT>())
125        }
126    }
127
128    // This is the interface that users can cast their CricketMomentsSOTM Collection as
129    // to allow others to deposit CricketMomentsSOTM into their Collection. It also allows for reading
130    // the details of CricketMomentsSOTM in the Collection.
131    access(all) resource interface CricketMomentsSOTMCollectionPublic {
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): &CricketMomentsSOTM.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, CricketMomentsSOTMCollectionPublic {
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<@CricketMomentsSOTM.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<@CricketMomentsSOTM.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! @CricketMomentsSOTM.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        access(all) view fun getIDs(): [UInt64] {
194            return self.ownedNFTs.keys
195        }
196
197        // borrowNFT
198        // Gets a reference to an NFT in the collection
199        // so that the caller can read its id
200        //
201        access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
202            return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)
203        }
204
205        // borrowCricketMoment
206        // Gets a reference to an NFT in the collection as a Moment,
207        // exposing all of its fields (including the momentId, serial, and metadata).
208        // This is safe as there are no functions that can be called on the Moment.
209        // Metadata is also a private field, therefore can't be changed using borrowed object.
210        //
211        access(all) view fun borrowCricketMoment(id: UInt64): &CricketMomentsSOTM.NFT? {
212            if self.ownedNFTs[id] != nil {
213                let ref = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
214                return ref as! &CricketMomentsSOTM.NFT
215            } else {
216                return nil
217            }
218        }
219
220        /// createEmptyCollection creates an empty Collection of the same type
221        /// and returns it to the caller
222        /// @return A an empty collection of the same type
223        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
224            return <-CricketMomentsSOTM.createEmptyCollection(nftType: Type<@CricketMomentsSOTM.NFT>())
225        }
226
227        // initializer
228        //
229        init () {
230            self.ownedNFTs <- {}
231        }
232    }
233
234    /// createEmptyCollection creates an empty Collection for the specified NFT type
235    /// and returns it to the caller so that they can own NFTs
236    access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
237        return <- create Collection()
238    }
239
240    // NFTMinter
241    // Resource that an admin or something similar would own to be
242    // able to mint new NFTs
243    //
244    access(all) resource NFTMinter {
245
246        // mintNFTs
247        // Mints multiple new NFTs with the same momentId
248        // Increments serial number
249        // deposits all in the recipients collection using their collection reference
250        //
251        access(all) fun mintNewNFTs(recipient: &{NonFungibleToken.CollectionPublic}, serialQuantity: UInt64, metadata: {String : String}) {
252
253
254            var serialNumber = 1 as UInt64
255            var ipfs: String = metadata["ipfs"] ?? panic("IPFS url not available")
256            while serialNumber <= serialQuantity {
257                emit Minted(
258                    id: CricketMomentsSOTM.totalSupply, 
259                    momentId:CricketMomentsSOTM.totalMomentIds, 
260                    serial:serialNumber,
261                    ipfs:ipfs
262                    )
263
264                // create NFT and deposit it in the recipient's account using their reference
265                recipient.deposit(token: <-create CricketMomentsSOTM.NFT(
266                    id: CricketMomentsSOTM.totalSupply, 
267                    momentId: CricketMomentsSOTM.totalMomentIds, 
268                    serial: serialNumber, 
269                    metadata: metadata
270                ))
271
272                serialNumber = serialNumber + (1 as UInt64)
273
274                CricketMomentsSOTM.totalSupply = CricketMomentsSOTM.totalSupply + (1 as UInt64)
275            }
276            // Save current serial number so that next copies of the same moment can start from here
277            CricketMomentsSOTM.nextSerial[CricketMomentsSOTM.totalMomentIds] = (serialNumber as UInt64)
278            // Initialize locked as false for a new Moment.
279            CricketMomentsSOTM.locked[CricketMomentsSOTM.totalMomentIds] = false
280            // Increment totalMomentIds
281            CricketMomentsSOTM.totalMomentIds = CricketMomentsSOTM.totalMomentIds + (1 as UInt64)
282        }
283
284        // Mint more NFTs for a particular momentId that already exists
285        access(all) fun mintOldNFTs(recipient: &{NonFungibleToken.CollectionPublic}, momentId:UInt64, serialQuantity: UInt64, metadata: {String : String}) {
286
287            var ipfs: String = metadata["ipfs"] ?? panic("IPFS url not available")
288            var serialNumber = CricketMomentsSOTM.nextSerial[momentId] ?? panic("momentId not present")
289            var isLocked = CricketMomentsSOTM.locked[momentId] ?? panic("momentId not present")
290            if (isLocked==true){
291                panic("Moment already locked. Can't mint any more NFTs for this momentId")
292            }
293
294            var i = 1 as UInt64
295            while i <= serialQuantity {
296                emit Minted(
297                    id: CricketMomentsSOTM.totalSupply, 
298                    momentId:momentId, 
299                    serial:serialNumber,
300                    ipfs:ipfs
301                    )
302
303                // deposit it in the recipient's account using their reference
304                recipient.deposit(token: <-create CricketMomentsSOTM.NFT(
305                    id: CricketMomentsSOTM.totalSupply, 
306                    momentId: momentId, 
307                    serial: serialNumber, 
308                    metadata: metadata
309                ))
310
311                serialNumber = serialNumber + (1 as UInt64)
312                i = i + 1 as UInt64
313                CricketMomentsSOTM.totalSupply = CricketMomentsSOTM.totalSupply + (1 as UInt64)
314            }
315            // Save current serial number so that next copies of the same moment can start from here
316            // No need to increment totalMomentIds
317            CricketMomentsSOTM.nextSerial[momentId] = serialNumber
318
319        }
320
321        // Lock a particular momentId, so that no more moments with this momentId can be minted. 
322        access(all) fun lockMoment(momentId:UInt64) {
323
324            if (CricketMomentsSOTM.locked[momentId]==nil) {
325                panic("Moment not minted yet")
326            }
327            CricketMomentsSOTM.locked[momentId]=true
328        }
329    }
330
331    // fetch
332    // Get a reference to a CricketMomentsSOTM from an account's Collection, if available.
333    // If an account does not have a CricketMomentsSOTM.Collection, panic.
334    // If it has a collection but does not contain the itemID, return nil.
335    // If it has a collection and that collection contains the itemID, return a reference to that.
336    //
337    access(all) fun fetch(_ from: Address, id: UInt64): &CricketMomentsSOTM.NFT? {
338        let collection = getAccount(from).capabilities
339            .borrow<&CricketMomentsSOTM.Collection>(CricketMomentsSOTM.CollectionPublicPath)
340            ?? panic("Couldn't get collection")
341        // We trust CricketMomentsSOTM.Collection.borrowMoment to get the correct itemID
342        // (it checks it before returning it).
343        return collection.borrowCricketMoment(id: id)
344    }
345
346    // get next serial for a momentId (nextSerial -1 indicates number of copies minted for this momentId)
347    access(all) fun getNextSerial(momentId:UInt64): UInt64? {
348        
349        if let nextSerial = self.nextSerial[momentId] {
350            return nextSerial
351        }
352        return nil
353    }
354
355    // check if a moment is locked. No more copies of a locked moment can be minted 
356    access(all) fun isMomentLocked(momentId:UInt64): Bool? {
357        
358        if let isLocked = self.locked[momentId] {
359            return isLocked
360        }
361        return nil
362    }
363
364    // initializer
365    //
366    init() {
367        // Set our named paths
368        self.CollectionStoragePath = /storage/CricketMomentsSOTMCollection
369        self.CollectionPublicPath = /public/CricketMomentsSOTMCollection
370        self.MinterStoragePath = /storage/CricketMomentsSOTMMinter
371        
372        // Initialize the total supply
373        self.totalSupply = 0
374
375        // Initialize the total Moment IDs
376        self.totalMomentIds = 0
377        
378        // Initialize locked and nextSerial as empty dictionaries
379        self.locked = {}
380        self.nextSerial = {}
381
382        // Create a Minter resource and save it to storage
383        let minter <- create NFTMinter()
384        self.account.storage.save(<-minter, to: self.MinterStoragePath)
385
386        emit ContractInitialized()
387    }
388}
389