Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}