Search Apps Documentation Source Content File Folder Download Copy Actions Download

grc20reg.gno

2.36 Kb · 97 lines
 1package grc20reg
 2
 3import (
 4	"chain"
 5	"chain/runtime"
 6
 7	"gno.land/p/demo/tokens/grc20"
 8	"gno.land/p/moul/md"
 9	"gno.land/p/nt/avl/v0"
10	"gno.land/p/nt/avl/v0/rotree"
11	"gno.land/p/nt/fqname/v0"
12	"gno.land/p/nt/ufmt/v0"
13)
14
15var registry = avl.NewTree() // rlmPath[.slug] -> *Token (slug is optional)
16func Register(cur realm, token *grc20.Token, slug string) {
17	if slug != "" {
18		validateSlug(slug)
19	}
20	rlmPath := runtime.PreviousRealm().PkgPath()
21	key := fqname.Construct(rlmPath, slug)
22	registry.Set(key, token)
23	chain.Emit(
24		registerEvent,
25		"pkgpath", rlmPath,
26		"slug", slug,
27	)
28}
29
30func Get(key string) *grc20.Token {
31	token, ok := registry.Get(key)
32	if !ok {
33		return nil
34	}
35	return token.(*grc20.Token)
36}
37
38func MustGet(key string) *grc20.Token {
39	token := Get(key)
40	if token == nil {
41		panic("unknown token: " + key)
42	}
43	return token
44}
45
46func Render(path string) string {
47	switch {
48	case path == "": // home
49		// TODO: add pagination
50		s := ""
51		count := 0
52		registry.Iterate("", "", func(key string, tokenI any) bool {
53			count++
54			token := tokenI.(*grc20.Token)
55			rlmPath, slug := fqname.Parse(key)
56			rlmLink := fqname.RenderLink(rlmPath, slug)
57			infoLink := "/r/demo/grc20reg:" + key
58			s += "- " + md.Bold(md.EscapeText(token.GetName())) + " - " + rlmLink + " - " + md.Link("info", infoLink) + "\n"
59			return false
60		})
61		if count == 0 {
62			return "No registered token."
63		}
64		return s
65	default: // specific token
66		key := path
67		token := MustGet(key)
68		rlmPath, slug := fqname.Parse(key)
69		rlmLink := fqname.RenderLink(rlmPath, slug)
70		s := ufmt.Sprintf("# %s\n", md.EscapeText(token.GetName()))
71		s += "- symbol: " + md.Bold(md.EscapeText(token.GetSymbol())) + "\n"
72		s += ufmt.Sprintf("- realm: %s\n", rlmLink)
73		s += ufmt.Sprintf("- decimals: %d\n", token.GetDecimals())
74		s += ufmt.Sprintf("- total supply: %d\n", token.TotalSupply())
75		return s
76	}
77}
78
79const registerEvent = "register"
80
81func GetRegistry() *rotree.ReadOnlyTree {
82	return rotree.Wrap(registry, nil)
83}
84
85// validateSlug panics if the slug contains non-alphanumeric characters.
86// Only letters, digits, dashes, and underscores are allowed.
87func validateSlug(slug string) {
88	for _, c := range slug {
89		if !isAlphanumeric(c) && c != '_' && c != '-' {
90			panic("grc20reg: invalid slug character: " + string(c))
91		}
92	}
93}
94
95func isAlphanumeric(c rune) bool {
96	return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')
97}