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