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.17 Kb · 273 lines
  1package gnft
  2
  3import (
  4	"chain"
  5	"errors"
  6
  7	"gno.land/p/gnoswap/deps/tokens/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(tid), 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	assertIsNonEmptyTokenURI(tURI)
 84	assertIsValidTokenURI(tid)
 85
 86	checkErr(setTokenURI(0, cur, tid, tURI))
 87
 88	return true, nil
 89}
 90
 91// SafeTransferFrom transfers token ownership with receiver validation.
 92//
 93// Parameters:
 94//   - from: current owner address
 95//   - to: recipient address
 96//   - tid: token ID to transfer
 97//
 98// Returns error if transfer fails.
 99//
100// Permission model:
101//   - Tokens held by the staker contract (i.e. currently staked) can only be
102//     moved by the staker itself; the underlying staked LP position is
103//     non-transferable.
104//   - Otherwise, ownership and approval are enforced by the GRC721 layer
105//     (owner / approved-for-token / approved-for-all).
106func SafeTransferFrom(cur realm, from, to address, tid grc721.TokenID) error {
107	assertFromIsValidAddress(from)
108	assertToIsValidAddress(to)
109	caller := cur.Previous().Address()
110	assertIsAllowedTransfer(caller, tid)
111
112	err := nft.SafeTransferFrom(caller, from, to, tid)
113	checkTransferErr(err, caller, from, to, tid)
114	return nil
115}
116
117// TransferFrom transfers a token from one address to another.
118//
119// Parameters:
120//   - from: current owner address
121//   - to: recipient address
122//   - tid: token ID
123//
124// Returns error if transfer fails.
125//
126// Permission model:
127//   - Tokens held by the staker contract (i.e. currently staked) can only be
128//     moved by the staker itself; the underlying staked LP position is
129//     non-transferable.
130//   - Otherwise, ownership and approval are enforced by the GRC721 layer
131//     (owner / approved-for-token / approved-for-all).
132func TransferFrom(cur realm, from, to address, tid grc721.TokenID) error {
133	assertFromIsValidAddress(from)
134	assertToIsValidAddress(to)
135	caller := cur.Previous().Address()
136	assertIsAllowedTransfer(caller, tid)
137
138	err := nft.TransferFrom(caller, from, to, tid)
139	checkTransferErr(err, caller, from, to, tid)
140	return nil
141}
142
143// Approve grants permission to transfer a specific token ID to another address.
144//
145// Parameters:
146//   - approved: address to approve
147//   - tid: token ID to approve for transfer
148//
149// Returns error if approval fails.
150func Approve(cur realm, approved address, tid grc721.TokenID) error {
151	assertIsValidAddress(approved)
152
153	caller := cur.Previous().Address()
154	err := nft.Approve(caller, approved, tid)
155	checkApproveErr(err, caller, approved, tid)
156	return nil
157}
158
159// SetApprovalForAll enables/disables operator approval for all tokens.
160//
161// Parameters:
162//   - operator: address to set approval for
163//   - approved: true to approve, false to revoke
164//
165// Returns error if operation fails.
166func SetApprovalForAll(cur realm, operator address, approved bool) error {
167	assertIsValidAddress(operator)
168
169	checkErr(nft.SetApprovalForAll(cur.Previous().Address(), operator, approved))
170	return nil
171}
172
173// GetApproved returns approved address for token ID.
174//
175// Parameters:
176//   - tid: token ID to check
177//
178// Returns approved address and error if token doesn't exist.
179func GetApproved(tid grc721.TokenID) (address, error) {
180	return nft.GetApproved(tid)
181}
182
183// IsApprovedForAll checks if operator can manage all owner's tokens.
184//
185// Parameters:
186//   - owner: token owner address
187//   - operator: operator address to check
188//
189// Returns true if operator is approved for all owner's tokens.
190func IsApprovedForAll(owner, operator address) bool {
191	return nft.IsApprovedForAll(owner, operator)
192}
193
194// Mint creates new NFT and transfers to address.
195//
196// Parameters:
197//   - to: recipient address
198//   - tid: token ID
199//
200// Returns minted token ID.
201// Only callable by position contract.
202func Mint(cur realm, to address, tid grc721.TokenID) grc721.TokenID {
203	caller := cur.Previous().Address()
204	access.AssertIsPosition(caller)
205
206	positionAddr := access.MustGetAddress(prabc.ROLE_POSITION.String())
207	checkErr(nft.Mint(positionAddr, tid))
208
209	// Store only the gradient parameters instead of full base64 SVG to reduce storage costs.
210	// Parameters are converted to full SVG on read via TokenURI().
211	imageParams := genImageParamsString(generateRandInstance())
212	checkErr(setTokenURI(0, cur, tid, grc721.TokenURI(imageParams)))
213
214	checkErr(nft.TransferFrom(positionAddr, positionAddr, to, tid))
215
216	return tid
217}
218
219// Exists checks if token ID exists.
220func Exists(tid grc721.TokenID) bool {
221	_, err := nft.OwnerOf(tid)
222	return err == nil
223}
224
225// Burn removes a specific token ID.
226//
227// Parameters:
228//   - tid: token ID to burn
229//
230// Only callable by position.
231func Burn(cur realm, tid grc721.TokenID) {
232	caller := cur.Previous().Address()
233	access.AssertIsPosition(caller)
234
235	checkErr(nft.Burn(tid))
236}
237
238// Render returns the HTML representation of the NFT.
239func Render(path string) string {
240	if path == "" {
241		return nft.RenderHome()
242	}
243	return "404\n"
244}
245
246// setTokenURI sets the metadata URI for a specific token ID.
247func setTokenURI(_ int, rlm realm, tid grc721.TokenID, tURI grc721.TokenURI) error {
248	if !rlm.IsCurrent() {
249		return errors.New(errSpoofedRealm)
250	}
251
252	previousRealm := rlm.Previous()
253	previousAddr := previousRealm.Address()
254
255	_, err := nft.SetTokenURI(previousAddr, tid, tURI)
256	if err != nil {
257		return makeErrorWithDetails(err.Error(), ufmt.Sprintf("token id (%s)", tid))
258	}
259	tokenURI, err := TokenURI(tid)
260	if err != nil {
261		return makeErrorWithDetails(err.Error(), ufmt.Sprintf("token id (%s)", tid))
262	}
263
264	chain.Emit(
265		"SetTokenURI",
266		"prevAddr", previousAddr.String(),
267		"prevRealm", previousRealm.PkgPath(),
268		"tokenId", string(tid),
269		"tokenURI", tokenURI,
270	)
271
272	return nil
273}