eventix.gno
3.54 Kb · 146 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/v0"
13 "gno.land/p/nt/avl/v0/pager"
14 "gno.land/p/nt/ufmt/v0"
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 if !runtime.PreviousRealm().IsUserCall() {
75 panic("only user-call (maketx call) accepted")
76 }
77 event, exists := getEvent(eventId)
78 if !exists {
79 panic("Event does not exist")
80 }
81
82 if event.ticketsSold >= event.maxTickets {
83 panic("Event is sold out")
84 }
85
86 buyer := runtime.PreviousRealm().Address()
87
88 switch pt := event.paymentToken.(type) {
89 case string:
90 if pt != "ugnot" {
91 panic("Unsupported native token")
92 }
93 if banker.OriginSend().AmountOf("ugnot") != int64(event.price) {
94 panic(ufmt.Sprintf("Invalid payment amount: needs to be %dugnot", event.price))
95 }
96 case *grc20.Token:
97 if err := pt.CallerTeller().Transfer(runtime.CurrentRealm().Address(), int64(event.price)); err != nil {
98 panic("GRC20 transfer error: " + err.Error())
99 }
100 default:
101 panic("Unsupported payment token type")
102 }
103
104 // Mint NFT ticket
105 tokenId := grc721.TokenID(ufmt.Sprintf("event_%d_ticket_%d", eventId, event.ticketsSold+1))
106 tickets.Mint(buyer, tokenId)
107
108 event.ticketsSold++
109 events.Set(strconv.Itoa(int(eventId)), event)
110}
111
112func Render(path string) string {
113 output := "# Event Ticketing System\n\n"
114
115 pg := pager.NewPager(events, 10, true).MustGetPageByPath(path)
116
117 for _, item := range pg.Items {
118 id, _ := strconv.ParseUint(item.Key, 10, 64)
119 event := item.Value.(Event)
120
121 output += ufmt.Sprintf("## Event #%d: %s\n", id, event.name)
122 output += ufmt.Sprintf("Description: %s\n", event.description)
123 output += ufmt.Sprintf("Date: %s\n", event.date.Format(time.RFC3339))
124 output += ufmt.Sprintf("Tickets: %d/%d\n", event.ticketsSold, event.maxTickets)
125 output += ufmt.Sprintf("Price: %d %v\n\n", event.price, event.paymentToken)
126
127 if event.ticketsSold < event.maxTickets {
128 output += ufmt.Sprintf("[Buy Ticket](/r/jjoptimist/eventix/BuyTicket?eventId=%d)\n", id)
129 } else {
130 output += "**SOLD OUT**\n"
131 }
132 output += "---\n\n"
133 }
134
135 output += pg.Picker(path)
136
137 return output
138}
139
140func getEvent(eventId uint64) (Event, bool) {
141 value, exists := events.Get(strconv.Itoa(int(eventId)))
142 if !exists {
143 return Event{}, false
144 }
145 return value.(Event), true
146}