offer.gno
2.45 Kb · 79 lines
1package gnogle_market2
2
3import (
4 "chain"
5 "chain/runtime"
6 "strconv"
7
8 "gno.land/p/nt/avl/v0"
9 nft "gno.land/r/g18wk4a80cr7dqa25vfka2yug5n3pd50udled6y3/gnogle_nft2"
10)
11
12// Offer is a standing bid on a token (listed or not). The offered GNOT is
13// escrowed in the market until accepted or withdrawn.
14type Offer struct {
15 collID string
16 tokenID string
17 buyer address
18 amount int64
19 madeAt int64
20}
21
22var offers avl.Tree // "collID/tokenID/buyer" -> *Offer
23
24func offerKey(collID, tokenID string, buyer address) string {
25 return collID + "/" + tokenID + "/" + buyer.String()
26}
27
28// MakeOffer escrows GNOT as an offer on a token. Re-offering replaces (refunds)
29// the caller's previous offer.
30func MakeOffer(cur realm, collID, tokenID string) {
31 assertUserCall(cur)
32 buyer := cur.Previous().Address()
33 amount := receivedUgnot()
34 if amount <= 0 {
35 panic("an offer must include a positive ugnot amount")
36 }
37 if nft.OwnerOf(collID, tokenID) == buyer {
38 panic("you already own this token")
39 }
40 k := offerKey(collID, tokenID, buyer)
41 if v, ok := offers.Get(k); ok {
42 payout(cur, buyer, v.(*Offer).amount)
43 }
44 offers.Set(k, &Offer{collID, tokenID, buyer, amount, runtime.ChainHeight()})
45 chain.Emit("OfferMade", "collection", collID, "tokenId", tokenID, "buyer", buyer.String(), "amount", strconv.FormatInt(amount, 10))
46}
47
48// CancelOffer withdraws the caller's offer and refunds the escrow.
49func CancelOffer(cur realm, collID, tokenID string) {
50 assertUserCall(cur)
51 buyer := cur.Previous().Address()
52 k := offerKey(collID, tokenID, buyer)
53 v, ok := offers.Get(k)
54 if !ok {
55 panic("you have no offer on this token")
56 }
57 o := v.(*Offer)
58 offers.Remove(k)
59 payout(cur, buyer, o.amount)
60 chain.Emit("OfferCancelled", "collection", collID, "tokenId", tokenID, "buyer", buyer.String())
61}
62
63// AcceptOffer lets the current owner accept a buyer's offer. The owner must hold
64// the token and have approved this market.
65func AcceptOffer(cur realm, collID, tokenID string, buyer address) {
66 assertUserCall(cur)
67 owner := cur.Previous().Address()
68 requireOwnerAndApproval(cur, collID, tokenID, owner)
69 k := offerKey(collID, tokenID, buyer)
70 v, ok := offers.Get(k)
71 if !ok {
72 panic("no such offer")
73 }
74 o := v.(*Offer)
75 offers.Remove(k)
76 nft.TransferFrom(cross(cur), collID, owner, buyer, tokenID)
77 settle(cur, collID, tokenID, owner, buyer, o.amount, "offer")
78 chain.Emit("OfferAccepted", "collection", collID, "tokenId", tokenID, "buyer", buyer.String(), "seller", owner.String(), "amount", strconv.FormatInt(o.amount, 10))
79}