1package grc20faucet23import (4 "strings"5 "testing"67 "gno.land/p/nt/testutils/v0"8 "gno.land/p/nt/uassert/v0"9 "gno.land/r/nt/grc20reg/v0"10)1112// testing.SetRealm governs the crossing calls made from the frame that called13// it, so switching accounts has to be written inline in each test, never14// through a helper that does not itself cross.15func TestClaimAndCooldown(cur realm, t *testing.T) {16 alice := testutils.TestAddress("f-alice")17 testing.SetRealm(testing.NewUserRealm(alice))1819 _, ok := NextClaim(alice)20 uassert.False(t, ok, "no cooldown before the first claim")2122 Claim(cross(cur))23 uassert.Equal(t, ClaimAmount, BalanceOf("RED", alice))24 uassert.Equal(t, ClaimAmount, BalanceOf("BLUE", alice))2526 next, ok := NextClaim(alice)27 uassert.True(t, ok, "a cooldown is recorded")28 uassert.AbortsContains(t, cur, "too soon", func() { Claim(cross(cur)) })2930 testing.SkipHeights(ClaimEvery + 1)31 Claim(cross(cur))32 uassert.Equal(t, 2*ClaimAmount, BalanceOf("RED", alice))3334 after, _ := NextClaim(alice)35 uassert.True(t, after > next, "the cooldown moved forward")36}3738func TestTransferAndAllowance(cur realm, t *testing.T) {39 alice := testutils.TestAddress("f-tx-alice")40 bob := testutils.TestAddress("f-tx-bob")41 carl := testutils.TestAddress("f-tx-carl")4243 testing.SetRealm(testing.NewUserRealm(alice))44 Claim(cross(cur))45 Transfer(cross(cur), "RED", bob, 400_000)46 uassert.Equal(t, int64(400_000), BalanceOf("RED", bob))47 uassert.Equal(t, ClaimAmount-400_000, BalanceOf("RED", alice))4849 Approve(cross(cur), "BLUE", carl, 250_000)50 uassert.Equal(t, int64(250_000), Allowance("BLUE", alice, carl))5152 testing.SetRealm(testing.NewUserRealm(carl))53 TransferFrom(cross(cur), "BLUE", alice, bob, 250_000)54 uassert.Equal(t, int64(250_000), BalanceOf("BLUE", bob))55 uassert.Equal(t, int64(0), Allowance("BLUE", alice, carl))56 uassert.AbortsContains(t, cur, "insufficient allowance", func() {57 TransferFrom(cross(cur), "BLUE", alice, bob, 1)58 })59}6061func TestUnknownSymbolIsRefused(cur realm, t *testing.T) {62 who := testutils.TestAddress("f-unknown")63 testing.SetRealm(testing.NewUserRealm(who))6465 uassert.PanicsContains(t, cur, "unknown symbol GREEN", func() {66 BalanceOf("GREEN", who)67 })68 uassert.AbortsContains(t, cur, "unknown symbol GREEN", func() {69 Transfer(cross(cur), "GREEN", who, 1)70 })71}7273// Both tokens are in the registry under their own realm and symbol, which is74// what lets another realm move them without importing this one.75func TestBothTokensAreRegistered(t *testing.T) {76 uassert.Equal(t, "gno.land/r/moul/x/grc20faucet/v0.RED", RedKey)77 uassert.Equal(t, "gno.land/r/moul/x/grc20faucet/v0.BLUE", BlueKey)78 uassert.Equal(t, "Red Token", grc20reg.MustGet(RedKey).GetName())79 uassert.Equal(t, "Blue Token", grc20reg.MustGet(BlueKey).GetName())80 uassert.Equal(t, 4, grc20reg.MustGet(BlueKey).GetDecimals())81}8283func TestRender(cur realm, t *testing.T) {84 who := testutils.TestAddress("f-render")85 testing.SetRealm(testing.NewUserRealm(who))86 Claim(cross(cur))8788 home := Render("")89 uassert.True(t, strings.Contains(home, "# GRC20 faucet: RED and BLUE"), home)90 uassert.True(t, strings.Contains(home, "| Red Token | RED | 4 |"), home)91 uassert.True(t, strings.Contains(home, RedKey), "the registry key is discoverable")9293 page := Render(who.String())94 uassert.True(t, strings.Contains(page, "Balance of"), page)95 uassert.True(t, strings.Contains(page, "RED 1000000"), page)96}97Allowance(symbol string, owner string, spender string) int64
Approve(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, symbol string, spender string, amount int64)
BalanceOf(symbol string, owner string) int64
Claim(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string})
NextClaim(who string) (int64, bool)
Render(path string) string
TotalSupply(symbol string) int64
Transfer(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, symbol string, to string, amount int64)
TransferFrom(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, symbol string, from string, to string, amount int64)
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.