Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}