← Back to Realms

Realm

gno.land/r/moul/x/grc20faucet/v0

Overview

Realm Path
gno.land/r/moul/x/grc20faucet/v0
Exported Functions
9
State Entries
10
Source Files
3
Total Package Entries
24

Exported Functions

9 exported functions

State

10 state entries

Source Code

FILES
faucet.gno
go
1// Package grc20faucet issues two free GRC20 tokens, RED and BLUE, and gives
2// them away on request.
3//
4// It exists so that anything needing a token to experiment on has one that is
5// worthless, unlimited and shared. Both tokens are registered in
6// gno.land/r/nt/grc20reg/v0, which is what lets another realm find and move
7// them by key without importing this package: that indirection is the whole
8// point, and it is what gno.land/r/moul/x/grc20wrapdemo/v0 wraps.
9//
10// Two tokens rather than one, because the interesting patterns start at two: a
11// meta-token over a pair needs a pair.
12package grc20faucet
13
14import (
15	"chain/runtime"
16
17	"gno.land/p/nt/avl/v0"
18	"gno.land/p/nt/grc20/v0"
19	"gno.land/p/nt/ufmt/v0"
20	"gno.land/r/nt/grc20reg/v0"
21)
22
23// Tokens are exported so an importing realm can read them directly; the
24// ledgers stay private, since they carry unrestricted mint and burn.
25var (
26	Red  *grc20.Token
27	Blue *grc20.Token
28
29	redLedger  *grc20.PrivateLedger
30	blueLedger *grc20.PrivateLedger
31
32	// RedKey and BlueKey are the grc20reg lookup keys, the way a realm that
33	// does NOT import this package refers to these tokens.
34	RedKey  string
35	BlueKey string
36
37	lastClaim = avl.NewTree() // address -> block height of its last claim
38)
39
40const (
41	decimals = 4
42
43	// ClaimAmount is minted of EACH token per claim: 100.0000 units.
44	ClaimAmount = int64(1_000_000)
45
46	// ClaimEvery is the cooldown, in blocks, between two claims by the same
47	// account. Free money with no cooldown is a spam vector, not a faucet.
48	ClaimEvery = int64(100)
49)
50
51func init(cur realm) {
52	Red, redLedger = grc20.NewToken("Red Token", "RED", decimals, 0, cur)
53	Blue, blueLedger = grc20.NewToken("Blue Token", "BLUE", decimals, 1, cur)
54	RedKey = grc20reg.Register(cross(cur), Red, "red")
55	BlueKey = grc20reg.Register(cross(cur), Blue, "blue")
56}
57
58// Claim mints ClaimAmount of both RED and BLUE to the caller.
59func Claim(cur realm) {
60	who := caller(cur)
61	now := runtime.ChainHeight()
62	if next, ok := NextClaim(who); ok && now < next {
63		panic(ufmt.Sprintf("too soon: next claim at block %d, current %d", next, now))
64	}
65	lastClaim.Set(who.String(), now)
66	checkErr(redLedger.Mint(who, ClaimAmount))
67	checkErr(blueLedger.Mint(who, ClaimAmount))
68}
69
70// NextClaim returns the block height at which `who` may claim again, and
71// whether they have ever claimed at all.
72func NextClaim(who address) (int64, bool) {
73	v := lastClaim.Get(who.String())
74	if v == nil {
75		return 0, false
76	}
77	return v.(int64) + ClaimEvery, true
78}
79
80// Transfer moves the caller's own units of `symbol`.
81func Transfer(cur realm, symbol string, to address, amount int64) {
82	checkErr(ledgerOf(symbol).CallerTeller().Transfer(0, cur, to, amount))
83}
84
85// Approve lets `spender` draw `amount` of `symbol` from the caller's balance.
86//
87// This is the call that makes every wrapper in grc20wrapdemo work: the wrapper
88// realm has no authority over anybody's tokens until its address is named here.
89func Approve(cur realm, symbol string, spender address, amount int64) {
90	checkErr(ledgerOf(symbol).CallerTeller().Approve(0, cur, spender, amount))
91}
92
93// TransferFrom spends an allowance the caller was granted.
94func TransferFrom(cur realm, symbol string, from, to address, amount int64) {
95	checkErr(ledgerOf(symbol).CallerTeller().TransferFrom(0, cur, from, to, amount))
96}
97
98// BalanceOf returns `owner`'s balance of `symbol`.
99func BalanceOf(symbol string, owner address) int64 {
100	return tokenOf(symbol).BalanceOf(owner)
101}
102
103// Allowance returns what `owner` let `spender` draw of `symbol`.
104func Allowance(symbol string, owner, spender address) int64 {
105	return tokenOf(symbol).Allowance(owner, spender)
106}
107
108// TotalSupply returns how much of `symbol` has been claimed so far.
109func TotalSupply(symbol string) int64 {
110	return tokenOf(symbol).TotalSupply()
111}
112
113func tokenOf(symbol string) *grc20.Token {
114	switch symbol {
115	case "RED":
116		return Red
117	case "BLUE":
118		return Blue
119	}
120	panic("unknown symbol " + symbol + " (RED or BLUE)")
121}
122
123func ledgerOf(symbol string) *grc20.PrivateLedger {
124	switch symbol {
125	case "RED":
126		return redLedger
127	case "BLUE":
128		return blueLedger
129	}
130	panic("unknown symbol " + symbol + " (RED or BLUE)")
131}
132
133// caller is the account or realm that crossed into this one.
134func caller(cur realm) address {
135	if !cur.IsCurrent() {
136		panic("grc20faucet: stale realm token")
137	}
138	return cur.Previous().Address()
139}
140
141func checkErr(err error) {
142	if err != nil {
143		panic(err)
144	}
145}
146
147func Render(path string) string {
148	s := "# GRC20 faucet: RED and BLUE\n\n"
149	s += "Two worthless tokens to experiment on. `Claim` gives you "
150	s += ufmt.Sprintf("%d of each, once every %d blocks.\n\n", ClaimAmount, ClaimEvery)
151
152	s += "| token | symbol | decimals | supply | registry key |\n"
153	s += "|---|---|---|---|---|\n"
154	s += ufmt.Sprintf("| %s | RED | %d | %d | `%s` |\n",
155		Red.GetName(), Red.GetDecimals(), Red.TotalSupply(), RedKey)
156	s += ufmt.Sprintf("| %s | BLUE | %d | %d | `%s` |\n",
157		Blue.GetName(), Blue.GetDecimals(), Blue.TotalSupply(), BlueKey)
158
159	s += "\n## Use it\n\n"
160	s += "```\n"
161	s += "gnokey maketx call -pkgpath gno.land/r/moul/x/grc20faucet/v0 -func Claim ...\n"
162	s += "gnokey maketx call -pkgpath gno.land/r/moul/x/grc20faucet/v0 -func Approve \\\n"
163	s += "  -args RED -args <spender> -args 1000000 ...\n"
164	s += "```\n\n"
165	s += "Both tokens are registered in [grc20reg](/r/nt/grc20reg/v0), so a realm can\n"
166	s += "move them by key without importing this one. That is what\n"
167	s += "[grc20wrapdemo](/r/moul/x/grc20wrapdemo/v0) does with them.\n"
168
169	if path != "" {
170		s += "\n---\n\n"
171		s += ufmt.Sprintf("Balance of `%s`: RED %d, BLUE %d\n",
172			path, Red.BalanceOf(address(path)), Blue.BalanceOf(address(path)))
173	}
174	return s
175}
176

Raw Package Data

Raw JSON data