Smart Contract

OlympicPin

A.1d007eed492fdbbe.OlympicPin

Deployed

3d ago
Feb 24, 2026, 08:38:48 AM UTC

Dependents

8 imports
1import NonFungibleToken from 0x1d7e57aa55817448
2import ViewResolver from 0x1d7e57aa55817448
3
4access(all) contract OlympicPin : NonFungibleToken {
5
6    // The total number of Cards in existence
7    access(all) var totalSupply: UInt64
8
9
10    
11    // Emitted when a new Pin struct is created
12    access(all) event PinCreated(id: UInt32, metadata: {String:String})
13
14     // Emitted when a new series has been triggered by an admin
15    access(all) event NewSeriesStarted(newCurrentSeries: UInt32)
16
17    // Emitted when a new Set is created
18    access(all) event SetCreated(setId: UInt32, series: UInt32, name: String)
19
20    // Emitted when a new Pin is added to a Set
21    access(all) event PinAddedToSet(setId: UInt32, pinId: UInt32)
22    // Emitted when a Pin is retired from a Set and cannot be used to mint
23    access(all) event PinRetiredFromSet(setId: UInt32, pinId: UInt32, numPieces: UInt32)
24    // Emitted when a Set is locked, meaning pins cannot be added
25    access(all) event SetLocked(setId: UInt32)
26    // Emitted when a Piece is minted from a Set
27    access(all) event PieceMinted(pieceId: UInt64, pinId: UInt32, setId: UInt32, serialNumber: UInt32)
28
29    // Events for Collection-related actions
30    //
31    // Emitted when a piece is withdrawn from a Collection
32
33    access(all) event Withdraw(id: UInt64, from: Address?)
34
35    // Emitted when a piece is deposited into a Collection
36    access(all) event Deposit(id: UInt64, to: Address?)
37
38    // Emitted when a Piece is destroyed
39    access(all) event PieceDestroyed(id: UInt64)
40
41    // -----------------------------------------------------------------------
42    // OlympicPin contract-level fields.
43    // These contain actual values that are stored in the smart contract.
44    // -----------------------------------------------------------------------
45
46    // Series that this Set belongs to.
47    // Series is a concept that indicates a group of Sets through time.
48    // Many Sets can exist at a time, but only one series.
49    access(all) var currentSeries: UInt32
50
51    // Variable size dictionary of Pin structs
52    access(self) var pins: {UInt32: Pin}
53
54    // Variable size dictionary of SetData structs
55    access(self) var setDatas: {UInt32: SetData}
56
57    // Variable size dictionary of Set resources
58    access(self) var sets: @{UInt32: Set}
59
60    // The Id that is used to create pins. 
61    // Every time a Pin is created, pinId is assigned 
62    // to the new Pin's Id and then is incremented by 1.
63    access(all) var nextPinId: UInt32
64
65    // The Id that is used to create SetDatas. 
66    // Every time a SetData is created, SetId is assigned 
67    // to the new SetData's Id and then is incremented by 1.
68    access(all) var nextSetId: UInt32
69
70    access(all) let CollectionStoragePath: StoragePath
71    access(all) let CollectionPublicPath: PublicPath
72    access(all) let AdminStoragePath: StoragePath
73
74    //Pin
75    //
76    access(all) struct Pin {
77        access(all) let pinId: UInt32
78        
79        access(all) let metadata: {String: String}
80
81        init(metadata: {String: String}){
82
83            pre {
84                metadata.length != 0: "New Pin metadata cannot be empty"
85            }
86            
87            self.pinId = OlympicPin.nextPinId
88            self.metadata = metadata
89        }
90    }
91
92    //SetData
93    //
94   
95    access(all) struct SetData {
96
97        // Unique Id for the Set
98        access(all) let setId: UInt32
99
100        // Name of the Set
101        access(all) let name: String
102
103        // Series that this Set belongs to.
104        // Series is a concept that indicates a group of Sets through time.
105        // Many Sets can exist at a time, but only one series.
106        access(all) let series: UInt32
107
108        init(name: String) {
109            pre {
110                name.length > 0: "New Set name cannot be empty"
111            }
112            self.setId = OlympicPin.nextSetId
113            self.name = name
114            self.series = OlympicPin.currentSeries
115        }
116    }
117
118    // Set is a resource type that contains the functions to add and remove
119    // pins from a set and mint NFTs.
120    //
121    // It is stored in a private field in the contract so that
122    // the admin resource can call its methods.
123    //
124    // The admin can add pins to a Set so that the set can mint NFTs
125    // that reference that PinId.
126    // The NFTs that are minted by a Set will be listed as belonging to
127    // the Set that minted it, as well as the Pin it references.
128    // 
129    // Admin can also retire pins from the Set, meaning that the retired
130    // Pin can no longer have NFTs minted from it.
131    //
132    // If the admin locks the Set, no more pins can be added to it, but 
133    // NFTs can still be minted.
134    //
135    // If retireAll() and lock() are called back-to-back, 
136    // the Set is closed off forever and nothing more can be done with it.
137    access(all) resource Set {
138
139        // Unique Id for the set
140        access(all) let setId: UInt32
141
142        // Array of pins that are a part of this set.
143        // When a Pin is added to the set, its Id gets appended here.
144        // The Id does not get removed from this array when a Pin is retired.
145        access(contract) var pins: [UInt32]
146
147        // Map of Pin Ids that Indicates if a Pin in this Set can be minted.
148        // When a Pin is added to a Set, it is mapped to false (not retired).
149        // When a Pin is retired, this is set to true and cannot be changed.
150        access(contract) var retired: {UInt32: Bool}
151
152        // Indicates if the Set is currently locked.
153        // When a Set is created, it is unlocked 
154        // and pins are allowed to be added to it.
155        // When a set is locked, pins cannot be added.
156        // A Set can never be changed from locked to unlocked,
157        // the decision to lock a Set it is final.
158        // If a Set is locked, pins cannot be added, but
159        // Pieces can still be minted from pins
160        // that exist in the Set.
161        access(all) var locked: Bool
162
163        // Mapping of Pin Ids that indicates the number of Pieces
164        // that have been minted for specific pins in this Set.
165        // When a Piece is minted, this value is stored in the Piece to
166        access(contract) var numberMintedPerPin: {UInt32: UInt32}
167
168        init(name: String) {
169            self.setId = OlympicPin.nextSetId
170            self.pins = []
171            self.retired = {}
172            self.locked = false
173            self.numberMintedPerPin = {}
174
175            // Create a new SetData for this Set and store it in contract storage
176            OlympicPin.setDatas[self.setId] = SetData(name: name)
177        }
178
179        // addPin adds a pin to the set
180        //
181        // Parameters: pinId: The Id of the Pin that is being added
182        //
183        // Pre-Conditions:
184        // The Pin needs to be an existing Pin
185        // The Set needs to be not locked
186        // The Pin can't have already been added to the Set
187        //
188        access(all) fun addPin(pinId: UInt32) {
189            pre {
190                OlympicPin.pins[pinId] != nil: "Cannot add the Pin to Set: Pin doesn't exist."
191                !self.locked: "Cannot add the Pin to the Set after the set has been locked."
192                self.numberMintedPerPin[pinId] == nil: "The pin has already been added to the set."
193            }
194
195            // Add the Pin to the array of pins
196            self.pins.append(pinId)
197
198            // Open the Pin up for minting
199            self.retired[pinId] = false
200
201            // Initialize the Piece count to zero
202            self.numberMintedPerPin[pinId] = 0
203
204            emit PinAddedToSet(setId: self.setId, pinId: pinId)
205        }
206
207        // addPins adds multiple pins to the Set
208        //
209        // Parameters: pinIds: The Ids of the pins that are being added
210        //                      as an array
211        //
212        access(all) fun addPins(pinIds: [UInt32]) {
213            for pinId in pinIds {
214                self.addPin(pinId: pinId)
215            }
216        }
217
218        // retirePin retires a Pin from the Set so that it can't mint new Piece
219        //
220        // Parameters: pinId: The Id of the Pin that is being retired
221        //
222        // Pre-Conditions:
223        // The Pin is part of the Set and not retired (available for minting).
224        // 
225        access(all) fun retirePin(pinId: UInt32) {
226            pre {
227                self.retired[pinId] != nil: "Cannot retire the Pin: Pin doesn't exist in this set!"
228            }
229
230            if !self.retired[pinId]! {
231                self.retired[pinId] = true
232
233                emit PinRetiredFromSet(setId: self.setId, pinId: pinId, numPieces: self.numberMintedPerPin[pinId]!)
234            }
235        }
236
237        // retireAll retires all the pins in the Set
238        // Afterwards, none of the retired pins will be able to mint new NFT
239        //
240        access(all) fun retireAll() {
241            for pinId in self.pins {
242                self.retirePin(pinId: pinId)
243            }
244        }
245
246        // lock() locks the Set so that no more pins can be added to it
247        //
248        // Pre-Conditions:
249        // The Set should not be locked
250        access(all) fun lock() {
251            if !self.locked {
252                self.locked = true
253                emit SetLocked(setId: self.setId)
254            }
255        }
256
257        // mintPiece mints a new and returns the newly minted Piece
258        // 
259        // Parameters: pinId: The ID of the Pin that the Piece references
260        //
261        // Pre-Conditions:
262        // The Pin must exist in the Set and be allowed to mint new Pieces
263        //
264        // Returns: The NFT that was minted
265        //
266        access(all) fun mintPiece(pinId: UInt32): @NFT {
267            pre {
268                self.retired[pinId] != nil: "Cannot mint the Piece: This pin doesn't exist."
269                !self.retired[pinId]!: "Cannot mint the Piece from this pin: This pin has been retired."
270            }
271
272            // Gets the number of Pieces that have been minted for this Pin
273            // to use as this Piece's serial number
274            let numInPin = self.numberMintedPerPin[pinId]!
275
276            // Mint the new Piece
277            let newPiece: @NFT <- create NFT(pinId: pinId, setId: self.setId, serialNumber: numInPin +  UInt32(1))
278
279            // Increment the count of Pieces minted for this Pin
280            self.numberMintedPerPin[pinId] = numInPin +  UInt32(1)
281
282            return <-newPiece
283        }
284
285        // batchMintPiece mints an arbitrary quantity of Pieces 
286        // and returns them as a Collection
287        //
288        // Parameters: pinId: the ID of the Pin that the Pieces are minted for
289        //             quantity: The quantity of Pieces to be minted
290        //
291        // Returns: Collection object that contains all the Pieces that were minted
292        //
293        access(all) fun batchMintPiece(pinId: UInt32, quantity: UInt64): @Collection {
294            let newCollection <- create Collection()
295
296            var i: UInt64 = 0
297            while i < quantity {
298                newCollection.deposit(token: <-self.mintPiece(pinId: pinId))
299                i = i + UInt64(1)
300            }
301
302            return <-newCollection
303        }
304
305        access(all) view fun getPins(): [UInt32] {
306            return self.pins
307        }
308
309        access(all) view fun getRetired(): {UInt32: Bool} {
310            return self.retired
311        }
312
313        access(all) view fun getNumMintedPerPlay(): {UInt32: UInt32} {
314            return self.numberMintedPerPin
315        }
316    }
317
318    access(all) struct PieceData {
319
320        access(all) let pinId: UInt32
321        access(all) let setId: UInt32
322        access(all) let serialNumber: UInt32
323        
324        init(pinId: UInt32, setId: UInt32, serialNumber: UInt32) {
325            self.pinId = pinId
326            self.setId = setId
327            self.serialNumber = serialNumber
328        }
329    }
330
331    access(all) resource NFT: NonFungibleToken.NFT {
332
333        // Global unique Piece Id
334        access(all) let id: UInt64
335
336        // Struct of Piece metadata
337        access(all) let data: PieceData
338
339        access(all) view fun getViews(): [Type] {
340            return []
341        }
342
343        access(all) fun resolveView(_ view: Type): AnyStruct? {
344            return nil
345        }
346
347        init(pinId: UInt32, setId: UInt32, serialNumber: UInt32) {
348
349            // Increment the global Piece Ids
350            OlympicPin.totalSupply = OlympicPin.totalSupply + UInt64(1)
351
352            self.id = OlympicPin.totalSupply
353            
354            self.data = PieceData(pinId: pinId, setId: setId, serialNumber: serialNumber)
355            
356            emit PieceMinted(pieceId: self.id, pinId: self.data.pinId, setId: self.data.setId, serialNumber: self.data.serialNumber)
357        }
358
359        // If the Piece is destroyed, emit an event to indicate
360        // to outside observers that it has been destroyed
361        access(all) event ResourceDestroyed(
362            id: UInt64 = self.id,
363            serialNumber: UInt32 =  self.data.serialNumber,
364            pinId: UInt32 =  self.data.pinId,
365            setId: UInt32 = self.data.setId
366        )
367
368        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
369            return <- OlympicPin.createEmptyCollection(nftType: Type<@OlympicPin.NFT>())
370        }
371    }
372
373    access(all) resource Admin {
374        // createPin creates a new Pin struct
375        // and stores it in the pins dictionary in the Olympic smart contract
376        //
377        // Parameters: metadata: A dictionary mapping metadata titles to their data
378        //
379        // Returns: the Id of the new Pin object
380        //
381        access(all) fun createPin(metadata: {String: String}): UInt32 {
382            // Create the new Pin
383            var newPin: OlympicPin.Pin = Pin(metadata: metadata)
384            let newId: UInt32 = newPin.pinId
385
386            // Increment nextPinId
387            OlympicPin.nextPinId = OlympicPin.nextPinId + UInt32(1)
388            emit PinCreated(id: newId, metadata: metadata)
389
390            // Store it in the contract storage
391            OlympicPin.pins[newId] = newPin
392            return newId
393        }
394
395        // createSet creates a new Set struct
396        // and stores it in the Sets dictionary in the Olympic smart contract
397        //
398        // Parameters: metadata: A dictionary mapping metadata titles to their data
399        //
400        // Returns: the Id of the new SetData object
401        //
402        access(all) fun createSet(name: String): UInt32 {
403
404            // Create the new Set
405            var newSet <- create Set(name: name)
406            let newId = newSet.setId
407
408            // Increment the setId
409            OlympicPin.nextSetId = OlympicPin.nextSetId + UInt32(1)
410            emit SetCreated(setId: newId, series: OlympicPin.currentSeries, name: name)
411
412            // Store it in the sets mapping field
413            OlympicPin.sets[newId] <-! newSet
414            return newId
415        }
416
417        // borrowSet returns a reference to a set in the OlympicPin
418        // contract so that the admin can call methods on it
419        //
420        // Parameters: setId: The Id of the Set that you want to
421        // get a reference to
422        //
423        // Returns: A reference to the Set with all of the fields
424        // and methods exposed
425        //
426        access(all) view fun borrowSet(setId: UInt32): &Set {
427            pre {
428                OlympicPin.sets[setId] != nil: "Cannot borrow Set: The Set doesn't exist"
429            }
430
431            // Get a reference to the Set and return it
432            // use `&` to indicate the reference to the object and type
433            return (&OlympicPin.sets[setId] as &Set?)!
434        }
435
436        // startNewSeries ends the current series by incrementing
437        // the series number, meaning that Pieces minted after this
438        // will use the new series number
439        //
440        // Returns: The new series number
441        //
442        access(all) fun startNewSeries(): UInt32 {
443            // End the current series and start a new one
444            // by incrementing the OlympicPin series number
445            OlympicPin.currentSeries = OlympicPin.currentSeries +  UInt32(1)
446
447            emit NewSeriesStarted(newCurrentSeries: OlympicPin.currentSeries)
448
449            return OlympicPin.currentSeries
450        }
451
452        // createNewAdmin creates a new Admin resource
453        //
454        access(all) fun createNewAdmin(): @Admin {
455            return <-create Admin()
456        }
457    }
458
459    access(all) resource interface PieceCollectionPublic : NonFungibleToken.CollectionPublic {
460        access(all) fun batchDeposit(tokens: @{NonFungibleToken.Collection}): Void
461        access(all) fun borrowPiece(id: UInt64): &OlympicPin.NFT? {
462            post {
463                result == nil || result?.id == id: 
464                "Cannot borrow Piece reference: The Id of the returned reference is incorrect"
465            }
466        }
467    }
468    
469    access(all) resource Collection: PieceCollectionPublic, NonFungibleToken.Collection {
470
471        // Keep track of all the NFTs that a user owns from this contract.
472        access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
473
474        init () {
475            self.ownedNFTs <- {}
476        }
477
478        // Return a list of NFT types that this receiver accepts
479        access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
480            let supportedTypes: {Type: Bool} = {}
481            supportedTypes[Type<@OlympicPin.NFT>()] = true
482            return supportedTypes
483        }
484
485        // Return whether or not the given type is accepted by the collection
486        // A collection that can accept any type should just return true by default
487        access(all) view fun isSupportedNFTType(type: Type): Bool {
488            if type == Type<@OlympicPin.NFT>() {
489                return true
490            }
491            return false
492        }
493
494        // Return the amount of NFTs stored in the collection
495        access(all) view fun getLength(): Int {
496            return self.ownedNFTs.length
497        }
498
499        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection}{ 
500			return <- OlympicPin.createEmptyCollection(nftType: Type<@OlympicPin.NFT>())
501        }
502
503        access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
504            // Remove the nft from the Collection
505            let token <- self.ownedNFTs.remove(key: withdrawID)
506                ?? panic("Cannot withdraw: Piece does not exist in the collection")
507
508            emit Withdraw(id: token.id, from: self.owner?.address)
509
510            // Return the withdrawn token
511            return <-token
512        }
513
514        // batchWithdraw withdraws multiple tokens and returns them as a Collection
515        //
516        // Parameters: ids: An array of IDs to withdraw
517        //
518        // Returns: @NonFungibleToken.Collection: A collection that contains
519        //                                        the withdrawn pieces
520        //
521        access(NonFungibleToken.Withdraw) fun batchWithdraw(ids: [UInt64]): @{NonFungibleToken.Collection} {
522            // Create a new empty Collection
523            var batchCollection <- create Collection()
524
525            // Iterate through the ids and withdraw them from the Collection
526            for id in ids {
527                batchCollection.deposit(token: <-self.withdraw(withdrawID: id))
528            }
529
530            // Return the withdrawn tokens
531            return <-batchCollection
532        }
533
534        access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
535            let token <- token as! @OlympicPin.NFT
536            let id = token.id
537
538            // add the new token to the dictionary which removes the old one
539            let oldToken <- self.ownedNFTs[id] <- token
540            
541            // Only emit a deposit event if the Collection 
542            // is in an account's storage
543            if self.owner?.address != nil {
544                emit Deposit(id: id, to: self.owner?.address)
545            }
546            destroy oldToken
547        }
548
549        // batchDeposit takes a Collection object as an argument
550        // and deposits each contained NFT into this Collection
551        access(all) fun batchDeposit(tokens: @{NonFungibleToken.Collection}) {
552
553            // Get an array of the IDs to be deposited
554            let keys = tokens.getIDs()
555
556            // Iterate through the keys in the collection and deposit each one
557            for key in keys {
558                self.deposit(token: <-tokens.withdraw(withdrawID: key))
559            }
560
561            // Destroy the empty Collection
562            destroy tokens
563        }
564
565        access(all) view fun getIDs(): [UInt64] {
566            return self.ownedNFTs.keys
567        }
568
569        access(all) view fun borrowNFT(_ id: UInt64) : &{NonFungibleToken.NFT}? {
570            return &self.ownedNFTs[id]
571        }
572
573        // borrowPiece returns a borrowed reference to a Piece
574        // so that the caller can read data and call methods from it.
575        // They can use this to read its setID, pinID, serialNumber,
576        // or any of the setData or Pin data associated with it by
577        // getting the setID or pinID and reading those fields from
578        // the smart contract.
579        //
580        // Parameters: id: The ID of the NFT to get the reference for
581        //
582        // Returns: A reference to the NFT
583        access(all) view fun borrowPiece(id: UInt64): &OlympicPin.NFT? {
584            return self.borrowNFT(id) as! &OlympicPin.NFT?
585        }
586
587        access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
588            if let nft = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? {
589                return nft as &{ViewResolver.Resolver}
590            }
591            return nil
592        }
593    }
594
595   // -----------------------------------------------------------------------
596    // OlympicPin contract-level function definitions
597    // -----------------------------------------------------------------------
598
599    // createEmptyCollection creates a new, empty Collection object so that
600    // a user can store it in their account storage.
601    // Once they have a Collection in their storage, they are able to receive
602    // Pieces in transactions.
603    //
604    access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
605        if nftType != Type<@OlympicPin.NFT>() {
606            panic("NFT type is not supported")
607        }
608        return <-create OlympicPin.Collection()
609    }
610
611    // getAllPins returns all the pins in OlympicPin
612    //
613    // Returns: An array of all the pins that have been created
614    access(all) view fun getAllPins(): [OlympicPin.Pin] {
615        return OlympicPin.pins.values
616    }
617
618    // getPinMetaData returns all the metadata associated with a specific Pin
619    // 
620    // Parameters: pinId: The id of the Pin that is being searched
621    //
622    // Returns: The metadata as a String to String mapping optional
623    access(all) view fun getPinMetaData(pinId: UInt32): {String: String}? {
624        return self.pins[pinId]?.metadata
625    }
626
627    // getPinMetaDataByField returns the metadata associated with a 
628    //                        specific field of the metadata
629    // 
630    // Parameters: pinId: The id of the Pin that is being searched
631    //             field: The field to search for
632    //
633    // Returns: The metadata field as a String Optional
634    access(all) view fun getPinMetaDataByField(pinId: UInt32, field: String): String? {
635        // Don't force a revert if the pinId or field is invalid
636        if let pin = OlympicPin.pins[pinId] {
637            return pin.metadata[field]
638        } else {
639            return nil
640        }
641    }
642
643    // getSetName returns the name that the specified Set
644    //            is associated with.
645    // 
646    // Parameters: setId: The id of the Set that is being searched
647    //
648    // Returns: The name of the Set
649    access(all) view fun getSetName(setId: UInt32): String? {
650        // Don't force a revert if the setId is invalid
651        return OlympicPin.setDatas[setId]?.name
652    }
653
654    // getSetSeries returns the series that the specified Set
655    //              is associated with.
656    // 
657    // Parameters: setId: The id of the Set that is being searched
658    //
659    // Returns: The series that the Set belongs to
660    access(all) view fun getSetSeries(setId: UInt32): UInt32? {
661        // Don't force a revert if the setId is invalid
662        return OlympicPin.setDatas[setId]?.series
663    }
664
665    // getSetIdsByName returns the Ids that the specified Set name
666    //                 is associated with.
667    // 
668    // Parameters: setName: The name of the Set that is being searched
669    //
670    // Returns: An array of the Ids of the Set if it exists, or nil if doesn't
671    access(all) fun getSetIdsByName(setName: String): [UInt32]? {
672        var setIds: [UInt32] = []
673
674        // Iterate through all the setDatas and search for the name
675        for setData in OlympicPin.setDatas.values {
676            if setName == setData.name {
677                // If the name is found, return the Id
678                setIds.append(setData.setId)
679            }
680        }
681
682        // If the name isn't found, return nil
683        // Don't force a revert if the setName is invalid
684        if setIds.length == 0 {
685            return nil
686        } else {
687            return setIds
688        }
689    }
690
691    // getPinsInSet returns the list of Pin Ids that are in the Set
692    // 
693    // Parameters: setId: The id of the Set that is being searched
694    //
695    // Returns: An array of Pin Ids
696    access(all) view fun getPinsInSet(setId: UInt32): [UInt32]? {
697        // Don't force a revert if the setId is invalid
698        return OlympicPin.sets[setId]?.pins
699    }
700
701    // isEditionRetired returns a boolean that indicates if a Set/Pin combo
702    //                  (otherwise known as an edition) is retired.
703    //                  If an edition is retired, it still remains in the Set,
704    //                  but Pieces can no longer be minted from it.
705    // 
706    // Parameters: setId: The id of the Set that is being searched
707    //             pinId: The id of the Pin that is being searched
708    //
709    // Returns: Boolean indicating if the edition is retired or not
710    access(all) fun isEditionRetired(setId: UInt32, pinId: UInt32): Bool? {
711        if let retired = OlympicPin.sets[setId]?.retired {
712            let retired = retired[pinId]
713
714            // Return the retired status
715            return retired
716        } else {
717
718            // If the Set wasn't found, return nil
719            return nil
720        }
721    }
722
723    // isSetLocked returns a boolean that indicates if a Set
724    //             is locked. If it's locked, 
725    //             new Pins can no longer be added to it,
726    //             but NFTs can still be minted from Pins the set contains.
727    // 
728    // Parameters: setId: The id of the Set that is being searched
729    //
730    // Returns: Boolean indicating if the Set is locked or not
731    access(all) view fun isSetLocked(setId: UInt32): Bool? {
732        // Don't force a revert if the setId is invalid
733        return OlympicPin.sets[setId]?.locked
734    }
735
736    // getNumPiecesInEdition return the number of Pieces that have been 
737    //                        minted from a certain edition.
738    //
739    // Parameters: setId: The id of the Set that is being searched
740    //             pinId: The id of the Pin that is being searched
741    //
742    // Returns: The total number of NFTs 
743    //          that have been minted from an edition
744    access(all) fun getNumPiecesInEdition(setId: UInt32, pinId: UInt32): UInt32? {
745        if let numberMintedPerPin = OlympicPin.sets[setId]?.numberMintedPerPin {
746            let amount = numberMintedPerPin[pinId]
747            return amount
748        } else {
749            return nil
750        }
751    }
752
753    access(all) view fun getContractViews(resourceType: Type?): [Type] {
754        return []
755    }
756
757    access(all) view fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
758        return nil
759    }
760
761    init() {
762        self.currentSeries = 0
763        self.pins = {}
764        self.setDatas = {}
765        self.sets <- {}
766        self.totalSupply = 0
767        self.nextPinId = 1
768        self.nextSetId = 1
769        self.CollectionStoragePath = /storage/PieceCollection
770        self.CollectionPublicPath = /public/PieceCollection
771        self.AdminStoragePath = /storage/OlympicPinAdmin
772
773        // Put a new Collection in storage
774		self.account.storage.save<@Collection>(<-create Collection(), to: self.CollectionStoragePath)
775
776        // Create a public capability for the Collection
777        var cap = self.account.capabilities.storage.issue<&{PieceCollectionPublic}>(self.CollectionStoragePath)
778		self.account.capabilities.publish(cap, at: self.CollectionPublicPath)
779		
780        // Create a Admin resource and save it to storage
781		self.account.storage.save<@Admin>(<-create Admin(), to: self.AdminStoragePath)
782    }
783}