Search Apps Documentation Source Content File Folder Download Copy Actions Download

store.gno

3.79 Kb · 133 lines
  1package transfer
  2
  3import (
  4	"chain"
  5	"strings"
  6
  7	"gno.land/p/demo/tokens/grc20"
  8	"gno.land/p/nt/bptree/v0"
  9	"gno.land/r/demo/defi/grc20reg"
 10)
 11
 12var (
 13	denoms        *bptree.BPTree // ibcDenom:Denom
 14	totalEscrow   *bptree.BPTree // denom:chain.Coin
 15	voucherTokens *bptree.BPTree // ibcDenom:*voucher
 16)
 17
 18// voucher holds a voucher token and its private ledger for mint/burn.
 19type voucher struct {
 20	token  *grc20.Token
 21	ledger *grc20.PrivateLedger
 22}
 23
 24func init() {
 25	denoms = bptree.NewBPTree32()
 26	totalEscrow = bptree.NewBPTree32()
 27	voucherTokens = bptree.NewBPTree32()
 28}
 29
 30func hasDenom(voucherDenom string) bool {
 31	return denoms.Has(voucherDenom)
 32}
 33
 34func setDenom(denom Denom) {
 35	denoms.Set(denom.IBCDenom(), denom)
 36}
 37
 38func getDenom(ibcDenom string) (Denom, bool) {
 39	x, found := denoms.Get(ibcDenom)
 40	if !found {
 41		return Denom{}, false
 42	}
 43	return x.(Denom), true
 44}
 45
 46func addEscrowForDenom(c chain.Coin) {
 47	x, found := totalEscrow.Get(c.Denom)
 48	if found {
 49		c = x.(chain.Coin).Add(c)
 50	}
 51	totalEscrow.Set(c.Denom, c)
 52}
 53
 54func subEscrowForDenom(c chain.Coin) {
 55	x, found := totalEscrow.Get(c.Denom)
 56	if !found {
 57		x = chain.Coin{Denom: c.Denom}
 58	}
 59	c = x.(chain.Coin).Sub(c)
 60	totalEscrow.Set(c.Denom, c)
 61}
 62
 63// getOrCreateVoucher returns the existing voucher instance for the given IBC
 64// denom, or creates a new one and registers it in grc20reg for DeFi
 65// discoverability. Non-crossing helper; rlm is needed to issue the cross
 66// call into grc20reg.Register from this realm's frame.
 67//
 68// The GRC20 symbol is derived from a short prefix of the IBC hash. We can't
 69// use the full voucherDenom ("ibc/<64-hex-hash>") because v2 grc20 enforces
 70// MaxSymbolLen=11 and [A-Za-z0-9_-] only — the "/" alone disqualifies it. The
 71// grc20reg slug stays the full hash (collision-safe across vouchers); the
 72// symbol is purely the display token symbol and an 8-char hex prefix is
 73// collision-resistant enough for that purpose. Name remains the base denom.
 74func getOrCreateVoucher(_ int, rlm realm, baseDenom, voucherDenom string) *voucher {
 75	if v, ok := voucherTokens.Get(voucherDenom); ok {
 76		return v.(*voucher)
 77	}
 78	hash := voucherDenom[len("ibc/"):]
 79	symbol := hash
 80	if len(symbol) > 11 {
 81		symbol = symbol[:11]
 82	}
 83	token, ledger := grc20.NewToken(0, rlm, baseDenom, symbol, 0)
 84	inst := &voucher{token: token, ledger: ledger}
 85	voucherTokens.Set(voucherDenom, inst)
 86	// Slug = full hash, unique per voucher.
 87	grc20reg.Register(cross(rlm), token, hash)
 88	return inst
 89}
 90
 91// getVoucher returns the voucher instance for the given IBC denom, or nil if not
 92// found.
 93func getVoucher(ibcDenom string) *voucher {
 94	v, ok := voucherTokens.Get(ibcDenom)
 95	if !ok {
 96		return nil
 97	}
 98	return v.(*voucher)
 99}
100
101// VoucherBalanceOf returns the balance of a voucher token for a given address.
102// Returns 0 if the token does not exist.
103func VoucherBalanceOf(ibcDenom string, addr address) int64 {
104	inst := getVoucher(ibcDenom)
105	if inst == nil {
106		return 0
107	}
108	return inst.token.BalanceOf(addr)
109}
110
111// GRC20Alias returns a slash-free alias for a GRC20 denom (grc20reg key)
112// by replacing "/" with ":". This is safe because grc20reg slugs are
113// alphanumeric (enforced by grc20reg.Register), so colons never appear in
114// grc20reg keys, making the replacement reversible.
115// Examples:
116//
117//	"gno.land/r/demo/foo"      → "gno.land:r:demo:foo"
118//	"gno.land/r/demo/foo.FOO"  → "gno.land:r:demo:foo.FOO"
119func GRC20Alias(grc20regKey string) string {
120	return strings.ReplaceAll(grc20regKey, "/", ":")
121}
122
123// resolveGRC20Alias converts a GRC20 alias back to the grc20reg key
124// by replacing ":" with "/".
125func resolveGRC20Alias(alias string) string {
126	return strings.ReplaceAll(alias, ":", "/")
127}
128
129// isGRC20Alias returns true if the denom is a GRC20 alias.
130// GRC20 aliases always start with "gno.land:" (the colon form of "gno.land/").
131func isGRC20Alias(denom string) bool {
132	return strings.HasPrefix(denom, "gno.land:")
133}