Search Apps Documentation Source Content File Folder Download Copy Actions Download

faucet.gno

3.83 Kb · 135 lines
  1// Package faucet is a toy points faucet realm for the gno.land test13 testnet.
  2//
  3// Any caller may Claim() 100 points, but only once per cooldown window of 100
  4// blocks. Cooldown is enforced deterministically via runtime.ChainHeight() — no
  5// real clocks, no RNG. Per-caller balance and last-claim height live in an
  6// avl.Tree so Render iterates in deterministic (address-sorted) order.
  7package faucet
  8
  9import (
 10	"chain"
 11	"chain/runtime"
 12	"chain/runtime/unsafe"
 13	"strconv"
 14	"strings"
 15
 16	"gno.land/p/nt/avl/v0"
 17)
 18
 19const (
 20	// ClaimAmount is granted on every successful claim.
 21	ClaimAmount = 100
 22	// CooldownBlocks is the minimum number of blocks between two claims by the
 23	// same caller.
 24	CooldownBlocks = 100
 25)
 26
 27// claimer holds the per-address state.
 28type claimer struct {
 29	Balance   int64 // total points accumulated
 30	LastClaim int64 // chain height of the last successful claim
 31	Claims    int64 // number of successful claims by this address
 32}
 33
 34var (
 35	// claimers maps address string -> *claimer.
 36	claimers = avl.NewTree()
 37	// totalDistributed is the running sum of all points ever granted.
 38	totalDistributed int64
 39	// totalClaims is the total number of successful claims across all callers.
 40	totalClaims int64
 41)
 42
 43// Claim grants ClaimAmount points to the caller, subject to the per-caller
 44// cooldown. It aborts (cross-realm) if the caller must still wait.
 45func Claim(cur realm) {
 46	caller := unsafe.PreviousRealm().Address()
 47	key := caller.String()
 48	height := runtime.ChainHeight()
 49
 50	var c *claimer
 51	if v, ok := claimers.Get(key); ok {
 52		c = v.(*claimer)
 53		wait := c.LastClaim + CooldownBlocks - height
 54		if wait > 0 {
 55			panic("cooldown active: " + strconv.FormatInt(wait, 10) +
 56				" block(s) remaining before you can claim again")
 57		}
 58	} else {
 59		c = &claimer{}
 60	}
 61
 62	c.Balance += ClaimAmount
 63	c.LastClaim = height
 64	c.Claims++
 65	claimers.Set(key, c)
 66
 67	totalDistributed += ClaimAmount
 68	totalClaims++
 69
 70	chain.Emit(
 71		"Claim",
 72		"caller", key,
 73		"amount", strconv.FormatInt(ClaimAmount, 10),
 74		"balance", strconv.FormatInt(c.Balance, 10),
 75		"height", strconv.FormatInt(height, 10),
 76	)
 77}
 78
 79// BalanceOf returns the current point balance of addr (0 if never claimed).
 80func BalanceOf(addr string) int64 {
 81	if v, ok := claimers.Get(addr); ok {
 82		return v.(*claimer).Balance
 83	}
 84	return 0
 85}
 86
 87// Render produces a Markdown dashboard of faucet activity.
 88func Render(path string) string {
 89	var b strings.Builder
 90
 91	b.WriteString("# Token Faucet\n\n")
 92	b.WriteString("A toy points faucet. Call `Claim` to receive **")
 93	b.WriteString(strconv.FormatInt(ClaimAmount, 10))
 94	b.WriteString(" points** — but only once every **")
 95	b.WriteString(strconv.FormatInt(CooldownBlocks, 10))
 96	b.WriteString(" blocks** per address.\n\n")
 97
 98	b.WriteString("## Stats\n\n")
 99	b.WriteString("- Total distributed: **")
100	b.WriteString(strconv.FormatInt(totalDistributed, 10))
101	b.WriteString("** points\n")
102	b.WriteString("- Total claims: **")
103	b.WriteString(strconv.FormatInt(totalClaims, 10))
104	b.WriteString("**\n")
105	b.WriteString("- Unique claimers: **")
106	b.WriteString(strconv.Itoa(claimers.Size()))
107	b.WriteString("**\n")
108	b.WriteString("- Current block height: **")
109	b.WriteString(strconv.FormatInt(runtime.ChainHeight(), 10))
110	b.WriteString("**\n\n")
111
112	b.WriteString("## Claimers\n\n")
113	if claimers.Size() == 0 {
114		b.WriteString("_No one has claimed yet. Be the first!_\n")
115		return b.String()
116	}
117
118	b.WriteString("| Address | Balance | Claims | Last claim (height) |\n")
119	b.WriteString("| --- | ---: | ---: | ---: |\n")
120	claimers.Iterate("", "", func(key string, value interface{}) bool {
121		c := value.(*claimer)
122		b.WriteString("| `")
123		b.WriteString(key)
124		b.WriteString("` | ")
125		b.WriteString(strconv.FormatInt(c.Balance, 10))
126		b.WriteString(" | ")
127		b.WriteString(strconv.FormatInt(c.Claims, 10))
128		b.WriteString(" | ")
129		b.WriteString(strconv.FormatInt(c.LastClaim, 10))
130		b.WriteString(" |\n")
131		return false
132	})
133
134	return b.String()
135}