Transaction

190A89BF3374B2…653E5DBC61B6

Block 26,386 · index 0 · indexed

Summary

Hash
190A89BF3374B232DACE1550B04E8A62A4DC54F207A762F2CB80653E5DBC61B6
Block
26,386
Size
7328 bytes
Gas used
12,208,336 / 14,650,063
Fee
1000000ugnot
Memo
gnopublish
Status
success

Messages

Arguments · 9

  1. #1reactions
  2. #2README.md
  3. #3# Reaction Board > ⚠️ **Experimental — generated with no human supervision.** This realm was > produced automatically by an MCP-driven agent to exercise the gno MCP server > and tooling, and to generate test content for gno compilers, linters and > formatters. **Not audited. Not for production.** Full context & folder README: > [r/moul/x/daily](https://github.com/moul/gno-contracts/blob/main/r/moul/x/daily/README.md) --- An on-chain emoji reaction board. Each topic collects emoji reactions, and each caller may register at most one reaction per topic (voters are tracked in an AVL set). `Render` lists every topic with its emoji tallies and a total count. **Realm path:** `gno.land/r/REPLACE_ADDR/reactions` ## Example calls ``` # React to a topic (creates the topic on demand). One reaction per caller. React("gno 0.9 launch", "👍") React("gno 0.9 launch", "❤️") # Optionally pre-create an empty topic. CreateTopic("weekend poll") # View the board (all topics), or a single topic. Render("") Render("gno 0.9 launch") ``` A topic renders as a row like `👍 3 ❤️ 5 🎉 1` (sorted by count, then emoji), followed by its total. Reacting twice to the same topic from the same address aborts. <!-- BEGIN GNOCONTRACTS FOOTER (generated by `make readmes`; do not edit below) --> --- Part of **[moul/gno-contracts](https://github.com/moul/gno-contracts)** — moul's versioned gno.land contracts. See the repository for the full catalog, build/test tooling, and usage. **Dependency graph:** ![gno.land/r/moul/x/daily/reactions/v0 dependency graph](https://raw.githubusercontent.com/moul/gno-contracts/main/_assets/gno.land/r/moul/x/daily/reactions/v0/deps.png) > 🧪 **Highly experimental — potentially vibe-coded.** Not audited; may break, change, or be removed at any time. Do not use with anything of value. Full disclaimer: [DISCLAIMER](https://github.com/moul/gno-contracts/blob/main/DISCLAIMER.md). <!-- END GNOCONTRACTS FOOTER -->
  4. #4gnomod.toml
  5. #5module = "gno.land/r/moul/x/daily/reactions/v0" gno = "0.9"
  6. #6reactions.gno
  7. #7// Package reactions is an emoji reaction board. // // Each topic collects emoji reactions. A caller may register at most one // reaction per topic; the emoji they picked increments a per-topic per-emoji // tally. Render lists every topic with its emoji tallies and total count. package reactions import ( "sort" "strconv" "strings" "chain" "chain/runtime/unsafe" "gno.land/p/nt/avl/v0" ) // topic holds the tallies and the set of callers who already reacted. type topic struct { name string tallies *avl.Tree // emoji (string) -> count (int) voters *avl.Tree // caller address (string) -> struct{} total int } // topics maps topic name -> *topic, kept in an avl.Tree for deterministic order. var topics = avl.NewTree() func getOrPanic(name string) *topic { v := topics.Get(name) if v == nil { panic("topic does not exist: " + name) } return v.(*topic) } func newTopic(name string) *topic { t := &topic{ name: name, tallies: avl.NewTree(), voters: avl.NewTree(), } topics.Set(name, t) return t } // CreateTopic registers a new empty topic. It is optional: React auto-creates // a topic if it does not exist yet. Panics if the topic already exists or the // name is empty. func CreateTopic(cur realm, name string) { name = strings.TrimSpace(name) if name == "" { panic("topic name must not be empty") } if topics.Has(name) { panic("topic already exists: " + name) } newTopic(name) chain.Emit("TopicCreated", "topic", name) } // React adds the caller's reaction (an emoji) to a topic. Each caller may react // at most once per topic. The topic is created on demand if missing. Panics on // empty input or when the caller has already reacted to this topic. func React(cur realm, name, emoji string) { name = strings.TrimSpace(name) emoji = strings.TrimSpace(emoji) if name == "" { panic("topic name must not be empty") } if emoji == "" { panic("emoji must not be empty") } caller := unsafe.PreviousRealm().Address().String() var t *topic if v := topics.Get(name); v != nil { t = v.(*topic) } else { t = newTopic(name) } if t.voters.Has(caller) { panic("caller already reacted to this topic: " + name) } t.voters.Set(caller, struct{}{}) count := 0 if v := t.tallies.Get(emoji); v != nil { count = v.(int) } t.tallies.Set(emoji, count+1) t.total++ chain.Emit("Reacted", "topic", name, "emoji", emoji) } // TopicCount returns the number of topics on the board. func TopicCount() int { return topics.Size() } // emojiTally is used to sort a topic's tallies deterministically. type emojiTally struct { emoji string count int } // byCountDesc sorts tallies by count descending, then emoji ascending. type byCountDesc []emojiTally func (s byCountDesc) Len() int { return len(s) } func (s byCountDesc) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s byCountDesc) Less(i, j int) bool { if s[i].count != s[j].count { return s[i].count > s[j].count } return s[i].emoji < s[j].emoji } func (t *topic) sortedTallies() []emojiTally { out := make([]emojiTally, 0, t.tallies.Size()) t.tallies.Iterate("", "", func(key string, value interface{}) bool { out = append(out, emojiTally{emoji: key, count: value.(int)}) return false }) sort.Stable(byCountDesc(out)) return out } // tallyRow renders a topic's tallies as e.g. "👍 3 ❤️ 5 🎉 1". func (t *topic) tallyRow() string { if t.tallies.Size() == 0 { return "_no reactions yet_" } rows := t.sortedTallies() parts := make([]string, 0, len(rows)) for _, r := range rows { parts = append(parts, r.emoji+" "+strconv.Itoa(r.count)) } return strings.Join(parts, " ") } // Render returns a Markdown view of the board. If path is a non-empty topic // name, only that topic is shown; otherwise all topics are listed. func Render(path string) string { path = strings.TrimSpace(strings.Trim(path, "/")) if path != "" { v := topics.Get(path) if v == nil { return "# Reaction Board\n\nTopic not found: `" + path + "`\n" } t := v.(*topic) var sb strings.Builder sb.WriteString("# Reaction Board — " + t.name + "\n\n") sb.WriteString(t.tallyRow() + "\n\n") sb.WriteString("**Total reactions:** " + strconv.Itoa(t.total) + "\n") return sb.String() } var sb strings.Builder sb.WriteString("# Reaction Board\n\n") if topics.Size() == 0 { sb.WriteString("_No topics yet. Call `React(cur, topic, emoji)` to start._\n") return sb.String() } grand := 0 topics.Iterate("", "", func(key string, value interface{}) bool { t := value.(*topic) grand += t.total sb.WriteString("## " + t.name + "\n\n") sb.WriteString(t.tallyRow() + "\n\n") sb.WriteString("Total: " + strconv.Itoa(t.total) + "\n\n") return false }) sb.WriteString("---\n\n") sb.WriteString("**Topics:** " + strconv.Itoa(topics.Size()) + " · **Reactions:** " + strconv.Itoa(grand) + "\n") return sb.String() }
  8. #8/gno.MemPackageType
  9. #9 MPUserProd

Result log

msg:0,success:true,log:,events:[]

← Back to block 26,386