Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

gnft.gno

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