Transaction
4978CEED788081…845A6E521DCA
Block 26,100 · index 0 · indexed
Summary
- Hash
- 4978CEED788081F57809F50581757D2F05FF126218B522569B5B845A6E521DCA
- Block
- 26,100
- Size
- 7874 bytes
- Gas used
- 12,380,828 / 14,857,053
- Fee
- 1000000 ugnot
- Memo
- gnopublish
- Status
- success
Messages
Arguments · 9
- #1quotes
- #2README.md
- #3# Quote of the Moment > ⚠️ **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) --- A community quote board realm for gno.land (sapphire, gno 0.9). Anyone can submit a quote; each entry records the text, the submitter's address, and the block height at submission. The board ships pre-seeded with a handful of Stoic quotes so a fresh deploy renders nicely, and the rendered page rotates a "featured" pick deterministically as the chain advances. - **Realm path:** `gno.land/r/REPLACE_ADDR/quotes` - **State-mutating tx:** `AddQuote(text string)` — a gno 0.9 crossing function. - **View:** `Render(path string)` — Markdown; a blockquote featured pick chosen by `ChainHeight() % len(quotes)`, followed by the full list with author + id. ## Example calls Add a quote (MsgCall dispatches to the crossing function; you do not write `cross(cur)` on the command line — the chain supplies it): ```sh gnokey maketx call \ -pkgpath "gno.land/r/REPLACE_ADDR/quotes" \ -func AddQuote \ -args "Luck is what happens when preparation meets opportunity." \ -gas-fee 1000000ugnot -gas-wanted 2000000 \ -broadcast -chainid sapphire-1 -remote <rpc> <key> ``` View the rendered board in gnoweb: ``` https://gno.land/r/REPLACE_ADDR/quotes ``` Or query the render output directly: ```sh gnokey query "vm/qrender" -data "gno.land/r/REPLACE_ADDR/quotes:" -remote <rpc> ``` <!-- 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. > 🧪 **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 -->
- #4gnomod.toml
- #5module = "gno.land/r/moul/x/daily/quotes/v0" gno = "0.9"
- #6quotes.gno
- #7// Package quotes is a community "Quote of the Moment" board realm. // // Anyone can submit a quote via the exported crossing function AddQuote. // Each quote records its text, the address of the caller who added it, and // the block height at submission time. Render deterministically picks a // "featured" quote by the current block height (a stable index into the // list), shows it as a blockquote, then lists every quote with its author // and id below. package quotes import ( "strconv" "strings" "chain" "chain/runtime" ) // quote is a single board entry. Author is stored as a plain string address // (realm values are ephemeral and must never be persisted). type quote struct { id int text string author string // bech32 address of the submitter, or "" for seeded quotes height int64 // block height at submission (0 for seeded quotes) } // board is the persistent realm state. A plain slice is fine here: the list // only ever grows by append and Render walks it in insertion order, which is // deterministic. var board []quote // init seeds the board so a fresh deploy renders nicely. func init() { seed := []string{ "The unexamined life is not worth living.", "We suffer more often in imagination than in reality.", "Waste no more time arguing about what a good person should be. Be one.", "He who is brave is free.", } for _, s := range seed { board = append(board, quote{ id: len(board), text: s, author: "", // seeded height: 0, }) } } // AddQuote is the exported, state-mutating entry point. It follows the gno 0.9 // interrealm crossing convention: the first parameter is `cur realm`. Callers // invoke it as AddQuote(cross(cur), "...") over a MsgCall. func AddQuote(cur realm, text string) { // Authentication primitive: reject a stale/forged realm value before // deriving any caller identity from it. if !cur.IsCurrent() { panic("spoofed realm: cur is not the live crossing frame") } if err := validate(text); err != nil { panic(err) } author := cur.Previous().Address().String() h := runtime.ChainHeight() board = append(board, quote{ id: len(board), text: text, author: author, height: h, }) chain.Emit("QuoteAdded", "id", strconv.Itoa(len(board)-1), "author", author, "height", strconv.FormatInt(h, 10), ) } // maxQuoteLen caps quote length to keep storage costs bounded and Render tidy. const maxQuoteLen = 280 // validate enforces basic content rules. Returns an error describing the // problem, or nil when the text is acceptable. func validate(text string) error { if len(text) == 0 { return errEmpty } if len(text) > maxQuoteLen { return errTooLong } return nil } // Sentinel errors for validation. Kept as package-level vars so tests can // compare against them directly. var ( errEmpty = quoteError("quote text must not be empty") errTooLong = quoteError("quote text exceeds " + strconv.Itoa(maxQuoteLen) + " characters") ) // quoteError is a trivial string-backed error type (the VM has no host // stdlib errors package convenience beyond this). type quoteError string func (e quoteError) Error() string { return string(e) } // featuredIndex deterministically picks which quote is "of the moment" for a // given block height. Pure function of (height, count) so it is fully // deterministic and easy to test. func featuredIndex(height int64, count int) int { if count <= 0 { return -1 } // Guard against a negative height (should not happen on-chain, but keeps // the modulo well-defined). if height < 0 { height = -height } return int(height % int64(count)) } // Render produces the Markdown page shown in gnoweb. It is NOT a crossing // function (no `cur realm` parameter) — it is a read-only view. func Render(path string) string { count := len(board) if count == 0 { return "# Quote of the Moment\n\n_No quotes yet. Be the first to add one._\n" } h := runtime.ChainHeight() idx := featuredIndex(h, count) featured := board[idx] var b strings.Builder b.WriteString("# Quote of the Moment\n\n") // Featured quote as a blockquote. b.WriteString("> ") b.WriteString(featured.text) b.WriteString("\n>\n> — ") b.WriteString(authorLabel(featured)) b.WriteString(" (#") b.WriteString(strconv.Itoa(featured.id)) b.WriteString(")\n\n") b.WriteString("_Featured pick rotates with block height ") b.WriteString(strconv.FormatInt(h, 10)) b.WriteString(" — index ") b.WriteString(strconv.Itoa(idx)) b.WriteString(" of ") b.WriteString(strconv.Itoa(count)) b.WriteString("._\n\n") // Full list. b.WriteString("## All quotes (") b.WriteString(strconv.Itoa(count)) b.WriteString(")\n\n") for _, q := range board { b.WriteString("- **#") b.WriteString(strconv.Itoa(q.id)) b.WriteString("** ") b.WriteString(q.text) b.WriteString(" — _") b.WriteString(authorLabel(q)) b.WriteString("_\n") } return b.String() } // authorLabel renders a human-friendly author string. Seeded quotes (empty // author) show as the house account. func authorLabel(q quote) string { if q.author == "" { return "the board" } return q.author }
- #8/gno.MemPackageType
- #9 MPUserProd
Result log
msg:0,success:true,log:,events:[]