types.gno
4.05 Kb · 92 lines
1// nsmarket — names for sale, without anybody holding anything.
2//
3// HOW A SALE WORKS, and why it is built this way.
4//
5// The seller keeps their name. Listing does not move it here: it records
6// an asking price and relies on the GRC-721 approval the seller has
7// already granted this realm. Ownership does not change until somebody
8// buys, and the seller can walk away at any moment by revoking the
9// approval — at which point the listing simply stops working.
10//
11// The buyer's money is never held either. Buy is ONE call: it checks the
12// listing still stands, moves the name, pays the seller, takes the fee,
13// refunds any overpayment. All of it inside the transaction the buyer
14// signed. If any part fails the whole thing reverts and nothing moved.
15//
16// Which means: at no point does this realm hold somebody else's name or
17// somebody else's money. That is not a nice-to-have. An escrow that
18// holds either one is a different kind of business, with a different set
19// of rules, run by people with licences.
20//
21// THIS REALM HAS NO VAULT PRIVILEGES. It is not in nsdata's trusted set
22// and does not ask to be. Every transfer goes through nslogic's ordinary
23// GRC-721 path, which means it can only move a name the owner has
24// explicitly approved — the same permission any other marketplace would
25// need. However wrong the code below turns out to be, the worst it can
26// do is fail.
27package nsmarket
28
29import (
30 "gno.land/p/nt/avl/v0"
31)
32
33// A listing. Kept small on purpose: the seller pays to store it, and
34// everything derivable is derived rather than copied. The seller's
35// address is here because the name can change hands outside this realm
36// and a stale seller must be caught, not trusted.
37type Listing struct {
38 seller address
39 price int64 // ugnot
40 listed int64 // unix seconds
41 /* PINNED TO ONE REGISTRATION, the same way nsskin pins a binding.
42 Without it a listing outlives the registration it was made
43 against: list at 50 GNOT having granted SetApprovalForAll, let the
44 name lapse, re-register it yourself — the burn clears the
45 per-token approval but NOT the operator grant, so owner matches,
46 IsNameValid is true, and any passer-by executes the year-old
47 listing against the name you just paid to get back. */
48 registered int64
49
50 /* PINNED TO ONE OWNERSHIP EPOCH as well as one registration.
51 The registration pin does not catch a name that was SOLD AND BOUGHT
52 BACK: owner matches, the name is valid, the registration date never
53 changed, and an operator grant (SetApprovalForAll) survives
54 transfers — so a seller who listed, sold elsewhere and later bought
55 the name back found their old listing executable by anyone at the old
56 price. The ledger's epoch moves on every transfer, so it catches it.
57 Zero means a listing written before this field, treated as unpinned. */
58 epoch int64
59}
60
61var (
62 // Keyed by token id, "label*domain".
63 listings avl.Tree
64 sold int64 // completed sales, for the honest version of a stat
65
66 /* This realm's own address, remembered by List. The reads need it to
67 ask whether the seller's approval still names this realm, and a read
68 has no `cur` to learn it from. Nothing can be listed before List
69 runs, so it is always set when a listing exists to be read. */
70 marketAddr address
71)
72
73/*
74WHO PAYS FOR A LISTING'S STORAGE, AND WHO GETS IT BACK. Measured on Pearl,
752026-09-10. The seller's List transaction pays the storage deposit for the
76listing. The chain refunds a deposit to whoever's transaction FREES the
77storage, so when Buy removes the listing the refund goes to the BUYER: the
78buyer's balance fell by 0.31 GNOT less than price plus gas. Unlist refunds
79the seller, and Prune refunds whoever prunes.
80
81This is gno's rule, not ours, and it cannot be routed elsewhere. The amount
82is small, but the site's quote should not promise a seller "price minus
83fee" as if listing were free: listing costs the deposit, recovered only by
84unlisting.
85*/
86
87const (
88 maxPage = 200
89 minPrice = int64(1) // zero would be a giveaway, not a sale
90 maxPrice = int64(1) << 52 // guards the fee arithmetic from overflow
91 ugnot = "ugnot"
92)