Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}