getters.gno
3.74 Kb · 107 lines
1package memba_nft_market_v3_2
2
3// v3.1 — structured read getters.
4//
5// These let the frontend read listings and offers STRUCTURALLY (full addresses,
6// deterministic format) instead of scraping the markdown Render(""), which truncated
7// addresses and capped at 50 rows. Output is one record per line, pipe-delimited, so
8// the client parses by split — no markdown regex, no truncation.
9
10import (
11 "strings"
12
13 "chain/runtime/unsafe"
14)
15
16// MaxPageSize bounds a single page so a getter can never iterate unboundedly (a
17// gas/DoS surface). The frontend pages with offset/limit.
18const MaxPageSize = 100
19
20// MarketAddress returns this engine realm's own address — the operator a seller
21// approves on the collection (SetApprovalForAll) and the address the collection
22// registers via RegisterMarket. Exposed so the frontend and the deploy ceremony can
23// read the deterministic address directly instead of hardcoding it.
24func MarketAddress() string { return unsafe.CurrentRealm().Address().String() }
25
26// ── NF-2 solvency getters (MAINNET_READINESS §3) ─────────────────────────────
27//
28// The offer escrow makes this a pooled-fund realm: without a machine-readable
29// liabilities figure the solvency monitor can only do a balance-only heuristic.
30// Invariant the monitor reconciles: realm balance >= TotalLiabilities().
31
32// TotalLiabilities returns the total ugnot the realm owes to offer escrows.
33// Maintained in lockstep with every escrow mutation (see offers.gno).
34func TotalLiabilities() int64 { return totalEscrowed }
35
36// RealmAddress is the NF-2 canonical name for the realm's own address (the
37// monitor pairs it with TotalLiabilities). Same value as MarketAddress.
38func RealmAddress() string { return MarketAddress() }
39
40// ListingsCount returns the number of active listings (for pagination).
41func ListingsCount() int { return len(listingOrder) }
42
43// GetListingsPage returns a page of active listings, one per line:
44//
45// collectionID|tokenID|seller|price|createdBlk
46//
47// Addresses are FULL (never truncated). offset/limit window the insertion-ordered
48// listing set; limit is clamped to [1, MaxPageSize].
49func GetListingsPage(offset, limit int) string {
50 if offset < 0 {
51 offset = 0
52 }
53 if limit <= 0 || limit > MaxPageSize {
54 limit = MaxPageSize
55 }
56 n := len(listingOrder)
57 end := offset + limit
58 if end > n {
59 end = n
60 }
61
62 var sb strings.Builder
63 for i := offset; i < end; i++ {
64 v, ok := listings.Get(listingOrder[i])
65 if !ok {
66 continue
67 }
68 l := v.(*Listing)
69 sb.WriteString(l.CollectionID)
70 sb.WriteString("|")
71 sb.WriteString(l.TokenID)
72 sb.WriteString("|")
73 sb.WriteString(l.Seller.String())
74 sb.WriteString("|")
75 sb.WriteString(itoa(l.Price))
76 sb.WriteString("|")
77 sb.WriteString(itoa(l.CreatedBlk))
78 sb.WriteString("\n")
79 }
80 return sb.String()
81}
82
83// GetOffersForToken returns the active offers on one (collection, token), one per line:
84//
85// buyer|amount|createdBlk
86//
87// It uses a PREFIX-RANGE scan over the offer tree — offerKey is "collection:token:buyer",
88// so the range ["collection:token:", "collection:token;") reads only this token's
89// offers, never a full-tree scan. This is the read that finally lets the frontend wire
90// accept-offer and show a token's offers (no offers getter existed before v3.1).
91func GetOffersForToken(collectionID, tokenID string) string {
92 start := collectionID + ":" + tokenID + ":"
93 end := collectionID + ":" + tokenID + ";" // ';' (0x3b) is the byte after ':' (0x3a)
94
95 var sb strings.Builder
96 offers.Iterate(start, end, func(_ string, v interface{}) bool {
97 o := v.(*Offer)
98 sb.WriteString(o.Buyer.String())
99 sb.WriteString("|")
100 sb.WriteString(itoa(o.Amount))
101 sb.WriteString("|")
102 sb.WriteString(itoa(o.CreatedBlk))
103 sb.WriteString("\n")
104 return false
105 })
106 return sb.String()
107}