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}