Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

faucet.gno

5.53 Kb · 175 lines
  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}