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.

grc721_metadata.gno

4.52 Kb · 199 lines
  1package grc721v2
  2
  3import (
  4	"chain"
  5	"encoding/base64"
  6	"strings"
  7
  8	"gno.land/p/g1wx60nkcp5lfsxmedeg7sfs47tzxkath05u63t4/avl"
  9)
 10
 11type MetadataNFT struct {
 12	*BasicNFT
 13	extensions *avl.Tree // AVL tree for storing metadata extensions
 14}
 15
 16var _ IGRC721MetadataOnchain = (*MetadataNFT)(nil)
 17
 18func NewNFTWithMetadata(_ int, rlm realm, name, symbol string) *MetadataNFT {
 19	return &MetadataNFT{
 20		BasicNFT:   NewBasicNFT(0, rlm, name, symbol),
 21		extensions: avl.NewTree(),
 22	}
 23}
 24
 25func (s *MetadataNFT) SetTokenMetadata(caller address, tid TokenID, metadata Metadata) error {
 26	owner, err := s.OwnerOf(tid)
 27	if err != nil {
 28		return err
 29	}
 30	if caller != owner {
 31		return ErrCallerIsNotOwner
 32	}
 33
 34	s.extensions.Set(tid.String(), metadata)
 35
 36	chain.Emit(
 37		MetadataUpdateEvent,
 38		"token", s.ID(),
 39		"tokenId", tid.String(),
 40	)
 41
 42	return nil
 43}
 44
 45func (s *MetadataNFT) TokenMetadata(tid TokenID) (Metadata, error) {
 46	metadata := s.extensions.Get(tid.String())
 47	if metadata == nil {
 48		return Metadata{}, ErrInvalidTokenId
 49	}
 50
 51	return metadata.(Metadata), nil
 52}
 53
 54func (s *MetadataNFT) Name() string {
 55	return s.BasicNFT.Name()
 56}
 57
 58func (s *MetadataNFT) Symbol() string {
 59	return s.BasicNFT.Symbol()
 60}
 61
 62func (s *MetadataNFT) TokenCount() int64 {
 63	return s.BasicNFT.TokenCount()
 64}
 65
 66func (s *MetadataNFT) BalanceOf(addr address) (int64, error) {
 67	return s.BasicNFT.BalanceOf(addr)
 68}
 69
 70func (s *MetadataNFT) OwnerOf(tid TokenID) (address, error) {
 71	return s.BasicNFT.OwnerOf(tid)
 72}
 73
 74func (s *MetadataNFT) TokenURI(tid TokenID) (string, error) {
 75	metadata, err := s.TokenMetadata(tid)
 76	if err != nil {
 77		return "", err
 78	}
 79	return metadataDataURI(metadata), nil
 80}
 81
 82func jsonEscape(s string) string {
 83	var b strings.Builder
 84	for _, r := range s {
 85		switch r {
 86		case '\\':
 87			b.WriteString(`\\`)
 88		case '"':
 89			b.WriteString(`\"`)
 90		case '\n':
 91			b.WriteString(`\n`)
 92		case '\r':
 93			b.WriteString(`\r`)
 94		case '\t':
 95			b.WriteString(`\t`)
 96		default:
 97			b.WriteRune(r)
 98		}
 99	}
100	return b.String()
101}
102
103func metadataDataURI(m Metadata) string {
104	image := m.Image
105	if image == "" {
106		image = m.ImageData
107	}
108
109	var b strings.Builder
110	b.WriteString(`{"name":"`)
111	b.WriteString(jsonEscape(m.Name))
112	b.WriteString(`","description":"`)
113	b.WriteString(jsonEscape(m.Description))
114	b.WriteString(`","image":"`)
115	b.WriteString(jsonEscape(image))
116	b.WriteString(`"`)
117	if m.ExternalURL != "" {
118		b.WriteString(`,"external_url":"`)
119		b.WriteString(jsonEscape(m.ExternalURL))
120		b.WriteString(`"`)
121	}
122	if m.BackgroundColor != "" {
123		b.WriteString(`,"background_color":"`)
124		b.WriteString(jsonEscape(m.BackgroundColor))
125		b.WriteString(`"`)
126	}
127	if m.AnimationURL != "" {
128		b.WriteString(`,"animation_url":"`)
129		b.WriteString(jsonEscape(m.AnimationURL))
130		b.WriteString(`"`)
131	}
132	if m.YoutubeURL != "" {
133		b.WriteString(`,"youtube_url":"`)
134		b.WriteString(jsonEscape(m.YoutubeURL))
135		b.WriteString(`"`)
136	}
137	b.WriteString(`,"attributes":[`)
138	for i, t := range m.Attributes {
139		if i > 0 {
140			b.WriteString(",")
141		}
142		b.WriteString(`{"trait_type":"`)
143		b.WriteString(jsonEscape(t.TraitType))
144		b.WriteString(`","value":"`)
145		b.WriteString(jsonEscape(t.Value))
146		b.WriteString(`"}`)
147	}
148	b.WriteString("]}")
149
150	return "data:application/json;base64," + base64.StdEncoding.EncodeToString([]byte(b.String()))
151}
152
153func (s *MetadataNFT) SetTokenURI(caller address, tid TokenID, tURI TokenURI) (bool, error) {
154	return s.BasicNFT.SetTokenURI(caller, tid, tURI)
155}
156
157func (s *MetadataNFT) IsApprovedForAll(owner, operator address) bool {
158	return s.BasicNFT.IsApprovedForAll(owner, operator)
159}
160
161func (s *MetadataNFT) Approve(caller, to address, tid TokenID) error {
162	return s.BasicNFT.Approve(caller, to, tid)
163}
164
165func (s *MetadataNFT) GetApproved(tid TokenID) (address, error) {
166	return s.BasicNFT.GetApproved(tid)
167}
168
169func (s *MetadataNFT) SetApprovalForAll(caller, operator address, approved bool) error {
170	return s.BasicNFT.SetApprovalForAll(caller, operator, approved)
171}
172
173func (s *MetadataNFT) SafeTransferFrom(caller, from, to address, tid TokenID) error {
174	return s.BasicNFT.SafeTransferFrom(caller, from, to, tid)
175}
176
177func (s *MetadataNFT) TransferFrom(caller, from, to address, tid TokenID) error {
178	return s.BasicNFT.TransferFrom(caller, from, to, tid)
179}
180
181func (s *MetadataNFT) Mint(to address, tid TokenID) error {
182	return s.BasicNFT.Mint(to, tid)
183}
184
185func (s *MetadataNFT) SafeMint(to address, tid TokenID) error {
186	return s.BasicNFT.SafeMint(to, tid)
187}
188
189func (s *MetadataNFT) Burn(tid TokenID) error {
190	if err := s.BasicNFT.Burn(tid); err != nil {
191		return err
192	}
193	s.extensions.Remove(tid.String())
194	return nil
195}
196
197func (s *MetadataNFT) RenderHome() string {
198	return s.BasicNFT.RenderHome()
199}