Transaction
DE45263BF202D7…0E2948AC0F04
Block 26,398 · index 0 · indexed
Summary
- Hash
- DE45263BF202D7F72A724E42BA85ED9A28EDD6FA930BC0F310290E2948AC0F04
- Block
- 26,398
- Size
- 7119 bytes
- Gas used
- 11,838,513 / 14,206,275
- Fee
- 1000000ugnot
- Memo
- gnopublish
- Status
- success
Messages
Arguments · 9
- #1splitter
- #2README.md
- #3# Payment Splitter > ⚠️ **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 share-based payment splitter for gno.land — an accounting-only port of the Solidity `PaymentSplitter`. No real coins move: each group tracks payees, their integer shares, and a pooled income figure, and computes what each payee is owed as `amount * share / totalShares`. Register a group with comma-separated payee addresses and matching shares, record income against it, and `Render` shows every group's per-payee owed amounts (integer division; any rounding remainder is displayed separately). **Realm path:** `gno.land/r/REPLACE_ADDR/splitter` ## Example calls ``` # Create a group: alice gets 3 shares, bob gets 1 -> returns group id 0 Register("g1alice...,g1bob...", "3,1") # Record 100 units of income into group 0 RecordIncome(0, 100) # View the ledger (alice owed 75, bob owed 25) gnokey query vm/qrender --data "gno.land/r/REPLACE_ADDR/splitter:" ``` <!-- 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/splitter/v0" gno = "0.9"
- #6splitter.gno
- #7// Package splitter is a share-based payment splitter for gno.land. // // It is an accounting-only port of the Solidity PaymentSplitter pattern: // no real coins move. A group is registered with a list of payees and a // matching list of integer shares. Income recorded against a group is // pooled, and each payee is owed a slice of that pool proportional to // their shares: owed = pool * share / totalShares. package splitter import ( "errors" "strconv" "strings" "chain" "chain/runtime/unsafe" "gno.land/p/nt/avl/v0" ) // payee is a single share holder inside a group. type payee struct { addr address shares int } // group is one payment-splitting arrangement. type group struct { id int owner address payees []payee totalShares int pool int // total income recorded, in abstract units } var ( groups avl.Tree // id (zero-padded string) -> *group nextID int errEmpty = errors.New("splitter: no payees") ) // Register creates a new group from comma-separated payees and shares. // payees: "g1abc...,g1def..." (addresses) // shares: "3,1" (positive integers, same count as payees) // Returns the new group id. Panics on malformed input. func Register(cur realm, payees string, shares string) int { caller := unsafe.PreviousRealm().Address() addrParts := splitTrim(payees) shareParts := splitTrim(shares) if len(addrParts) == 0 { panic(errEmpty) } if len(addrParts) != len(shareParts) { panic("splitter: payees and shares count mismatch") } g := &group{ id: nextID, owner: caller, } for i := range addrParts { if addrParts[i] == "" { panic("splitter: empty payee address") } s, err := strconv.Atoi(shareParts[i]) if err != nil { panic("splitter: bad share value: " + shareParts[i]) } if s <= 0 { panic("splitter: share must be positive") } g.payees = append(g.payees, payee{ addr: address(addrParts[i]), shares: s, }) g.totalShares += s } groups.Set(key(g.id), g) nextID++ chain.Emit( "GroupRegistered", "id", strconv.Itoa(g.id), "payees", strconv.Itoa(len(g.payees)), "totalShares", strconv.Itoa(g.totalShares), ) return g.id } // RecordIncome adds amount to the pool of group id. amount must be positive. func RecordIncome(cur realm, id int, amount int) { if amount <= 0 { panic("splitter: amount must be positive") } v := groups.Get(key(id)) if v == nil { panic("splitter: unknown group id: " + strconv.Itoa(id)) } g := v.(*group) g.pool += amount groups.Set(key(id), g) chain.Emit( "IncomeRecorded", "id", strconv.Itoa(id), "amount", strconv.Itoa(amount), "pool", strconv.Itoa(g.pool), ) } // owed returns the amount owed to a payee: pool * shares / totalShares. func (g *group) owed(p payee) int { if g.totalShares == 0 { return 0 } return g.pool * p.shares / g.totalShares } // Render shows all groups, their payees, shares, and computed owed amounts. func Render(path string) string { if groups.Size() == 0 { return "# Payment Splitter\n\n_No groups registered yet._\n" } var b strings.Builder b.WriteString("# Payment Splitter\n\n") b.WriteString("Share-based accounting. `owed = pool * share / totalShares`.\n\n") groups.Iterate("", "", func(k string, v interface{}) bool { g := v.(*group) b.WriteString("## Group #" + strconv.Itoa(g.id) + "\n\n") b.WriteString("- Owner: `" + g.owner.String() + "`\n") b.WriteString("- Pool: " + strconv.Itoa(g.pool) + "\n") b.WriteString("- Total shares: " + strconv.Itoa(g.totalShares) + "\n\n") b.WriteString("| Payee | Shares | Owed |\n") b.WriteString("|---|---:|---:|\n") distributed := 0 for _, p := range g.payees { o := g.owed(p) distributed += o b.WriteString("| `" + p.addr.String() + "` | " + strconv.Itoa(p.shares) + " | " + strconv.Itoa(o) + " |\n") } remainder := g.pool - distributed if remainder > 0 { b.WriteString("| _remainder (rounding)_ | | " + strconv.Itoa(remainder) + " |\n") } b.WriteString("\n") return false }) return b.String() } // splitTrim splits on commas and trims whitespace around each element. func splitTrim(s string) []string { if strings.TrimSpace(s) == "" { return nil } parts := strings.Split(s, ",") out := make([]string, 0, len(parts)) for _, p := range parts { out = append(out, strings.TrimSpace(p)) } return out } // key produces a zero-padded, lexicographically-sortable avl key for an id, // so Render iterates groups in ascending numeric order. func key(id int) string { s := strconv.Itoa(id) for len(s) < 12 { s = "0" + s } return s }
- #8/gno.MemPackageType
- #9 MPUserProd
Result log
msg:0,success:true,log:,events:[]