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}