Search Apps Documentation Source Content File Folder Download Copy Actions Download

transfer.gno

2.26 Kb · 64 lines
 1package gnogle_nft
 2
 3import (
 4	"chain"
 5	"strconv"
 6
 7	"gno.land/p/g18wk4a80cr7dqa25vfka2yug5n3pd50udled6y3/grc721"
 8	"gno.land/p/nt/avl/v0"
 9)
10
11// operators holds realm-wide operator approvals: "owner:operator" -> bool.
12// One approval covers ALL of the owner's tokens across ALL collections — so a
13// user enables a marketplace once. The marketplace realm is just an operator.
14var operators avl.Tree
15
16func opKey(owner, operator address) string { return owner.String() + ":" + operator.String() }
17
18// SetApprovalForAll grants/revokes an operator (e.g. a marketplace realm) the
19// right to transfer all of the caller's tokens. This is how a user "enables
20// trading" with a marketplace — and how upgrading the market stays NFT-safe:
21// the new market is just a new operator to approve; tokens never move realms.
22func SetApprovalForAll(cur realm, operator address, approved bool) {
23	owner := cur.Previous().Address()
24	if !operator.IsValid() {
25		panic("invalid operator address")
26	}
27	operators.Set(opKey(owner, operator), approved)
28	chain.Emit("ApprovalForAll", "owner", owner.String(), "operator", operator.String(),
29		"approved", strconv.FormatBool(approved))
30}
31
32// IsApprovedForAll reports whether operator may move all of owner's tokens.
33func IsApprovedForAll(owner, operator address) bool {
34	v, ok := operators.Get(opKey(owner, operator))
35	return ok && v.(bool)
36}
37
38// TransferFrom moves a token from `from` to `to`. The caller
39// (cur.Previous().Address()) must be the owner OR an approved operator. This is
40// the single entry point the marketplace realm uses to settle trades.
41func TransferFrom(cur realm, collID string, from, to address, tokenID string) {
42	caller := cur.Previous().Address()
43	coll := mustGetCollection(collID)
44	tid := grc721.TokenID(tokenID)
45
46	owner, err := coll.nft.OwnerOf(tid)
47	if err != nil {
48		panic(err)
49	}
50	if owner != from {
51		panic("from is not the current owner")
52	}
53	if caller != from && !IsApprovedForAll(from, caller) {
54		panic("caller is not the owner or an approved operator")
55	}
56
57	// Authorized at the realm level; call grc721 with caller=owner so its own
58	// ownership check passes.
59	if err := coll.nft.TransferFrom(from, from, to, tid); err != nil {
60		panic(err)
61	}
62	chain.Emit("Transfer", "collection", collID, "tokenId", tokenID,
63		"from", from.String(), "to", to.String())
64}