Smart Contract

TixologiTickets

A.09e8665388e90671.TixologiTickets

Deployed

3d ago
Feb 25, 2026, 12:20:01 AM UTC

Dependents

5 imports
1/*
2    Description: Central Smart Contract for Tixologi Tickets
3
4    This smart contract contains the core functionality for 
5    Tixologi Tickets, created by Tixologi
6
7    The contract manages the data associated with all the ticket types and events
8    that are used as templates for the Tickets NFTs
9
10    When a new ticket type wants to be added to the records, an Admin creates
11    a new ticket type struct that is stored in the smart contract.
12
13    Then an Admin can create new Events. Event consist of a public struct that 
14    contains public information about a event, and a private resource used
15    to mint new tickets based off of ticket types that have been linked to the Event.
16
17    The admin resource has the power to do all of the important actions
18    in the smart contract. When admins want to call functions in a event,
19    they call their borrowEvent function to get a reference 
20    to a event in the contract. Then, they can call functions on the event using that reference.
21
22    In this way, the smart contract and its defined resources interact 
23    with great teamwork.
24    
25    When tickets are minted, they are initialized with a TicketData struct and
26    are returned by the minter.
27
28    The contract also defines a Collection resource. This is an object that 
29    every TixologiTicket owner will store in their account
30    to manage their NFT collection.
31
32    The main TixologiTicket account will also have its own Ticket collections
33    it can use to hold its own tickets that have not yet been sent to a user.
34
35    Note: All state changing functions will panic if an invalid argument is
36    provided or one of its pre-conditions or post conditions aren't met.
37    Functions that don't modify state will simply return 0 or nil 
38    and those cases need to be handled by the caller.
39
40*/
41
42import NonFungibleToken from 0x1d7e57aa55817448
43import MetadataViews from 0x1d7e57aa55817448
44import ViewResolver   from 0x1d7e57aa55817448
45
46access(all) contract TixologiTickets: NonFungibleToken {
47
48    //access(all) entitlement NFTMinter
49    // -----------------------------------------------------------------------
50    // TixologiTickets deployment variables
51    // -----------------------------------------------------------------------
52    // The network the contract is deployed on
53    //access(all) view fun Network() : String { return ${NETWORK} }
54
55    // -----------------------------------------------------------------------
56    // Tixologi tickets contract Events
57    // -----------------------------------------------------------------------
58
59    // Emitted when the Tixologi tickets contract is created
60    access(all) event ContractInitialized()
61
62    // Emitted when a new Event struct is created
63    access(all) event EventCreated(eventID: UInt32)
64    // Emitted when a new TicketType struct is created
65    access(all) event TicketTypeCreated(eventID: UInt32, ticketTypeID: UInt32, name: String)
66     // Emitted when a new TicketType is added to an Event
67    access(all) event TicketTypeAddedToEvent(eventID: UInt32, ticketTypeID: UInt32)
68     // Emitted when a TicketType is retired from a Event and cannot be used to mint
69    access(all) event TicketTypeRetiredFromEvent(eventID: UInt32, ticketTypeID: UInt32, numTickets: UInt32)
70     // Emitted when a Event is locked, meaning TicketTypes cannot be added
71    access(all) event EventLocked(eventID: UInt32)
72    // Emitted when an Event is closed, meaning Tickets cannot be minted
73    access(all) event EventClosed(eventID: UInt32)
74     // Emitted when a TicketType is sold, meaning Tickets cannot be minted
75    access(all) event TicketTypeSold(eventID: UInt32, ticketTypeID: UInt32)
76    // Emitted when a Ticket is minted from an TicketType
77    access(all) event TicketMinted(ticketID: UInt64, eventID: UInt32, ticketTypeID: UInt32 , serialNumber: UInt32)
78
79    // Events for Collection-related actions
80    //
81    // Emitted when a Ticket is withdrawn from a Collection
82    access(all) event Withdraw(id: UInt64, from: Address?)
83    // Emitted when a Ticket is deposited into a Collection
84    access(all) event Deposit(id: UInt64, to: Address?)
85      // Emitted when a Ticket is transferred from a Collection
86    access(all) event Transfer(id: UInt64, from: Address?, to: Address?)
87
88    // Emitted when a Ticket is destroyed
89    access(all) event TicketDestroyed(id: UInt64)
90
91
92    // Variable size dictionary of TicketType structs
93    access(self) var ticketTypeDatas: {UInt32: TicketType}
94
95    // Variable size dictionary of Event structs
96    access(self) var eventDatas: {UInt32: EventData}
97
98    // Variable size dictionary of TicketType resources
99    access(self) var events: @{UInt32: Event}
100
101    // The ID that is used to create Events. 
102    // Every time a Event is created, eventID is assigned 
103    // to the new Event's ID and then is incremented by 1.
104    access(all) var totalEvents: UInt32
105
106    // The ID that is used to create TicketTypes. Every time a TicketTypes is created
107    // ticketTypeID is assigned to the new ticketType's ID and then is incremented by 1.
108    access(all) var totalTicketTypes: UInt32
109
110    // The total number of Tickets NFTs that have been created
111    // Because NFTs can be destroyed, it doesn't necessarily mean that this
112    // reflects the total number of NFTs in existence, just the number that
113    // have been minted to date. Also used as global ticket IDs for minting.
114    access(all) var totalSupply: UInt64
115
116    // -----------------------------------------------------------------------
117    // Tixologi contract-level Composite Type definitions
118    // -----------------------------------------------------------------------
119    // These are just *definitions* for Types that this contract
120    // and other accounts can use. These definitions do not contain
121    // actual stored values, but an instance (or object) of one of these Types
122    // can be created by this contract that contains stored values.
123    // -----------------------------------------------------------------------
124
125    // Event is a Struct that holds metadata associated 
126    // with specific information : like name, venue, event_type,
127    // Ray Allen hit the 3 to tie the Heat and Spurs in the 2013 finals game 6
128    // or when Lance Stephenson blew in the ear of Lebron James.
129    //
130    // Tickets NFTs will all reference a single Event and a single TicketType.
131    // The Tickets are publicly accessible, so anyone can
132    // read the metadata associated with a specific Ticket ID
133    //
134    access(all) struct EventData {
135
136        // The unique ID for the Event
137        access(all) let eventID: UInt32
138        access(all) let name : String
139        access(all) let startTime : String?
140        access(all) let venue : String?
141        access(all) let eventType : String?
142        access(all) let timeZone : Int?
143        access(all) let gateTime : String?
144        access(all) let image : String?
145        access(all) let description : String?
146
147        init(
148            eventID: UInt32,
149            name: String,
150            startTime : String?,
151            venue: String?,
152            eventType: String?,
153            timeZone: Int?,
154            gateTime : String?,
155            image: String?,
156            description: String?
157            ) {
158            pre {
159                eventID != 0: "Event ID cannot be 0"
160            }
161            self.eventID = eventID
162            self.name = name
163            self.venue = venue
164            self.startTime = startTime
165            self.eventType = eventType
166            self.timeZone = timeZone
167            self.gateTime = gateTime
168            self.image = image
169            self.description = description
170        }
171    }
172
173    // A TicketType contains details for Ticket Categories
174    // that make up a related group of collectibles.
175    // TicketTypeData is a struct that is stored in a field of the contract.
176    // Anyone can query the constant information
177    // about a set by calling various getters located 
178    // at the end of the contract. Only the admin has the ability 
179    // to modify any data in the private Set resource.
180    //
181    access(all) struct TicketType {
182
183        // Unique ID for the TicketType
184        access(all) let ticketTypeID: UInt32
185
186        // EventID to which the ticket belongs
187        access(all) let eventID: UInt32
188
189        // Name of the TicketType
190        // ex. VIP, GA, etc.
191        access(all) let name: String
192
193        // Initial price of the ticket type in primary market
194        access(all) let initialPrice : UFix64
195
196        // Maximum number of tickets that can exist with this ticket type
197        access(all) let totalAmount : UInt32
198
199        // Image for this TicketType
200        access(all) let image : String?
201
202        // Description of the ticket type
203        access(all) let description : String?
204
205        init(
206            ticketTypeID: UInt32,
207            eventID: UInt32,
208            name: String,
209            initialPrice : UFix64,
210            totalAmount : UInt32,
211            image : String?,
212            description : String?
213            ) {
214
215            pre {
216                ticketTypeID != 0: "TicketTypeID cannot be 0"
217                eventID != 0: "EventID cannot be 0"
218            }
219            self.ticketTypeID = ticketTypeID
220            self.eventID = eventID
221            self.name = name
222            self.initialPrice = initialPrice
223            self.totalAmount = totalAmount
224            self.image = image
225            self.description = description
226        }
227    }
228
229    // Event is a resource type that contains the functions to add and remove
230    // TicketTypes from an event and mint Tickets.
231    //
232    // It is stored in a private field in the contract so that
233    // the admin resource can call its methods.
234    //
235    // The admin can add TicketTypes to a Event so that the event can mint Tickets
236    // that reference that ticket type data.
237    // The Tickets that are minted by a Event will be listed as belonging to
238    // the Event that minted it, as well as the TicketType it references.
239    // 
240    // Admin can also retire TicketTypes from the Events, meaning that the retired
241    // TicketType can no longer have Tickets minted from it.
242    //
243    // If the admin locks the Event, no more Plays can be added to it, but 
244    // Tickets can still be minted.
245    //
246    // If an Event is closed, no more tickets can be minted.
247    //
248    // If retireAll() and lock() are called back-to-back, 
249    // the Set is closed off forever and nothing more can be done with it.
250    access(all) resource Event {
251
252        // Unique ID for the Event
253        access(all) let eventID: UInt32
254
255        // Array of TicketTypes that are a part of this Event.
256        // When a TicketType is added to the Event, its ID gets appended here.
257        // The ID does not get removed from this array when a TicketType is retired.
258        access(contract) var ticketTypes: [UInt32]
259
260        // Map of TicketType IDs that Indicates if a TicketType in this Event can be minted.
261        // When a TicketType is added to a Event, it is mapped to false (not retired).
262        // When a TicketType is retired, this is set to true and cannot be changed.
263        access(contract) var retired: {UInt32: Bool}
264
265        // Indicates if the Event is currently locked.
266        // When a Event is created, it is unlocked 
267        // and TicketTypes are allowed to be added to it.
268        // When a event is locked, TicketTypes cannot be added.
269        // A Event can never be changed from locked to unlocked,
270        // the decision to lock a Event it is final.
271        // If a Event is locked, TicketTypes cannot be added, but
272        // Tickets can still be minted from TicketTypes
273        // that exist in the Event.
274        access(all) var locked: Bool
275
276        // Mapping of TicketTypes IDs that indicates the number of Tickets 
277        // that have been minted for specific TicketTypes in this Event.
278        // When a Ticket is minted, this value is stored in the Ticket to
279        // show its place in the Event, eg. 13 of 60.
280        access(contract) var numberMintedPerTicketType: {UInt32: UInt32}
281
282        init( 
283            eventID: UInt32,
284            name: String,
285            startTime : String?,
286            venue: String?,
287            eventType: String?,
288            timeZone: Int?,
289            gateTime : String?,
290            image: String?,
291            description: String?
292            ) {
293            self.eventID = eventID
294            self.ticketTypes = []
295            self.retired = {}
296            self.locked = false
297            self.numberMintedPerTicketType = {}
298
299            // Create a new EventData for this Event and store it in contract storage
300            TixologiTickets.eventDatas[self.eventID] = EventData(eventID: eventID, name: name, startTime : startTime, venue: venue, eventType: eventType, timeZone: timeZone, gateTime : gateTime, image: image, description: description)
301        }
302
303        // addTicketType adds a ticketType to the event
304        //
305        // Parameters: ticketTypeID: The ID of the ticketType that is being added
306        //
307        // Pre-Conditions:
308        // The TicketType needs to be an existing TicketType
309        // The Event needs to be not locked
310        // The TicketType can't have already been added to the Event
311        //
312        access(all) fun addTicketType(ticketTypeID: UInt32) {
313            pre {
314                TixologiTickets.ticketTypeDatas[ticketTypeID] != nil: "Cannot add the TicketType to Event: TicketType doesn't exist."
315                !self.locked: "Cannot add the ticketType to the Event after the set has been locked."
316                self.numberMintedPerTicketType[ticketTypeID] == nil: "The TicketType has already beed added to the event."
317            }
318
319            // Add the TicketType to the array of TicketTypes
320            self.ticketTypes.append(ticketTypeID)
321
322            // Open the TicketType up for minting
323            self.retired[ticketTypeID] = false
324
325            // Initialize the Tickets count to zero
326            self.numberMintedPerTicketType[ticketTypeID] = 0
327
328            emit TicketTypeAddedToEvent(eventID: self.eventID, ticketTypeID: ticketTypeID)
329        }
330
331        // addTicketTypes adds multiple TicketTypes to the Event
332        //
333        // Parameters: ticketTypesIDs: The IDs of the TicketTypes that are being added
334        //                      as an array
335        //
336        access(all) fun addTicketTypes(ticketTypeIDs: [UInt32]) {
337            for ticketType in ticketTypeIDs {
338                self.addTicketType(ticketTypeID: ticketType)
339            }
340        }
341
342        // retireTicketType retires a TicketType from the Evebt so that it can't mint new Tickets
343        //
344        // Parameters: ticketTypeID: The ID of the TicketType that is being retired
345        //
346        // Pre-Conditions:
347        // The TicketType is part of the Event and not retired (available for minting).
348        // 
349        access(all) fun retireTicketType(ticketTypeID: UInt32) {
350            pre {
351                self.retired[ticketTypeID] != nil: "Cannot retire the TicketType: TicketType doesn't exist in this event!"
352            }
353
354            if !self.retired[ticketTypeID]! {
355                self.retired[ticketTypeID] = true
356
357                emit TicketTypeRetiredFromEvent(eventID: self.eventID, ticketTypeID: ticketTypeID, numTickets: self.numberMintedPerTicketType[ticketTypeID]!)
358            }
359        }
360
361        // retireAll retires all the ticketTypes in the Event
362        // Afterwards, none of the retired TicketTypes will be able to mint new Tickets
363        //
364        access(all) fun retireAll() {
365            for ticketType in self.ticketTypes {
366                self.retireTicketType(ticketTypeID: ticketType)
367            }
368        }
369
370        // mintTicket mints a new Ticket and returns the newly minted Ticket
371        // 
372        // Parameters: ticketTypeID: The ID of the TicketType that the Ticket references
373        //
374        // Pre-Conditions:
375        // The TicketType must exist in the Event and be allowed to mint new Tickets
376        //
377        // Returns: The NFT that was minted
378        // 
379        access(all) fun mintTicket(ticketTypeID: UInt32, metadata: String?): @NFT {
380            pre {
381                self.retired[ticketTypeID] != nil: "Cannot mint the ticket: This TicketType doesn't exist."
382                !self.retired[ticketTypeID]!: "Cannot mint the ticket from this ticketType: This ticketType has been retired."
383            }
384
385            // Gets the number of Tickets that have been minted for this TicketType
386            // to use as this Ticket's serial number
387            let numInTicketType = self.numberMintedPerTicketType[ticketTypeID]!
388
389            // Mint the new ticket
390            let newTicket: @NFT <- create NFT(serialNumber: numInTicketType + UInt32(1),
391                                              ticketTypeID: ticketTypeID,
392                                              eventID: self.eventID,
393                                              metadata: metadata)
394
395            // Increment the count of Tickets minted for this TicketType
396            self.numberMintedPerTicketType[ticketTypeID] = numInTicketType + UInt32(1)
397
398            return <-newTicket
399        }
400
401        // batchMintTicket mints an arbitrary quantity of Tickets 
402        // and returns them as a Collection
403        //
404        // Parameters: ticketTypeID: the ID of the ticketType that the Tickets are minted for
405        //             quantity: The quantity of Tickets to be minted
406        //
407        // Returns: Collection object that contains all the Tickets that were minted
408        //
409        access(all) fun batchMintTicket(ticketTypeID: UInt32, metadatas:[String?], quantity: UInt64): @Collection {
410            let newCollection <- create Collection()
411
412            var i: UInt64 = 0
413            while i < quantity {
414                newCollection.deposit(token: <-self.mintTicket(ticketTypeID: ticketTypeID, metadata: metadatas[i]))
415                i = i + 1
416            }
417
418            return <-newCollection
419        }
420
421        access(all) fun getTicketTypes(): [UInt32] {
422            return self.ticketTypes
423        }
424
425        access(all) fun getRetired(): {UInt32: Bool} {
426            return self.retired
427        }
428
429        access(all) fun getNumMintedPerTicketType(): {UInt32: UInt32} {
430            return self.numberMintedPerTicketType
431        }
432    }
433
434    // Struct that contains all of the important data about a event
435    // Can be easily queried by instantiating the `QueryEventData` object
436    // with the desired event ID
437    // let eventData = TixologiTickets.QueryEventData(eventID: 12)
438    //
439    access(all) struct QueryEventData {
440        access(all) let eventID: UInt32
441        access(all) let name: String
442        access(self) var ticketTypes: [UInt32]
443        access(self) var retired: {UInt32: Bool}
444        access(all) var locked: Bool
445        access(self) var numberMintedPerTicketType: {UInt32: UInt32}
446
447        init(eventID: UInt32) {
448            pre {
449                TixologiTickets.events[eventID] != nil: "The event with the provided ID does not exist"
450            }
451
452
453            let tixoEvent = (&TixologiTickets.events[eventID] as &TixologiTickets.Event?)!
454            let eventData = TixologiTickets.eventDatas[eventID]!
455
456            self.eventID = eventID
457            self.name = eventData.name
458            self.ticketTypes = tixoEvent.getTicketTypes()
459            self.retired = tixoEvent.getRetired()
460            self.locked = tixoEvent.locked
461            self.numberMintedPerTicketType = tixoEvent.getNumMintedPerTicketType()
462        }
463
464        access(all) fun getTicketTypes(): [UInt32] {
465            return self.ticketTypes
466        }
467
468        access(all) fun getRetired(): {UInt32: Bool} {
469            return self.retired
470        }
471
472        access(all) fun getNumberMintedPerTicketType(): {UInt32: UInt32} {
473            return self.numberMintedPerTicketType
474        }
475    }
476
477    access(all) struct TicketData {
478
479        // The ID of the Event that the Ticket comes from
480        access(all) let eventID: UInt32
481
482        // The ID of the TicketType that the Ticket references
483        access(all) let ticketTypeID: UInt32
484
485        // The place in the edition that this Ticket was minted
486        // Otherwise know as the serial number
487        access(all) let serialNumber: UInt32
488
489        // Metadata regarding the ticket itself
490        // e.g : seat, row, etc.
491        access(all) let metadata : String?
492
493        init(eventID: UInt32, ticketTypeID: UInt32, serialNumber: UInt32, metadata: String?) {
494            self.eventID = eventID
495            self.ticketTypeID = ticketTypeID
496            self.serialNumber = serialNumber
497            self.metadata = metadata
498        }
499
500    }
501
502    // This is an implementation of a custom metadata view for Tixologi Ticket.
503    // This view contains the ticket metadata.
504    //
505    access(all) struct TixologiTicketMetadataView {
506
507        access(all) let eventName: String?
508        access(all) let ticketTypeName: String?
509        access(all) let venue: String?
510        access(all) let startDate: String?
511        access(all) let serialNumber: UInt32
512        access(all) let ticketTypeID: UInt32
513        access(all) let eventID: UInt32
514        access(all) let numTicketsInTicketType: UInt32?
515
516        init(
517            eventName: String?,
518            ticketTypeName: String?,
519            venue: String?,
520            startDate: String?,
521            serialNumber: UInt32,
522            ticketTypeID: UInt32,
523            eventID: UInt32,
524            numTicketsInTicketType: UInt32?
525        ) {
526            self.eventName = eventName
527            self.ticketTypeName = ticketTypeName
528            self.venue = venue
529            self.startDate = startDate
530            self.serialNumber = serialNumber
531            self.ticketTypeID = ticketTypeID
532            self.eventID = eventID
533            self.numTicketsInTicketType = numTicketsInTicketType
534        }
535    }
536
537    // The resource that represents the Ticket NFTs
538    //
539    access(all) resource NFT: NonFungibleToken.NFT {
540
541        // Global unique ticket ID
542        access(all) let id: UInt64
543        
544        // Struct of Ticket metadata
545        access(all) let data: TicketData
546
547        init(serialNumber: UInt32, ticketTypeID: UInt32, eventID: UInt32, metadata: String?) {
548            // Increment the global Ticket IDs
549            TixologiTickets.totalSupply = TixologiTickets.totalSupply + UInt64(1)
550
551            self.id = TixologiTickets.totalSupply
552
553            // Set the metadata struct
554            self.data = TicketData(eventID: eventID, ticketTypeID: ticketTypeID, serialNumber: serialNumber, metadata: metadata)
555
556            emit TicketMinted(ticketID: self.id,  eventID: self.data.eventID, ticketTypeID: self.data.ticketTypeID, serialNumber: self.data.serialNumber)
557        }
558
559        access(all) fun name(): String {
560            let ticketTypeName: String = TixologiTickets.getTicketTypeName(ticketTypeID: self.data.ticketTypeID) ?? ""
561            let eventName: String = TixologiTickets.getEventName(eventID: self.data.eventID) ?? ""
562            return eventName
563                .concat(" ")
564                .concat(ticketTypeName)
565        }
566
567         access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
568            return <- TixologiTickets.createEmptyCollection(nftType: Type<@TixologiTickets.NFT>())
569        }
570
571        access(all) fun description(): String {
572            let eventName: String = TixologiTickets.getEventName(eventID: self.data.eventID) ?? ""
573            let ticketTypeName: String = TixologiTickets.getTicketTypeName(ticketTypeID: self.data.ticketTypeID) ?? ""
574            let serialNumber: String = self.data.serialNumber.toString()
575            return " A ticketType "
576                .concat(ticketTypeName)
577                .concat("From event ")
578                .concat(eventName)
579                .concat(" ticket with serial number ")
580                .concat(serialNumber)
581        }
582
583        access(all) view fun getViews(): [Type] {
584            return [
585                Type<MetadataViews.Display>(),
586                Type<TixologiTicketMetadataView>(),
587                Type<MetadataViews.Royalties>(),
588                Type<MetadataViews.Editions>(),
589                Type<MetadataViews.ExternalURL>(),
590                Type<MetadataViews.NFTCollectionData>(),
591                Type<MetadataViews.NFTCollectionDisplay>(),
592                Type<MetadataViews.Serial>(),
593                Type<MetadataViews.Traits>(),
594                Type<MetadataViews.Medias>()
595            ]
596        }
597
598        access(all) fun resolveView(_ view: Type): AnyStruct? {
599            switch view {
600                case Type<MetadataViews.Display>():
601                    return MetadataViews.Display(
602                        name: self.name(),
603                        description: self.description(),
604                        thumbnail: MetadataViews.HTTPFile(url:"https://ipfs.dapperlabs.com/ipfs/Qmbdj1agtbzpPWZ81wCGaDiMKRFaRN3TU6cfztVCu6nh4o")
605                    )
606                case Type<TixologiTicketMetadataView>():
607                    return TixologiTicketMetadataView(
608                        eventName: TixologiTickets.getEventName(eventID: self.data.eventID),
609                        ticketTypeName: TixologiTickets.getTicketTypeName(ticketTypeID: self.data.ticketTypeID),
610                        venue: TixologiTickets.getEventVenue(eventID: self.data.eventID),
611                        startDate: TixologiTickets.getEventStartDate(eventID: self.data.eventID),
612                        serialNumber: self.data.serialNumber,
613                        ticketTypeID: self.data.ticketTypeID,
614                        eventID: self.data.eventID,
615                        numTicketsInTicketType: TixologiTickets.getNumTicketsInTicketType(eventID: self.data.eventID, ticketTypeID: self.data.ticketTypeID)
616                    )
617            }
618
619            return nil
620        }   
621    }
622
623    // Admin is a special authorization resource that 
624    // allows the owner to perform important functions to modify the 
625    // various aspects of the TicketTypes, Events, and Tickets
626    //
627    access(all) resource Admin {
628
629        // createTicketType creates a new TicketType struct 
630        // and stores it in the TicketTypes dictionary in the TixologiTickets smart contract
631        //
632        // Parameters: ticketTypeID : The ticketTypeID
633        //             eventID : The eventID
634        //             name : name of the ticketType, examples {"Vip", "GA", "VIP ONLY MEMBERS"}
635        //             initialPrice : initial price of tickets for the primary market
636        //             image: an URL for an image of that ticket type
637        //             description: a description for the ticketType. example {"ticket type created by manu member of Tixologi for launch party event"}
638        //
639        // Returns: the ID of the new TicketType object
640        //
641        access(all) fun createTicketType(ticketTypeID: UInt32, eventID: UInt32, name: String, initialPrice: UFix64, totalAmount: UInt32, image: String?, description: String?): UInt32 {
642            // Create the new TicketType
643            var newTicketType = TicketType(ticketTypeID: ticketTypeID, eventID: eventID, name: name, initialPrice: initialPrice, totalAmount: totalAmount, image: image, description: description)
644            let newID = newTicketType.ticketTypeID
645
646            // Increment the ID so that it isn't used again
647            TixologiTickets.totalTicketTypes = TixologiTickets.totalTicketTypes + UInt32(1)
648
649            emit TicketTypeCreated(eventID: newTicketType.eventID, ticketTypeID: newTicketType.ticketTypeID, name: newTicketType.name)
650
651            // Store it in the contract storage
652            TixologiTickets.ticketTypeDatas[newID] = newTicketType
653
654            return newID
655        }
656
657        // createEvent creates a new Event resource and stores it
658        // in the event mapping in the TixologiTickets contract
659        //
660        // Parameters: eventID : the id of the event
661        //             name: The name of the Event
662        //             startTime : Start time of the event
663        //             venue : The venue where the event will take place.  example {"Madison Square Garden"}
664        //             eventType : The type of the event  example {Sports}
665        //             timeZone : timeZone of the event
666        //             gateTime : the time the gates will open
667        //             image : an image for the event (URL)
668        //             description : description of the event. example {NBA finals between Lakers and Golden States}
669        //
670        // Returns: The ID of the created event
671        access(all) fun createEvent(eventID: UInt32, name: String, startTime: String, venue: String, eventType: String, timeZone : Int, gateTime: String, image: String?, description: String?): UInt32 {
672
673            // Create the new Event
674            var newEvent <- create Event(eventID: eventID, name: name, startTime: startTime, venue: venue, eventType: eventType, timeZone: timeZone, gateTime: gateTime, image: image, description: description)
675
676            // Increment the eventID 
677            TixologiTickets.totalEvents = TixologiTickets.totalEvents + UInt32(1)
678
679            let newID = newEvent.eventID
680
681            emit EventCreated(eventID: newEvent.eventID)
682            // Store it in the events mapping field
683            TixologiTickets.events[newID] <-! newEvent
684
685
686            return newID
687        }
688
689        // borrowEvent returns a reference to a event in the TixologiTickets
690        // contract so that the admin can call methods on it
691        //
692        // Parameters: eventID: The ID of the Event that you want to
693        // get a reference to
694        //
695        // Returns: A reference to the Event with all of the fields
696        // and methods exposed
697        //
698        access(all) fun borrowEvent(eventID: UInt32): &Event {
699            pre {
700                TixologiTickets.events[eventID] != nil: "Cannot borrow Event: Event doesn't exist"
701            }
702            
703            // Get a reference to the event and return it
704            // use `&` to indicate the reference to the object and type
705            return (&TixologiTickets.events[eventID])!
706        }
707
708        // createNewAdmin creates a new Admin resource
709        //
710        access(all) fun createNewAdmin(): @Admin {
711            return <-create Admin()
712        }
713    }
714
715    // This is the interface that users can cast their Ticket Collection as
716    // to allow others to deposit Tickets into their Collection. It also allows for reading
717    // the IDs of Tickets in the Collection.
718    access(all) resource interface TixologiTicketsCollectionPublic {
719        access(all) fun deposit(token: @{NonFungibleToken.NFT})
720        access(all) fun batchDeposit(tokens: @{NonFungibleToken.Collection})
721        access(all) fun getIDs(): [UInt64]
722        access(all) fun borrowTicket(id: UInt64): &TixologiTickets.NFT {
723            // If the result isn't nil, the id of the returned reference
724            // should be the same as the argument to the function
725            post {
726                (result == nil) || (result.id == id): 
727                    "Cannot borrow Ticket reference: The ID of the returned reference is incorrect"
728            }
729        }
730    }
731
732    // Collection is a resource that every user who owns NFTs 
733    // will store in their account to manage their NFTS
734    //
735    access(all) resource Collection: TixologiTicketsCollectionPublic, NonFungibleToken.Collection { 
736        // Dictionary of Moment conforming tokens
737        // NFT is a resource type with a UInt64 ID field
738        access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
739
740        init() {
741            self.ownedNFTs <- {}
742        }
743
744       access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
745            let supportedTypes: {Type: Bool} = {}
746            supportedTypes[Type<@TixologiTickets.NFT>()] = true
747            return supportedTypes
748        }
749
750        // Return whether or not the given type is accepted by the collection
751        // A collection that can accept any type should just return true by default
752        access(all) view fun isSupportedNFTType(type: Type): Bool {
753            if type == Type<@TixologiTickets.NFT>() {
754                return true
755            }
756            return false
757        }
758
759        // Return the amount of NFTs stored in the collection
760        access(all) view fun getLength(): Int {
761            return self.ownedNFTs.keys.length
762        }
763
764        // Create an empty Collection for TixologiTickets NFTs and return it to the caller
765        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
766            return <- TixologiTickets.createEmptyCollection(nftType: Type<@TixologiTickets.NFT>())
767        }
768
769
770        // withdraw removes an Ticket from the Collection and moves it to the caller
771        //
772        // Parameters: withdrawID: The ID of the NFT 
773        // that is to be removed from the Collection
774        //
775        // returns: @NonFungibleToken.NFT the token that was withdrawn
776        access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
777            // Borrow nft and check if locked
778            let nft = self.borrowNFT(withdrawID)
779                ?? panic("Cannot borrow: empty reference")
780            // Remove the nft from the Collection
781            let token <- self.ownedNFTs.remove(key: withdrawID) 
782                ?? panic("Cannot withdraw: Ticket does not exist in the collection")
783
784            emit Withdraw(id: token.id, from: self.owner?.address)
785            
786            // Return the withdrawn token
787            return <-token
788        }
789
790        // batchWithdraw withdraws multiple tokens and returns them as a Collection
791        //
792        // Parameters: ids: An array of IDs to withdraw
793        //
794        // Returns: @NonFungibleToken.Collection: A collection that contains
795        //                                        the withdrawn moments
796        //
797        access(NonFungibleToken.Withdraw) fun batchWithdraw(ids: [UInt64]): @{NonFungibleToken.Collection} {
798            // Create a new empty Collection
799            var batchCollection <- create Collection()
800            
801            // Iterate through the ids and withdraw them from the Collection
802            for id in ids {
803                batchCollection.deposit(token: <-self.withdraw(withdrawID: id))
804            }
805            
806            // Return the withdrawn tokens
807            return <-batchCollection
808        }
809
810        // deposit takes a Ticket and adds it to the Collections dictionary
811        //
812        // Paramters: token: the NFT to be deposited in the collection
813        //
814       access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
815            
816            // Cast the deposited token as a TixologiTickets NFT to make sure
817            // it is the correct type
818            let token <- token as! @TixologiTickets.NFT
819
820            // Get the token's ID
821            let id = token.id
822
823            // Add the new token to the dictionary
824            let oldToken <- self.ownedNFTs[id] <- token
825
826            // Only emit a deposit event if the Collection 
827            // is in an account's storage
828            if self.owner?.address != nil {
829                emit Deposit(id: id, to: self.owner?.address)
830            }
831
832            // Destroy the empty old token that was "removed"
833            destroy oldToken
834        }
835
836        // batchDeposit takes a Collection object as an argument
837        // and deposits each contained NFT into this Collection
838        access(all) fun batchDeposit(tokens: @{NonFungibleToken.Collection}) {
839
840            // Get an array of the IDs to be deposited
841            let keys = tokens.getIDs()
842
843            // Iterate through the keys in the collection and deposit each one
844            for key in keys {
845                self.deposit(token: <-tokens.withdraw(withdrawID: key))
846            }
847
848            // Destroy the empty Collection
849            destroy tokens
850        }
851
852        // getIDs returns an array of the IDs that are in the Collection
853         access(all) view fun getIDs(): [UInt64] {
854            return self.ownedNFTs.keys
855        }
856
857        // borrowNFT Returns a borrowed reference to a Ticket in the Collection
858        // so that the caller can read its ID
859        //
860        // Parameters: id: The ID of the NFT to get the reference for
861        //
862        // Returns: A reference to the NFT
863        //
864        // Note: This only allows the caller to read the ID of the NFT,
865        // not any tixologi ticket specific data. Please use borrowTicket to 
866        // read Ticket data.
867        //
868      access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
869            return &self.ownedNFTs[id]
870        }
871
872        // borrowTicket returns a borrowed reference to a Ticket
873        // so that the caller can read data and call methods from it.
874        // They can use this to read its eventID, ticketTypeID, serialNumber,
875        // or any of the eventData or ticketTypeData associated with it by
876        // getting the eventID or ticketTypeID and reading those fields from
877        // the smart contract.
878        //
879        // Parameters: id: The ID of the NFT to get the reference for
880        //
881        // Returns: A reference to the NFT
882        access(all) fun borrowTicket(id: UInt64): &TixologiTickets.NFT {
883            return self.borrowNFT(id) as! &TixologiTickets.NFT
884        }
885
886       access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
887            if let nft = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? {
888                return nft as &{ViewResolver.Resolver}
889            }
890            return nil
891        }
892
893        // If a transaction destroys the Collection object,
894        // All the NFTs contained within are also destroyed!
895        //
896    }
897
898    // -----------------------------------------------------------------------
899    // TixologiTickets contract-level function definitions
900    // -----------------------------------------------------------------------
901
902    // createEmptyCollection creates a new, empty Collection object so that
903    // a user can store it in their account storage.
904    // Once they have a Collection in their storage, they are able to receive
905    // Tickets in transactions.
906    //
907     access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
908        if nftType != Type<@TixologiTickets.NFT>() {
909            panic("NFT type is not supported")
910        }
911        return <-create TixologiTickets.Collection()
912    }
913
914    // getAllTicketTypes returns all the TicketTypes in tixologi tickets
915    //
916    // Returns: An array of all the TicketTypes that have been created
917    access(all) fun getAllTicketTypes(): [TixologiTickets.TicketType] {
918        return TixologiTickets.ticketTypeDatas.values
919    }
920
921    access(all) fun getTicketTypeName(ticketTypeID: UInt32) : String? {
922        if let ticketType = TixologiTickets.ticketTypeDatas[ticketTypeID] {
923            return ticketType.name
924        } else {
925            return nil
926        }
927    }
928
929    access(all) fun getTicketTypeInitialPrice(ticketTypeID: UInt32) : UFix64? {
930        if let ticketType = TixologiTickets.ticketTypeDatas[ticketTypeID] {
931            return ticketType.initialPrice
932        } else {
933            return nil
934        }
935    }
936
937    access(all) fun getTicketTypeTotalAmount(ticketTypeID: UInt32) : UInt32? {
938        if let ticketType = TixologiTickets.ticketTypeDatas[ticketTypeID] {
939            return ticketType.totalAmount
940        } else {
941            return nil
942        }
943    }
944
945    access(all) fun getTicketTypeImage(ticketTypeID: UInt32) : String? {
946        if let ticketType = TixologiTickets.ticketTypeDatas[ticketTypeID] {
947            return ticketType.image
948        } else {
949            return nil
950        }
951    }
952
953    access(all) fun getTicketTypeDescription(ticketTypeID: UInt32) : String? {
954        if let ticketType = TixologiTickets.ticketTypeDatas[ticketTypeID] {
955            return ticketType.description
956        } else {
957            return nil
958        }
959    }
960
961    // getEventData returns the data that the specified Event
962    //            is associated with.
963    // 
964    // Parameters: eventID: The id of the Event that is being searched
965    //
966    // Returns: The QueryEventData struct that has all the important information about the Event
967    access(all) fun getEventData(eventID: UInt32): QueryEventData? {
968        if TixologiTickets.events[eventID] == nil {
969            return nil
970        } else {
971            return QueryEventData(eventID: eventID)
972        }
973    }
974
975    // getEventName returns the name that the specified Event
976    //            is associated with.
977    // 
978    // Parameters: eventID: The id of the Event that is being searched
979    //
980    // Returns: The name of the Event
981    access(all) fun getEventName(eventID: UInt32): String? {
982        // Don't force a revert if the eventID is invalid
983        return TixologiTickets.eventDatas[eventID]?.name
984    }
985
986
987    // getEventVenue returns the venue that the specified Event
988    //            is associated with.
989    // 
990    // Parameters: eventID: The id of the Event that is being searched
991    //
992    // Returns: The venue of the Event
993    access(all) fun getEventVenue(eventID: UInt32): String? {
994        // Don't force a revert if the eventID is invalid
995        return TixologiTickets.eventDatas[eventID]?.venue
996    }
997
998    // getEventStartDate returns the StartDate that the specified Event
999    //            is associated with.
1000    // 
1001    // Parameters: eventID: The id of the Event that is being searched
1002    //
1003    // Returns: The StartDate of the Event
1004    access(all) fun getEventStartDate(eventID: UInt32): String? {
1005        // Don't force a revert if the eventID is invalid
1006        return TixologiTickets.eventDatas[eventID]?.startTime
1007    }
1008
1009    // getEventIDsByName returns the IDs that the specified Event name
1010    //                 is associated with.
1011    // 
1012    // Parameters: eventName: The name of the Event that is being searched
1013    //
1014    // Returns: An array of the IDs of the Event if it exists, or nil if doesn't
1015    access(all) fun getEventIDsByName(eventName: String): [UInt32]? {
1016        var eventIDs: [UInt32] = []
1017
1018        // Iterate through all the eventDatas and search for the name
1019        for eventData in TixologiTickets.eventDatas.values {
1020            if eventName == eventData.name {
1021                // If the name is found, return the ID
1022                eventIDs.append(eventData.eventID)
1023            }
1024        }
1025
1026        // If the name isn't found, return nil
1027        // Don't force a revert if the eventName is invalid
1028        if eventIDs.length == 0 {
1029            return nil
1030        } else {
1031            return eventIDs
1032        }
1033    }
1034
1035    // getTicketTypesInEvent returns the list of TicketTypeIDs that are in the Event
1036    // 
1037    // Parameters: eventID: The id of the Event that is being searched
1038    //
1039    // Returns: An array of TicketTypeIDs
1040    access(all) fun getTicketTypesInEvent(eventID: UInt32): [UInt32]? {
1041        // Don't force a revert if the eventID is invalid
1042        return TixologiTickets.events[eventID]?.ticketTypes
1043    }
1044
1045    // getNumTicketsInTicketType return the number of Ticket that have been 
1046    //                        minted from a certain TicketType on an event.
1047    //
1048    // Parameters: eventID: The id of the Event that is being searched
1049    //             ticketTypeID: The id of the ticketType that is being searched
1050    //
1051    // Returns: The total number of Tickets 
1052    //          that have been minted from an from a certain TicketType on an event.
1053    access(all) fun getNumTicketsInTicketType(eventID: UInt32, ticketTypeID: UInt32): UInt32? {
1054        if let eventData = self.getEventData(eventID: eventID) {
1055
1056            // Read the numMintedPerTicketType
1057            let amount = eventData.getNumberMintedPerTicketType()[ticketTypeID]
1058
1059            return amount
1060        } else {
1061            // If the set wasn't found return nil
1062            return nil
1063        }
1064    }
1065
1066     //------------------------------------------------------------
1067    // Contract MetadataViews
1068    //------------------------------------------------------------
1069    /// Return the metadata view types available for this contract
1070    ///
1071    access(all) view fun getContractViews(resourceType: Type?): [Type] {
1072        return [
1073            Type<MetadataViews.Display>(),
1074            Type<TixologiTicketMetadataView>()
1075        ]
1076    }
1077
1078    /// Resolve this contract's metadata views
1079    ///
1080    access(all) view fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
1081        return nil
1082    }
1083
1084    // -----------------------------------------------------------------------
1085    // TixologiTickets initialization function
1086    // -----------------------------------------------------------------------
1087    //
1088    init() {
1089        // Initialize contract fields
1090        self.ticketTypeDatas = {}
1091        self.eventDatas = {}
1092        self.events <- {}
1093        self.totalEvents = 0
1094        self.totalTicketTypes = 0
1095        self.totalSupply = 0
1096
1097        // Put a new Collection in storage
1098        self.account.storage.save<@Collection>(<- create Collection(), to: /storage/TixologiTicketsCollection)
1099
1100        let cap = self.account.capabilities.storage.issue<&TixologiTickets.Collection>(/storage/TixologiTicketsCollection)
1101        self.account.capabilities.publish(cap, at: /public/TixologiTicketsCollection)
1102
1103        // Put the Minter in storage
1104        self.account.storage.save<@Admin>(<- create Admin(), to: /storage/TixologiTicketAdmin)
1105
1106        emit ContractInitialized()
1107    }
1108}