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

token.gno

3.45 Kb · 161 lines
  1package enumerable
  2
  3import (
  4	"gno.land/p/nt/grc721/v0"
  5)
  6
  7const Kind = "enumerable"
  8
  9// NewEnumerable attaches an enumerable extension and registers it as a core
 10// Extension. Attach it before the first mint so every token is indexed.
 11func NewEnumerable(coreLedger *grc721.PrivateLedger) (*Enumerable, *Ledger) {
 12	if coreLedger == nil {
 13		panic("enumerable: nil core ledger")
 14	}
 15
 16	st := &storage{}
 17	core := coreLedger.ReadToken()
 18	led := &Ledger{core: core, st: st}
 19	coreLedger.RegisterExtension(led)
 20
 21	return &Enumerable{core: core, st: st}, led
 22}
 23
 24func (e *Enumerable) ExtensionKind() string {
 25	return Kind
 26}
 27
 28func (e *Enumerable) TokenID() string {
 29	return e.core.ID()
 30}
 31
 32func (e *Enumerable) TotalSupply() int64 {
 33	return int64(len(e.st.allTokens))
 34}
 35
 36func (e *Enumerable) TokenByIndex(index int64) (grc721.TokenID, error) {
 37	if index < 0 || index >= int64(len(e.st.allTokens)) {
 38		return "", ErrIndexOutOfRange
 39	}
 40
 41	return e.st.allTokens[index], nil
 42}
 43
 44func (e *Enumerable) TokenOfOwnerByIndex(owner address, index int64) (grc721.TokenID, error) {
 45	list := e.ownedList(owner)
 46	if list == nil || index < 0 || index >= int64(len(list.ids)) {
 47		return "", ErrIndexOutOfRange
 48	}
 49
 50	return list.ids[index], nil
 51}
 52
 53func (e *Enumerable) ownedList(owner address) *tokenList {
 54	v := e.st.owned.Get(owner.String())
 55	if v == nil {
 56		return nil
 57	}
 58
 59	return v.(*tokenList)
 60}
 61
 62func (led *Ledger) ExtensionKind() string { return Kind }
 63
 64func (led *Ledger) OnMint(to address, tid grc721.TokenID) {
 65	led.addToAll(tid)
 66	led.addToOwner(to, tid)
 67}
 68
 69func (led *Ledger) OnTransfer(from, to address, tid grc721.TokenID) {
 70	led.removeFromOwner(from, tid)
 71	led.addToOwner(to, tid)
 72}
 73
 74func (led *Ledger) OnBurn(tid grc721.TokenID) {
 75	if v := led.st.owner.Get(tid.String()); v != nil {
 76		led.removeFromOwner(v.(address), tid)
 77	}
 78
 79	led.removeFromAll(tid)
 80}
 81
 82func (led *Ledger) addToAll(tid grc721.TokenID) {
 83	led.st.allIndex.Set(tid.String(), len(led.st.allTokens))
 84	led.st.allTokens = append(led.st.allTokens, tid)
 85}
 86
 87// Swap-and-pop: move the last id into the freed slot, then truncate.
 88func (led *Ledger) removeFromAll(tid grc721.TokenID) {
 89	tidStr := tid.String()
 90
 91	v := led.st.allIndex.Get(tidStr)
 92	if v == nil {
 93		return
 94	}
 95
 96	idx := v.(int)
 97
 98	last := len(led.st.allTokens) - 1
 99	if idx != last {
100		moved := led.st.allTokens[last]
101		led.st.allTokens[idx] = moved
102		led.st.allIndex.Set(moved.String(), idx)
103	}
104
105	led.st.allTokens = led.st.allTokens[:last]
106
107	led.st.allIndex.Remove(tidStr)
108}
109
110func (led *Ledger) addToOwner(owner address, tid grc721.TokenID) {
111	ownerStr := owner.String()
112
113	var list *tokenList
114	if v := led.st.owned.Get(ownerStr); v != nil {
115		list = v.(*tokenList)
116	} else {
117		list = &tokenList{}
118		led.st.owned.Set(ownerStr, list)
119	}
120
121	led.st.ownedIndex.Set(tid.String(), len(list.ids))
122	list.ids = append(list.ids, tid)
123	led.st.owner.Set(tid.String(), owner)
124}
125
126// Swap-and-pop within the owner list; drops the owner entry when it empties.
127func (led *Ledger) removeFromOwner(owner address, tid grc721.TokenID) {
128	ownerStr := owner.String()
129
130	v := led.st.owned.Get(ownerStr)
131	if v == nil {
132		return
133	}
134
135	list := v.(*tokenList)
136	tidStr := tid.String()
137
138	iv := led.st.ownedIndex.Get(tidStr)
139	if iv == nil {
140		return
141	}
142
143	idx := iv.(int)
144
145	last := len(list.ids) - 1
146	if idx != last {
147		moved := list.ids[last]
148		list.ids[idx] = moved
149		led.st.ownedIndex.Set(moved.String(), idx)
150	}
151
152	list.ids = list.ids[:last]
153
154	led.st.ownedIndex.Remove(tidStr)
155
156	if len(list.ids) == 0 {
157		led.st.owned.Remove(ownerStr)
158	}
159
160	led.st.owner.Remove(tidStr)
161}