← Back to Realms

Realm

gno.land/r/moul/x/daily/faucet/v0

Overview

Realm Path
gno.land/r/moul/x/daily/faucet/v0
Exported Functions
3
State Entries
6
Source Files
2
Total Package Entries
9

Exported Functions

3 exported functions

State

6 state entries

Source Code

FILES
faucet.gno
go
1// Package faucet is a toy points faucet realm for the gno.land sapphire 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 := claimers.Get(key); v != nil {
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 := claimers.Get(addr); v != nil {
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}
136

Raw Package Data

Raw JSON data