token.gno
2.36 Kb · 102 lines
1package metadata
2
3import (
4 "chain"
5
6 "gno.land/p/nt/grc721/v0"
7)
8
9const Kind = "metadata"
10
11// NewMetadata attaches a metadata extension and registers it as a core Extension.
12// Attach it before the first mint so every token is indexed.
13func NewMetadata(coreLedger *grc721.PrivateLedger) (*Metadata, *Ledger) {
14 if coreLedger == nil {
15 panic("metadata: nil core ledger")
16 }
17
18 st := &storage{}
19 core := coreLedger.ReadToken()
20 led := &Ledger{core: core, st: st}
21 coreLedger.RegisterExtension(led)
22
23 return &Metadata{core: core, st: st}, led
24}
25
26func (m *Metadata) ExtensionKind() string { return Kind }
27
28func (m *Metadata) TokenID() string { return m.core.ID() }
29
30func (m *Metadata) TokenURI(tid grc721.TokenID) (string, error) {
31 v := m.st.uris.Get(tid.String())
32 if v == nil {
33 return "", ErrNoTokenURI
34 }
35
36 return v.(string), nil
37}
38
39func (m *Metadata) TokenMetadata(tid grc721.TokenID) (Data, error) {
40 v := m.st.data.Get(tid.String())
41 if v == nil {
42 return Data{}, ErrNoMetadata
43 }
44
45 return v.(Data), nil
46}
47
48func (m *Metadata) HasTokenURI(tid grc721.TokenID) bool {
49 return m.st.uris.Has(tid.String())
50}
51
52func (m *Metadata) HasMetadata(tid grc721.TokenID) bool {
53 return m.st.data.Has(tid.String())
54}
55
56// SetTokenURI sets the EIP-721 tokenURI of tid; issuer-only so a holder cannot
57// rewrite the URI after acquisition.
58func (led *Ledger) SetTokenURI(tid grc721.TokenID, uri string) error {
59 if _, err := led.core.OwnerOf(tid); err != nil {
60 return ErrTokenNotMinted
61 }
62
63 led.st.uris.Set(tid.String(), uri)
64
65 chain.Emit(
66 TokenURIUpdateEvent,
67 "token", led.core.ID(),
68 "tokenId", tid.String(),
69 "uri", uri,
70 )
71
72 return nil
73}
74
75// SetTokenMetadata sets the OpenSea on-chain metadata of tid; issuer-only.
76func (led *Ledger) SetTokenMetadata(tid grc721.TokenID, data Data) error {
77 if _, err := led.core.OwnerOf(tid); err != nil {
78 return ErrTokenNotMinted
79 }
80
81 led.st.data.Set(tid.String(), data)
82
83 chain.Emit(
84 MetadataUpdateEvent,
85 "token", led.core.ID(),
86 "tokenId", tid.String(),
87 )
88
89 return nil
90}
91
92func (led *Ledger) ExtensionKind() string { return Kind }
93
94func (led *Ledger) OnMint(to address, tid grc721.TokenID) {}
95func (led *Ledger) OnTransfer(from, to address, tid grc721.TokenID) {}
96
97// OnBurn clears per-token state so it cannot resurface on re-mint.
98func (led *Ledger) OnBurn(tid grc721.TokenID) {
99 tidStr := tid.String()
100 led.st.uris.Remove(tidStr)
101 led.st.data.Remove(tidStr)
102}