Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

coins.gno

3.08 Kb · 138 lines
  1package ucs03_zkgm
  2
  3import (
  4	"chain"
  5	"strconv"
  6	"strings"
  7
  8	u256 "gno.land/p/onbloc/math/uint256"
  9	"gno.land/r/demo/defi/grc20reg"
 10)
 11
 12// ibcDenomPrefix is the prefix of wrapped IBC voucher denoms (mirrors
 13// z.PredictWrappedToken*). Used to detect and trim wrapped denoms.
 14const ibcDenomPrefix = "ibc/"
 15
 16const (
 17	// IBC_TOKEN is a zkgm-issued ibc/ voucher. Supply is minted on recv and
 18	// burned on send-side UNESCROW or maker burn-address ack; custody moves via
 19	// transferVoucher between holders and the proxy escrow.
 20	IBC_TOKEN = iota
 21	// GRC20_TOKEN is a locally registered grc20 (not a zkgm voucher). Custody
 22	// moves via Teller TransferFrom/Transfer; this realm cannot burn its supply.
 23	GRC20_TOKEN
 24	// NATIVE_COIN is a banker coin attached with tx -send. Escrow consumes the
 25	// attached funds budget; unescrow releases from the proxy banker balance.
 26	NATIVE_COIN
 27)
 28
 29func isIBCTokenDenom(denom string) bool {
 30	return strings.HasPrefix(denom, ibcDenomPrefix)
 31}
 32
 33func getTokenType(denom string) int {
 34	if isIBCTokenDenom(denom) {
 35		return IBC_TOKEN
 36	}
 37
 38	if token := grc20reg.Get(denom); token != nil {
 39		return GRC20_TOKEN
 40	}
 41
 42	return NATIVE_COIN
 43}
 44
 45// parseCoins parses a comma-separated <amount><denom> string into Coins.
 46func parseCoins(s string) chain.Coins {
 47	if s == "" {
 48		return nil
 49	}
 50
 51	parts := strings.Split(s, ",")
 52	coins := make([]chain.Coin, 0, len(parts))
 53
 54	for _, part := range parts {
 55		if part == "" {
 56			continue
 57		}
 58
 59		i := 0
 60		for i < len(part) && part[i] >= '0' && part[i] <= '9' {
 61			i++
 62		}
 63
 64		if i == 0 || i == len(part) {
 65			panic("zkgm/coins: invalid coin " + part)
 66		}
 67
 68		amount, err := strconv.ParseInt(part[:i], 10, 64)
 69		if err != nil {
 70			panic(err)
 71		}
 72
 73		coins = append(coins, chain.NewCoin(part[i:], amount))
 74	}
 75
 76	return chain.NewCoins(coins...)
 77}
 78
 79// funds is the gno analog of the reference contract's threaded `funds: &mut Coins`
 80// (the message's attached coins). It is decremented as each token order consumes
 81// its BaseAmount during verification.
 82type funds struct {
 83	coins map[string]chain.Coin
 84}
 85
 86// newFunds snapshots the attached coins into a mutable spend budget.
 87func newFunds(sent chain.Coins) *funds {
 88	f := &funds{coins: make(map[string]chain.Coin)}
 89
 90	for _, coin := range sent {
 91		f.coins[coin.Denom] = coin
 92	}
 93
 94	return f
 95}
 96
 97// sub deducts amount of denom from the attached funds, mirroring funds.sub(coin)
 98// in the reference contract. It errors when the funds do not cover the amount.
 99func (f *funds) sub(denom string, amount *u256.Uint) error {
100	want, err := amountInt64(amount)
101	if err != nil {
102		return err
103	}
104
105	coin, ok := f.coins[denom]
106	if !ok {
107		return makeError(errCoinMismatch)
108	}
109
110	if coin.Amount < want {
111		return makeError(errCoinMismatch)
112	}
113
114	coin.Amount -= want
115	f.coins[denom] = coin
116
117	return nil
118}
119
120// isEmpty reports whether every attached coin has been fully consumed.
121func (f *funds) isEmpty() bool {
122	for _, coin := range f.coins {
123		if coin.Amount != 0 {
124			return false
125		}
126	}
127
128	return true
129}
130
131// assertEmpty errors when any attached coin is left unconsumed.
132func (f *funds) assertEmpty() error {
133	if !f.isEmpty() {
134		return makeError(errCoinMismatch)
135	}
136
137	return nil
138}