Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

v1 source realm

Package guestbook is a public, on-chain guestbook realm for gno.land.

Readme View source

Guestbook

⚠️ 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


A public, on-chain guestbook realm for gno.land. Anyone can leave a signed message; every entry records the signer's address, the message, and the block height at which it was signed. The realm's Render page shows all entries as a Markdown table, newest-first, with a running total.

Built for the sapphire testnet (gno 0.9).

Realm path

gno.land/r/REPLACE_ADDR/guestbook

(REPLACE_ADDR is substituted with the deployer's address at publish time.)

Public API

  • Sign(cur realm, message string) — crossing tx. Appends an entry attributed to the immediate caller. Panics on an empty or over-long (>280 bytes) message.
  • Count() int — total number of signatures (read-only).
  • Render(path string) string — Markdown table of all entries, newest-first.

Example calls

Sign the guestbook (from an EOA via gnokey):

1gnokey maketx call \
2  -pkgpath "gno.land/r/REPLACE_ADDR/guestbook" \
3  -func Sign \
4  -args "gm gno.land!" \
5  -gas-fee 1000000ugnot -gas-wanted 2000000 \
6  -broadcast -chainid sapphire-1 -remote <rpc> mykey

From another realm, invoke it as a crossing call:

1guestbook.Sign(cross(cur), "hello from my realm")

View the rendered guestbook:

1gnokey query vm/qrender --data "gno.land/r/REPLACE_ADDR/guestbook:"

Example render output

1# Guestbook
2
3**Total signatures:** 2
4
5| # | Who | Message | Height |
6|---|-----|---------|--------|
7| 2 | g1v9jxgu…0gh | hello from my realm | 4210 |
8| 1 | g1jg8mtu…qf5 | gm gno.land! | 4180 |

Notes

  • Caller identity uses the gno 0.9 interrealm convention: Sign is a crossing function that checks cur.IsCurrent() before deriving the author from cur.Previous().Address().
  • Block height comes from chain/runtime.ChainHeight() — deterministic.
  • Entries are stored in a gno.land/p/nt/avl/v0 tree keyed by a zero-padded sequence number so iteration order is deterministic and matches insertion order (reversed for newest-first rendering).

## What changed in v1

Two ports against [`v0`](https://github.com/moul/gno-contracts/tree/4f2df83869b80470eb81c48a82fdbe82256b8113/r/moul/x/daily/guestbook), which stays live and untouched.

### Rendering

Identical behaviour to [`v0`](https://github.com/moul/gno-contracts/tree/4f2df83869b80470eb81c48a82fdbe82256b8113/r/moul/x/daily/guestbook) apart from rendering, which now goes
through [`p/moul/kit/ui`](/p/moul/kit/ui/v0) instead of the local `shortAddr`, `escapeCell` helpers.

**One address format.** Eleven realms carried their own `shortAddr` with four different truncation rules, so the same account rendered differently depending on which realm you opened. `ui.Addr` is that decision made once: 8 leading characters, an ellipsis, 4 trailing, in backticks.

**Real escaping.** The local helper replaced a handful of markdown metacharacters. `ui.Inline` and `ui.Cell` delegate to [`p/nt/markdown/sanitize`](/p/nt/markdown/sanitize/v0), which also strips bidi and zero-width characters and folds newlines, so user text cannot reorder or escape the line it sits on.

Visible change: addresses now render in monospace.

### Storage

State moved from a hand-rolled `avl.Tree` + `nextID` + zero-padding helper to
[`p/moul/kit/store`](/p/moul/kit/store/v0), which keys entries by
[`seqid`](/p/nt/seqid/v0) instead of a fixed-width decimal string.

`v0` padded ids to width 16, the widest of the six hand-rolled variants in this
repo and still a ceiling. Past that width the padding stops and the tree
orders `"1000000000000"` before `"999999999999"`, so every list this realm
renders would be wrong from that entry on. `store` keys are 8 big-endian bytes
whose order is numeric for every `uint64`, so there is no width left to outgrow.

`Entry` loses its `ID` field, which the store now owns, so `addEntry` returns
`(store.ID, *Entry)`. The `count` global is gone too: `Len()` answers it.

Ids stay plain integers and the rendered output of the storage port is
unchanged. Each port changes something `v0` promised, one the rendered output
and one the storage layout, so under this repo's versioning rule each is a
compatibility change and neither could be an edit to `v0` in place.

They land in the **same** new version because `v1` was never published. A
version number is a tag on something that exists on a chain, and until it does
there is nothing for a second number to avoid disturbing, so the right move is
to keep editing the version you have. `gnopm unbump` is what folded the second
port back down into this one.

<!-- 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/guestbook/v1 dependency graph](https://raw.githubusercontent.com/moul/gno-contracts/main/_assets/gno.land/r/moul/x/daily/guestbook/v1/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 -->

Overview

Package guestbook is a public, on-chain guestbook realm for gno.land.

Anyone can sign the guestbook with a short message via the Sign crossing function. Every entry records the caller's address, the message, and the block height at which it was signed. Render lists all entries newest-first as a Markdown table.

v1 renders through p/moul/kit/ui(/p/moul/kit/ui/v0) instead of the hand-rolled shortAddr and escapeCell helpers v0 carried. The escaping is the substantive change: v0 replaced four characters, while ui.Cell also strips bidi and zero-width characters, which a message can otherwise use to reorder how the rest of the row reads.

Functions 3

func Count

Action
1func Count() int
source

Count returns the total number of signatures. Read-only, non-crossing.

func Render

1func Render(path string) string
source

Render lists every entry newest-first as a Markdown table plus a total count.

func Sign

crossing Action
1func Sign(cur realm, message string)
source

Sign appends a new entry to the guestbook attributed to the immediate caller.

It is a crossing function (gno 0.9 interrealm convention): callers invoke it as Sign(cross(cur), "hello"). The cur.IsCurrent() guard is the authentication primitive — without it a stale/forged realm value could spoof the author.

Types 1

type Entry

struct
1type Entry struct {
2	Author  string // bech32 address of the signer
3	Message string // the message left by the signer
4	Height  int64  // block height at which the entry was signed
5}
source

Entry is a single signed guestbook line. It carries no ID field: the id belongs to the store, which hands it back on lookup and iteration.

Imports 6

Source Files 5