eventix.gno
3.43 Kb ยท 143 lines
1package eventix
2
3import (
4 "chain"
5 "chain/banker"
6 "chain/runtime"
7 "strconv"
8 "time"
9
10 "gno.land/p/demo/tokens/grc20"
11 "gno.land/p/demo/tokens/grc721"
12 "gno.land/p/nt/avl"
13 "gno.land/p/nt/avl/pager"
14 "gno.land/p/nt/ufmt"
15)
16
17type Event struct {
18 name string
19 description string
20 date time.Time
21 maxTickets int
22 price uint64
23 paymentToken any
24 ticketsSold int
25}
26
27var (
28 events = avl.NewTree()
29 eventCounter uint64 = 0
30 tickets = grc721.NewBasicNFT("Event Ticket", "EVTIX")
31)
32
33func CreateEvent(_ realm, name, description, dateStr string, paymentToken any, maxTickets int, price uint64) uint64 {
34 // validate inputs
35 if maxTickets <= 0 {
36 panic("Maximum tickets must be greater than 0")
37 }
38
39 date, err := time.Parse("2006-01-02T15:04:05Z", dateStr)
40 if err != nil {
41 panic("Invalid date format. Use: YYYY-MM-DDThh:mm:ssZ")
42 }
43
44 switch pt := paymentToken.(type) {
45 case string:
46 if pt != "ugnot" {
47 panic("Unsupported native token")
48 }
49 case *grc20.Token:
50 if pt == nil {
51 panic("Invalid GRC20 token")
52 }
53 default:
54 panic("Unsupported payment token type")
55 }
56
57 newID := eventCounter + 1
58 event := Event{
59 name: name,
60 description: description,
61 date: date,
62 maxTickets: maxTickets,
63 price: price,
64 ticketsSold: 0,
65 paymentToken: paymentToken,
66 }
67 events.Set(strconv.Itoa(int(newID)), event)
68 eventCounter = newID
69 chain.Emit("EventCreated", "id", strconv.FormatUint(newID, 10), "name", name)
70 return newID
71}
72
73func BuyTicket(_ realm, eventId uint64) {
74 event, exists := getEvent(eventId)
75 if !exists {
76 panic("Event does not exist")
77 }
78
79 if event.ticketsSold >= event.maxTickets {
80 panic("Event is sold out")
81 }
82
83 buyer := runtime.PreviousRealm().Address()
84
85 switch pt := event.paymentToken.(type) {
86 case string:
87 if pt != "ugnot" {
88 panic("Unsupported native token")
89 }
90 if banker.OriginSend().AmountOf("ugnot") != int64(event.price) {
91 panic(ufmt.Sprintf("Invalid payment amount: needs to be %dugnot", event.price))
92 }
93 case *grc20.Token:
94 if err := pt.CallerTeller().Transfer(runtime.CurrentRealm().Address(), int64(event.price)); err != nil {
95 panic("GRC20 transfer error: " + err.Error())
96 }
97 default:
98 panic("Unsupported payment token type")
99 }
100
101 // Mint NFT ticket
102 tokenId := grc721.TokenID(ufmt.Sprintf("event_%d_ticket_%d", eventId, event.ticketsSold+1))
103 tickets.Mint(buyer, tokenId)
104
105 event.ticketsSold++
106 events.Set(strconv.Itoa(int(eventId)), event)
107}
108
109func Render(path string) string {
110 output := "# Event Ticketing System\n\n"
111
112 pg := pager.NewPager(events, 10, true).MustGetPageByPath(path)
113
114 for _, item := range pg.Items {
115 id, _ := strconv.ParseUint(item.Key, 10, 64)
116 event := item.Value.(Event)
117
118 output += ufmt.Sprintf("## Event #%d: %s\n", id, event.name)
119 output += ufmt.Sprintf("Description: %s\n", event.description)
120 output += ufmt.Sprintf("Date: %s\n", event.date.Format(time.RFC3339))
121 output += ufmt.Sprintf("Tickets: %d/%d\n", event.ticketsSold, event.maxTickets)
122 output += ufmt.Sprintf("Price: %d %v\n\n", event.price, event.paymentToken)
123
124 if event.ticketsSold < event.maxTickets {
125 output += ufmt.Sprintf("[Buy Ticket](/r/jjoptimist/eventix/BuyTicket?eventId=%d)\n", id)
126 } else {
127 output += "**SOLD OUT**\n"
128 }
129 output += "---\n\n"
130 }
131
132 output += pg.Picker(path)
133
134 return output
135}
136
137func getEvent(eventId uint64) (Event, bool) {
138 value, exists := events.Get(strconv.Itoa(int(eventId)))
139 if !exists {
140 return Event{}, false
141 }
142 return value.(Event), true
143}