Transaction
E7340B62518C12…AB78AC2BEF98
Block 26,071 · index 0 · indexed
Summary
- Hash
- E7340B62518C12155163D32379776C74FA41903723C5B2B262CDAB78AC2BEF98
- Block
- 26,071
- Size
- 8936 bytes
- Gas used
- 13,208,221 / 15,849,925
- Fee
- 1000000ugnot
- Memo
- gnopublish
- Status
- success
Messages
Arguments · 9
- #1handles
- #2README.md
- #3# handles > ⚠️ **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 tiny on-chain nickname registry. Any address can claim one unique handle (3-20 lowercase letters/digits, starting with a letter), attach a short bio, transfer the handle to another address, or release it back into the pool. Two `avl.Tree` indexes (`handle -> record`, `owner -> record`) keep lookups in both directions cheap and deterministic. Realm path: `gno.land/r/g12cs4cehujpffpjpywmkqj43m6u5ya53nj69sjz/handles` ## Example calls ``` Register(cur, "moul") # claim the handle "moul" for the caller SetBio(cur, "building gno stuff") Transfer(cur, g1...recipient) # hand the handle to another address Release(cur) # free the handle back up OwnerOf("moul") # (address, bool) HandleOf(g1...) # (string, bool) Count() # number of registered handles ``` `Render("")` lists every registered handle in a table; `Render("/moul")` shows a single handle's detail page (owner, bio, block registered). ## Toolchain status Built and tested against a local `gno` build at `master.3130+bf5b31eda` (GNOROOT pointed at a full `gnolang/gno` checkout). `gno test .` and `gno lint .` both pass (6 tests, 0 lint issues). Note for test authors: plain `recover()` did not catch panics thrown across a `cross(cur)` boundary in this toolchain build — the non-crossing call form (`Register(cur, ...)` instead of `Register(cross(cur), ...)`) was used in the two panic-expectation tests instead, since no realm boundary is crossed and there was nothing to revive. <!-- 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:**  > 🧪 **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/handles/v0" gno = "0.9"
- #6handles.gno
- #7// Package handles is a tiny on-chain nickname registry. // // Any address may claim one unique handle (3-20 lowercase letters/digits, // starting with a letter), attach a short bio, transfer the handle to // another address, or release it back to the pool. It is deliberately // small: two AVL indexes (handle -> record, address -> record) and a // handful of guarded mutators. package handles import ( "strconv" "strings" "chain" "chain/runtime" "gno.land/p/nt/avl/v0" ) const maxBioLen = 140 // record is the persisted state for one claimed handle. type record struct { handle string owner address bio string sinceBlk int64 } var ( byHandle avl.Tree // handle (string) -> *record byOwner avl.Tree // owner address.String() -> *record ) // validHandle reports whether h is 3-20 chars, starts with a lowercase // letter, and contains only lowercase letters and digits thereafter. func validHandle(h string) bool { if len(h) < 3 || len(h) > 20 { return false } for i := 0; i < len(h); i++ { c := h[i] switch { case c >= 'a' && c <= 'z': case c >= '0' && c <= '9' && i > 0: default: return false } } return true } // Register claims handle for the caller. Aborts if the handle is invalid, // already taken, or the caller already owns a handle (release it first). func Register(cur realm, handle string) { if !cur.IsCurrent() { panic("invalid realm") } if !cur.Previous().IsUserCall() { panic("only a direct user call can register a handle") } if !validHandle(handle) { panic("handle must be 3-20 lowercase letters/digits, starting with a letter") } caller := cur.Previous().Address() if byOwner.Has(caller.String()) { panic("this address already owns a handle; release it first") } if byHandle.Has(handle) { panic("handle already taken") } r := &record{ handle: handle, owner: caller, sinceBlk: runtime.ChainHeight(), } byHandle.Set(handle, r) byOwner.Set(caller.String(), r) chain.Emit("HandleRegistered", "handle", handle, "owner", caller.String()) } // SetBio updates the bio of the handle owned by the caller. func SetBio(cur realm, bio string) { if !cur.IsCurrent() { panic("invalid realm") } if len(bio) > maxBioLen { panic("bio too long (max " + strconv.Itoa(maxBioLen) + " chars)") } r := ownRecord(cur) r.bio = bio chain.Emit("BioUpdated", "handle", r.handle) } // Release frees the handle owned by the caller, making it claimable again. func Release(cur realm) { if !cur.IsCurrent() { panic("invalid realm") } r := ownRecord(cur) byHandle.Remove(r.handle) byOwner.Remove(r.owner.String()) chain.Emit("HandleReleased", "handle", r.handle) } // Transfer moves the caller's handle to another address that does not // already own one. func Transfer(cur realm, to address) { if !cur.IsCurrent() { panic("invalid realm") } if !to.IsValid() { panic("invalid recipient address") } r := ownRecord(cur) if byOwner.Has(to.String()) { panic("recipient already owns a handle") } byOwner.Remove(r.owner.String()) r.owner = to byOwner.Set(to.String(), r) chain.Emit("HandleTransferred", "handle", r.handle, "to", to.String()) } // ownRecord resolves the record for the current caller, panicking if the // caller does not own a handle. func ownRecord(cur realm) *record { caller := cur.Previous().Address() v := byOwner.Get(caller.String()) if v == nil { panic("this address does not own a handle") } return v.(*record) } // OwnerOf returns the owner of handle and whether it exists. func OwnerOf(handle string) (address, bool) { v := byHandle.Get(handle) if v == nil { return address(""), false } return v.(*record).owner, true } // HandleOf returns the handle owned by addr, if any. func HandleOf(addr address) (string, bool) { v := byOwner.Get(addr.String()) if v == nil { return "", false } return v.(*record).handle, true } // Count returns the number of currently registered handles. func Count() int { return byHandle.Size() } // Render renders Markdown. "/" lists every handle; "/<handle>" shows a // single record's detail. func Render(path string) string { path = strings.TrimPrefix(path, "/") if path == "" { return renderIndex() } return renderHandle(path) } func renderIndex() string { var b strings.Builder b.WriteString("# 🪪 Handles\n\n") b.WriteString("A tiny on-chain nickname registry. Claim a short handle, ") b.WriteString("attach a bio, transfer it, or release it.\n\n") if byHandle.Size() == 0 { b.WriteString("_No handles registered yet._\n\n") b.WriteString("Call `Register(handle)` to claim the first one.\n") return b.String() } b.WriteString("| Handle | Owner | Since block |\n") b.WriteString("|--------|-------|-------------|\n") byHandle.Iterate("", "", func(key string, value interface{}) bool { r := value.(*record) b.WriteString("| [" + key + "](/" + key + ") | " + short(r.owner.String()) + " | " + strconv.Itoa(int(r.sinceBlk)) + " |\n") return false }) b.WriteString("\nTotal handles: " + strconv.Itoa(byHandle.Size()) + "\n") return b.String() } func renderHandle(handle string) string { v := byHandle.Get(handle) if v == nil { return "# Handles\n\nNo handle named `" + handle + "`.\n\n[← all handles](/)\n" } r := v.(*record) var b strings.Builder b.WriteString("# 🪪 @" + r.handle + "\n\n") b.WriteString("**Owner:** " + r.owner.String() + "\n\n") if r.bio != "" { b.WriteString("**Bio:** " + r.bio + "\n\n") } b.WriteString("**Registered at block:** " + strconv.Itoa(int(r.sinceBlk)) + "\n\n") b.WriteString("[← all handles](/)\n") return b.String() } // short truncates a long address for the index table. func short(s string) string { if len(s) <= 12 { return s } return s[:6] + "…" + s[len(s)-4:] }
- #8/gno.MemPackageType
- #9 MPUserProd
Result log
msg:0,success:true,log:,events:[]