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

reaper/v1 package

Overview

Package reaper is a noticeboard whose garbage is a standing bounty.

Anyone can post a note with an expiry. Posting locks a storage deposit, paid by the poster. Once a note expires, anyone at all can delete it, and the chain refunds that deposit to whoever signed the deleting transaction. So the poster pays to occupy space and a stranger is paid to reclaim it.

Nothing here is a token, a reward pool or an emission schedule. The incentive is the chain's own storage accounting, which already works this way for every realm on gno.land; this realm only makes it legible. Reap and Compact are permissionless because the expiry predicate is checked on chain, so the worst a malicious reaper can do is waste their own gas.

Two collaborators own the parts this realm does not:

  • gno.land/p/moul/ulist/v1 stores the notes and owns compaction. Its Delete is a soft delete that leaves a dead tree node behind, and its Compact frees those nodes without moving any live index.
  • gno.land/p/moul/x/storagecost/v0 owns the arithmetic: what a byte refunds, and how many bytes a transaction must free to pay for itself.
  • gno.land/p/moul/kit/ui/v0 owns display: the escaping, the length cap and the address format. A note body is arbitrary bytes chosen by whoever paid to post it and is rendered back to every reader, so it is attacker-controlled input in a markdown slot. Render never emits a body unescaped; ui.Excerpt is what makes that true, and it delegates to p/nt/markdown/sanitize.

The realm deliberately cannot see its own byte count. No stdlib call exposes a realm's locked storage, so every figure Render shows is an estimate from payload length. The authoritative numbers are the chain's, in the StorageDepositEvent and StorageUnlockEvent each transaction emits.

Functions

Bounty

func Bounty() storagecost.Quote

Bounty prices what is currently on the table for a reaper, at the default storage price and the floor gas price.

The byte figure is an estimate from payload length, never the chain's own accounting. Treat it as an advertisement, not a settlement.

Command

gnokey query vm/qeval -remote "https://rpc.gno.land" -data "gno.land/r/moul/x/reaper/v1.Bounty()"

Result

Compact

func Compact(cur realm) int

Compact frees the tree nodes that reaping left behind and returns how many.

Also permissionless, and also paid the same way. It is a separate call because it is a separate economic decision, and a surprisingly large one: on chain, compacting a drained board returned about three times what deleting the notes themselves did, because a list node costs more than the note it carries. But it returns nothing at all while any live note sits below the dead ones, so the two calls are worth batching in that order: reap, then compact. Compactable says how much is actually there, for free.

Command

# WARNING: This command is running in an INSECURE mode.
# It is strongly recommended to use a hardware device for signing
# and avoid trusting any computer connected to the internet,
# as your private keys could be exposed.

gnokey maketx call -pkgpath "gno.land/r/moul/x/reaper/v1" -func "Compact" -gas-fee 1000000ugnot -gas-wanted 1_000_000_000 -send "" -chainid "gnoland-1" -remote "https://rpc.gno.land" ADDRESSgnokey query -remote "https://rpc.gno.land" auth/accounts/ADDRESS
gnokey maketx call -pkgpath "gno.land/r/moul/x/reaper/v1" -func "Compact" -gas-fee 1000000ugnot -gas-wanted 1_000_000_000 -send "" -broadcast=false ADDRESS > call.tx
gnokey sign -tx-path call.tx -chainid "gnoland-1" -account-number ACCOUNTNUMBER -account-sequence SEQUENCENUMBER ADDRESS
gnokey broadcast -remote "https://rpc.gno.land" call.tx
  

Compactable

func Compactable() int

Compactable reports how many dead tree nodes a Compact would free.

Command

gnokey query vm/qeval -remote "https://rpc.gno.land" -data "gno.land/r/moul/x/reaper/v1.Compactable()"

Result

Live

func Live() int

Live counts the notes still holding storage.

Command

gnokey query vm/qeval -remote "https://rpc.gno.land" -data "gno.land/r/moul/x/reaper/v1.Live()"

Result

Post

func Post(cur realm, body string, ttl int64) int

Post adds a note that becomes reapable ttl blocks from now, and locks the storage deposit for it against the caller.

A ttl of zero is allowed and makes the note reapable immediately, which is the cheapest way to demonstrate the mechanism.

Params

Command

# WARNING: This command is running in an INSECURE mode.
# It is strongly recommended to use a hardware device for signing
# and avoid trusting any computer connected to the internet,
# as your private keys could be exposed.

gnokey maketx call -pkgpath "gno.land/r/moul/x/reaper/v1" -func "Post" -args $'' -args $'' -gas-fee 1000000ugnot -gas-wanted 1_000_000_000 -send "" -chainid "gnoland-1" -remote "https://rpc.gno.land" ADDRESSgnokey query -remote "https://rpc.gno.land" auth/accounts/ADDRESS
gnokey maketx call -pkgpath "gno.land/r/moul/x/reaper/v1" -func "Post" -args $'' -args $'' -gas-fee 1000000ugnot -gas-wanted 1_000_000_000 -send "" -broadcast=false ADDRESS > call.tx
gnokey sign -tx-path call.tx -chainid "gnoland-1" -account-number ACCOUNTNUMBER -account-sequence SEQUENCENUMBER ADDRESS
gnokey broadcast -remote "https://rpc.gno.land" call.tx
  

Reap

func Reap(cur realm, limit int) int

Reap deletes up to limit expired notes and returns how many it deleted.

Permissionless by design. The storage deposit freed goes to whoever signed this transaction, so a stranger keeping the board tidy is paid for it out of the deposits the posters locked. A note that has not expired is skipped, not refused, so a reaper never has to guess which indices are ripe.

It walks from the highest index down, which is not cosmetic. In the backing list the oldest indices are the ancestors of the newest, so a node can only be freed once everything below it is dead. Reaping oldest-first with a binding limit therefore never creates a dead tail and leaves Compactable at zero, stranding the tree structure, which measures at roughly two thirds of what an entry costs. Reaping newest-first makes each batch immediately compactable. Every candidate is expired either way, so the order changes only who gets paid how much, and it is measured: see the ulist package doc.

Param

Command

# WARNING: This command is running in an INSECURE mode.
# It is strongly recommended to use a hardware device for signing
# and avoid trusting any computer connected to the internet,
# as your private keys could be exposed.

gnokey maketx call -pkgpath "gno.land/r/moul/x/reaper/v1" -func "Reap" -args $'' -gas-fee 1000000ugnot -gas-wanted 1_000_000_000 -send "" -chainid "gnoland-1" -remote "https://rpc.gno.land" ADDRESSgnokey query -remote "https://rpc.gno.land" auth/accounts/ADDRESS
gnokey maketx call -pkgpath "gno.land/r/moul/x/reaper/v1" -func "Reap" -args $'' -gas-fee 1000000ugnot -gas-wanted 1_000_000_000 -send "" -broadcast=false ADDRESS > call.tx
gnokey sign -tx-path call.tx -chainid "gnoland-1" -account-number ACCOUNTNUMBER -account-sequence SEQUENCENUMBER ADDRESS
gnokey broadcast -remote "https://rpc.gno.land" call.tx
  

Reapable

func Reapable() int

Reapable counts the notes that have expired and not yet been reaped.

Command

gnokey query vm/qeval -remote "https://rpc.gno.land" -data "gno.land/r/moul/x/reaper/v1.Reapable()"

Result

Render

func Render(path string) string

Param

Command

gnokey query vm/qeval -remote "https://rpc.gno.land" -data "gno.land/r/moul/x/reaper/v1.Render()"

Result