1package nft
2
3import (
4 "std"
5 "strconv"
6
7 "gno.land/p/demo/avl"
8 "gno.land/p/demo/grc/grc721"
9)
10
11type token struct {
12 grc721.IGRC721 // implements the GRC721 interface
13
14 tokenCounter int
15 tokens avl.Tree // grc721.TokenID -> *NFToken{}
16 operators avl.Tree // owner std.Address -> operator std.Address
17}
18
19type NFToken struct {
20 owner std.Address
21 approved std.Address
22 tokenID grc721.TokenID
23 data string
24}
25
26var gToken = &token{}
27
28func GetToken() *token { return gToken }
29
30func (grc *token) nextTokenID() grc721.TokenID {
31 grc.tokenCounter++
32 s := strconv.Itoa(grc.tokenCounter)
33 return grc721.TokenID(s)
34}
35
36func (grc *token) getToken(tid grc721.TokenID) (*NFToken, bool) {
37 token, ok := grc.tokens.Get(string(tid))
38 if !ok {
39 return nil, false
40 }
41 return token.(*NFToken), true
42}
43
44func (grc *token) Mint(to std.Address, data string) grc721.TokenID {
45 tid := grc.nextTokenID()
46 grc.tokens.Set(string(tid), &NFToken{
47 owner: to,
48 tokenID: tid,
49 data: data,
50 })
51 return tid
52}
53
54func (grc *token) BalanceOf(owner std.Address) (count int64) {
55 panic("not yet implemented")
56}
57
58func (grc *token) OwnerOf(tid grc721.TokenID) std.Address {
59 token, ok := grc.getToken(tid)
60 if !ok {
61 panic("token does not exist")
62 }
63 return token.owner
64}
65
66// XXX not fully implemented yet.
67func (grc *token) SafeTransferFrom(from, to std.Address, tid grc721.TokenID) {
68 grc.TransferFrom(from, to, tid)
69 // When transfer is complete, this function checks if `_to` is a smart
70 // contract (code size > 0). If so, it calls `onERC721Received` on
71 // `_to` and throws if the return value is not
72 // `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
73 // XXX ensure "to" is a realm with onERC721Received() signature.
74}
75
76func (grc *token) TransferFrom(from, to std.Address, tid grc721.TokenID) {
77 caller := std.GetCallerAt(2)
78 token, ok := grc.getToken(tid)
79 // Throws if `_tokenId` is not a valid NFT.
80 if !ok {
81 panic("token does not exist")
82 }
83 // Throws unless `msg.sender` is the current owner, an authorized
84 // operator, or the approved address for this NFT.
85 if caller != token.owner && caller != token.approved {
86 operator, ok := grc.operators.Get(token.owner.String())
87 if !ok || caller != operator.(std.Address) {
88 panic("unauthorized")
89 }
90 }
91 // Throws if `_from` is not the current owner.
92 if from != token.owner {
93 panic("from is not the current owner")
94 }
95 // Throws if `_to` is the zero address.
96 if to == "" {
97 panic("to cannot be empty")
98 }
99 // Good.
100 token.owner = to
101}
102
103func (grc *token) Approve(approved std.Address, tid grc721.TokenID) {
104 caller := std.GetCallerAt(2)
105 token, ok := grc.getToken(tid)
106 // Throws if `_tokenId` is not a valid NFT.
107 if !ok {
108 panic("token does not exist")
109 }
110 // Throws unless `msg.sender` is the current owner,
111 // or an authorized operator.
112 if caller != token.owner {
113 operator, ok := grc.operators.Get(token.owner.String())
114 if !ok || caller != operator.(std.Address) {
115 panic("unauthorized")
116 }
117 }
118 // Good.
119 token.approved = approved
120}
121
122// XXX make it work for set of operators.
123func (grc *token) SetApprovalForAll(operator std.Address, approved bool) {
124 caller := std.GetCallerAt(2)
125 grc.operators.Set(caller.String(), operator)
126}
127
128func (grc *token) GetApproved(tid grc721.TokenID) std.Address {
129 token, ok := grc.getToken(tid)
130 // Throws if `_tokenId` is not a valid NFT.
131 if !ok {
132 panic("token does not exist")
133 }
134 return token.approved
135}
136
137// XXX make it work for set of operators
138func (grc *token) IsApprovedForAll(owner, operator std.Address) bool {
139 operator2, ok := grc.operators.Get(owner.String())
140 if !ok {
141 return false
142 }
143 return operator == operator2.(std.Address)
144}
nft.gno
3.58 Kb ยท 144 lines