gnft.gno
6.60 Kb ยท 267 lines
1package gnft
2
3import (
4 "chain"
5 "chain/runtime"
6
7 "gno.land/p/demo/tokens/grc721"
8 "gno.land/p/nt/ufmt"
9 "gno.land/r/gnoswap/access"
10 "gno.land/r/gnoswap/halt"
11
12 prabc "gno.land/p/gnoswap/rbac"
13 _ "gno.land/r/gnoswap/rbac"
14)
15
16var nft = grc721.NewBasicNFT("GNOSWAP NFT", "GNFT")
17
18// Name returns the NFT collection name.
19func Name() string {
20 return nft.Name()
21}
22
23// Symbol returns the NFT symbol.
24func Symbol() string {
25 return nft.Symbol()
26}
27
28// TotalSupply returns the total number of NFTs minted.
29func TotalSupply() int64 {
30 return nft.TokenCount()
31}
32
33// TokenURI returns the metadata URI for the specified token ID.
34// If stored value is in parameter format (x1,y1,x2,y2,color1,color2),
35// it converts to full base64-encoded SVG image URI on read.
36func TokenURI(tid grc721.TokenID) (string, error) {
37 stored, err := nft.TokenURI(tid)
38 if err != nil {
39 return "", err
40 }
41
42 params, err := parseImageParams(stored)
43 if err == nil {
44 return params.generateImageURI(), nil
45 }
46
47 return stored, nil
48}
49
50// BalanceOf returns the number of NFTs owned by the specified address.
51func BalanceOf(owner address) (int64, error) {
52 assertIsValidAddress(owner)
53 return nft.BalanceOf(owner)
54}
55
56// OwnerOf returns the owner address for the specified token ID.
57func OwnerOf(tid grc721.TokenID) (address, error) {
58 return nft.OwnerOf(tid)
59}
60
61// MustOwnerOf returns the owner address for the specified token ID.
62// It panics if the token ID is invalid.
63func MustOwnerOf(tid grc721.TokenID) address {
64 ownerAddr, err := nft.OwnerOf(tid)
65 checkErr(err)
66 return ownerAddr
67}
68
69// SetTokenURI sets the metadata URI for the specified token.
70//
71// Parameters:
72// - tid: token ID
73// - tURI: token URI
74//
75// Only callable by position contract.
76func SetTokenURI(cur realm, tid grc721.TokenID, tURI grc721.TokenURI) (bool, error) {
77 halt.AssertIsNotHaltedPosition()
78
79 caller := runtime.PreviousRealm().Address()
80 access.AssertIsPosition(caller)
81
82 assertIsValidTokenURI(tid)
83
84 checkErr(setTokenURI(tid, tURI))
85
86 return true, nil
87}
88
89// SafeTransferFrom transfers token ownership with receiver validation.
90//
91// Parameters:
92// - from: current owner address
93// - to: recipient address
94// - tid: token ID to transfer
95//
96// Returns error if transfer fails.
97// Only callable by staker contract.
98func SafeTransferFrom(cur realm, from, to address, tid grc721.TokenID) error {
99 halt.AssertIsNotHaltedPosition()
100
101 caller := runtime.PreviousRealm().Address()
102 access.AssertIsStaker(caller)
103
104 assertFromIsValidAddress(from)
105 assertToIsValidAddress(to)
106
107 err := nft.SafeTransferFrom(from, to, tid)
108 checkTransferErr(err, from, to, tid)
109 return nil
110}
111
112// TransferFrom transfers a token from one address to another.
113//
114// Parameters:
115// - from: current owner address
116// - to: recipient address
117// - tid: token ID
118//
119// Returns error if transfer fails.
120// Only callable by staker contract.
121func TransferFrom(cur realm, from, to address, tid grc721.TokenID) error {
122 halt.AssertIsNotHaltedPosition()
123
124 caller := runtime.PreviousRealm().Address()
125 access.AssertIsStaker(caller)
126
127 assertFromIsValidAddress(from)
128 assertToIsValidAddress(to)
129
130 err := nft.TransferFrom(from, to, tid)
131 checkTransferErr(err, from, to, tid)
132 return nil
133}
134
135// Approve grants permission to transfer a specific token ID to another address.
136//
137// Parameters:
138// - approved: address to approve
139// - tid: token ID to approve for transfer
140//
141// Returns error if approval fails.
142// Only callable when not halted.
143func Approve(cur realm, approved address, tid grc721.TokenID) error {
144 halt.AssertIsNotHaltedPosition()
145 assertIsValidAddress(approved)
146
147 err := nft.Approve(approved, tid)
148 checkApproveErr(err, approved, tid)
149 return nil
150}
151
152// SetApprovalForAll enables/disables operator approval for all tokens.
153//
154// Parameters:
155// - operator: address to set approval for
156// - approved: true to approve, false to revoke
157//
158// Returns error if operation fails.
159// Only callable when not halted.
160func SetApprovalForAll(cur realm, operator address, approved bool) error {
161 halt.AssertIsNotHaltedPosition()
162 assertIsValidAddress(operator)
163
164 checkErr(nft.SetApprovalForAll(operator, approved))
165 return nil
166}
167
168// GetApproved returns approved address for token ID.
169//
170// Parameters:
171// - tid: token ID to check
172//
173// Returns approved address and error if token doesn't exist.
174func GetApproved(tid grc721.TokenID) (address, error) {
175 return nft.GetApproved(tid)
176}
177
178// IsApprovedForAll checks if operator can manage all owner's tokens.
179//
180// Parameters:
181// - owner: token owner address
182// - operator: operator address to check
183//
184// Returns true if operator is approved for all owner's tokens.
185func IsApprovedForAll(owner, operator address) bool {
186 return nft.IsApprovedForAll(owner, operator)
187}
188
189// Mint creates new NFT and transfers to address.
190//
191// Parameters:
192// - to: recipient address
193// - tid: token ID
194//
195// Returns minted token ID.
196// Only callable by position contract.
197func Mint(cur realm, to address, tid grc721.TokenID) grc721.TokenID {
198 halt.AssertIsNotHaltedPosition()
199
200 caller := runtime.PreviousRealm().Address()
201 access.AssertIsPosition(caller)
202
203 positionAddr := access.MustGetAddress(prabc.ROLE_POSITION.String())
204 checkErr(nft.Mint(positionAddr, tid))
205
206 // Store only the gradient parameters instead of full base64 SVG to reduce storage costs.
207 // Parameters are converted to full SVG on read via TokenURI().
208 imageParams := genImageParamsString(generateRandInstance())
209 checkErr(setTokenURI(tid, grc721.TokenURI(imageParams)))
210
211 checkErr(nft.TransferFrom(positionAddr, to, tid))
212
213 return tid
214}
215
216// Exists checks if token ID exists.
217func Exists(tid grc721.TokenID) bool {
218 _, err := nft.OwnerOf(tid)
219 return err == nil
220}
221
222// Burn removes a specific token ID.
223//
224// Parameters:
225// - tid: token ID to burn
226//
227// Only callable by position.
228func Burn(cur realm, tid grc721.TokenID) {
229 halt.AssertIsNotHaltedPosition()
230
231 caller := runtime.PreviousRealm().Address()
232 access.AssertIsPosition(caller)
233
234 checkErr(nft.Burn(tid))
235}
236
237// Render returns the HTML representation of the NFT.
238func Render(path string) string {
239 if path == "" {
240 return nft.RenderHome()
241 }
242 return "404\n"
243}
244
245// setTokenURI sets the metadata URI for a specific token ID.
246func setTokenURI(tid grc721.TokenID, tURI grc721.TokenURI) error {
247 _, err := nft.SetTokenURI(tid, tURI)
248 if err != nil {
249 return makeErrorWithDetails(err, ufmt.Sprintf("token id (%s)", tid))
250 }
251
252 tokenURI, err := TokenURI(tid)
253 if err != nil {
254 return makeErrorWithDetails(err, ufmt.Sprintf("token id (%s)", tid))
255 }
256
257 previousRealm := runtime.PreviousRealm()
258 chain.Emit(
259 "SetTokenURI",
260 "prevAddr", previousRealm.Address().String(),
261 "prevRealm", previousRealm.PkgPath(),
262 "tokenId", string(tid),
263 "tokenURI", tokenURI,
264 )
265
266 return nil
267}