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

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

grc1155.gno

3.19 Kb · 134 lines
  1// Package grc1155 is an edition ledger. One id has a balance per address.
  2// The realm that calls New owns every later Mint and Move. A caller address
  3// is not an argument: the home check is the realm value itself.
  4package grc1155
  5
  6import (
  7	"chain"
  8	"math/overflow"
  9	"strconv"
 10
 11	"gno.land/p/nt/avl/v0"
 12)
 13
 14// Ledger is stored by the creating realm. Do not export it from that realm.
 15type Ledger struct {
 16	home   string
 17	bal    avl.Tree // id|address -> int64
 18	supply avl.Tree // id -> int64
 19}
 20
 21// New binds the ledger to rlm. The realm is not the first argument, so this
 22// stays a normal call and home stays the creating realm. rlm must be current.
 23func New(_ int, rlm realm) *Ledger {
 24	if !rlm.IsCurrent() {
 25		panic("grc1155: spoofed realm")
 26	}
 27	home := rlm.PkgPath()
 28	if home == "" {
 29		panic("grc1155: home")
 30	}
 31	return &Ledger{home: home}
 32}
 33
 34// Mint adds amount of id to to. from is empty in the event.
 35func (led *Ledger) Mint(_ int, rlm realm, to address, id string, amount int64) {
 36	led.guard(0, rlm)
 37	if !to.IsValid() {
 38		panic("grc1155: address")
 39	}
 40	if !validID(id) || amount <= 0 {
 41		panic("grc1155: amount")
 42	}
 43	next := overflow.Add64p(led.Supply(id), amount)
 44	led.supply.Set(id, next)
 45	key := balKey(id, to)
 46	led.bal.Set(key, overflow.Add64p(led.Balance(to, id), amount))
 47	emit(address(""), to, id, amount)
 48}
 49
 50// Move sends amount of id from from to to. Both addresses must already be valid.
 51func (led *Ledger) Move(_ int, rlm realm, from, to address, id string, amount int64) {
 52	led.guard(0, rlm)
 53	if !from.IsValid() || !to.IsValid() || from == to {
 54		panic("grc1155: address")
 55	}
 56	if !validID(id) || amount <= 0 {
 57		panic("grc1155: amount")
 58	}
 59	if led.supply.Get(id) == nil {
 60		panic("grc1155: unknown id")
 61	}
 62	have := led.Balance(from, id)
 63	if have < amount {
 64		panic("grc1155: balance")
 65	}
 66	left := overflow.Sub64p(have, amount)
 67	if left == 0 {
 68		led.bal.Remove(balKey(id, from))
 69	} else {
 70		led.bal.Set(balKey(id, from), left)
 71	}
 72	led.bal.Set(balKey(id, to), overflow.Add64p(led.Balance(to, id), amount))
 73	emit(from, to, id, amount)
 74}
 75
 76// Balance is the free or escrowed amount of id held by addr. Unknown pairs are 0.
 77func (led *Ledger) Balance(addr address, id string) int64 {
 78	if led == nil || !addr.IsValid() || !validID(id) {
 79		return 0
 80	}
 81	v := led.bal.Get(balKey(id, addr))
 82	if v == nil {
 83		return 0
 84	}
 85	return v.(int64)
 86}
 87
 88// Supply is the minted amount of id. An unknown id is 0.
 89func (led *Ledger) Supply(id string) int64 {
 90	if led == nil || !validID(id) {
 91		return 0
 92	}
 93	v := led.supply.Get(id)
 94	if v == nil {
 95		return 0
 96	}
 97	return v.(int64)
 98}
 99
100func (led *Ledger) guard(_ int, rlm realm) {
101	if led == nil || !rlm.IsCurrent() || rlm.PkgPath() != led.home {
102		panic("grc1155: home")
103	}
104}
105
106func balKey(id string, addr address) string {
107	return id + "|" + addr.String()
108}
109
110func validID(id string) bool {
111	if len(id) < 1 || len(id) > 16 {
112		return false
113	}
114	if id[0] < 'a' || id[0] > 'z' {
115		return false
116	}
117	for i := 0; i < len(id); i++ {
118		c := id[i]
119		if (c < 'a' || c > 'z') && (c < '0' || c > '9') {
120			return false
121		}
122	}
123	return true
124}
125
126func emit(from, to address, id string, amount int64) {
127	chain.Emit(
128		"EditionTransfer",
129		"from", from.String(),
130		"to", to.String(),
131		"id", id,
132		"amount", strconv.FormatInt(amount, 10),
133	)
134}