Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

read.gno

5.25 Kb · 161 lines
  1package gnofly
  2
  3import (
  4	"strconv"
  5	"strings"
  6
  7	"gno.land/p/nt/grc721/v0"
  8	"gno.land/p/onbloc/json/v0"
  9
 10	"gno.land/r/g1t2kg2vtr3fukg43eujkn6x53gfdyakhngt4sfd/gnofly/nfts/planes"
 11)
 12
 13// The reads are in the shapes Bazaar's site (bazaar.gnomi.fun) parses on a
 14// collection package, so that this market can be registered there later
 15// without a new version. Lines are fields joined by "|":
 16//
 17//	id|name|owner|seller|price|listed|image|collection|rarity|traits|revealed|slot
 18//
 19// owner is the planes realm's owner, this realm for a listed plane (escrow);
 20// seller and price are empty and 0 when the plane is not listed. collection
 21// is the slug Bazaar would know this realm by: the last path element.
 22
 23// slug is the last element of this realm's path.
 24const slug = "gnofly"
 25
 26// maxOpen is the most listings ListOpen returns, as Bazaar's own realms do.
 27const maxOpen = 50
 28
 29// Name and Symbol are the planes collection's: the market sells nothing else.
 30func Name() string   { return "gnofly planes" }
 31func Symbol() string { return "PLANE" }
 32
 33// Minted returns how many planes were minted.
 34func Minted() int { return int(planes.Minted()) }
 35
 36// OwnerOf returns the owner of a plane on the planes realm: this realm while
 37// it is listed.
 38func OwnerOf(tid string) (address, error) {
 39	return planes.OwnerOf(grc721.TokenID(tid))
 40}
 41
 42// TokenURI returns the image of a plane, an https URL.
 43func TokenURI(tid string) string {
 44	uri, err := planes.TokenURI(grc721.TokenID(tid))
 45	if err != nil {
 46		return ""
 47	}
 48	return uri
 49}
 50
 51// TokensOf returns the ids of the planes owner holds, comma-separated, in
 52// id order: "3,42,700". A listed plane is not among them: it is held here.
 53func TokensOf(owner string) string {
 54	ids := strings.TrimSuffix(strings.TrimPrefix(planes.PlanesOf(address(owner)), "["), "]")
 55	return ids
 56}
 57
 58// ListOpen returns the listings, one line each, in id order, at most 50.
 59func ListOpen() string {
 60	var lines []string
 61	listings.Iterate("", "", func(k string, v any) bool {
 62		id, _ := strconv.Atoi(k)
 63		lines = append(lines, line(id, v.(*listing)))
 64		return len(lines) >= maxOpen
 65	})
 66	return strings.Join(lines, "\n")
 67}
 68
 69// ItemLine returns the line of plane id, listed or not. It panics for an id
 70// that is not a minted plane: Bazaar's site skips the ids it cannot read.
 71func ItemLine(id int) string {
 72	ownerOf(id) // panics for an id that is not minted
 73	return line(id, getListing(id))
 74}
 75
 76// line builds the line of plane id; l is its listing, nil when it has none.
 77func line(id int, l *listing) string {
 78	p := planeInfo(id)
 79	owner, _ := planes.OwnerOf(tid(id))
 80	seller, price, listed := "", "0", "false"
 81	if l != nil {
 82		seller, price, listed = l.seller.String(), strconv.FormatInt(l.price, 10), "true"
 83	}
 84	traits := "model:" + p.model + ",pilot:" + p.pilot + ",laser:" + p.laser
 85	return strings.Join([]string{
 86		strconv.Itoa(id), clean(p.name), owner.String(), seller, price, listed,
 87		clean(p.image), slug, p.tier, clean(traits), "true", "",
 88	}, "|")
 89}
 90
 91type plane struct {
 92	name, image, tier, model, pilot, laser string
 93}
 94
 95// planeInfo reads plane id from the planes realm's PlaneJSON.
 96func planeInfo(id int) plane {
 97	node, err := json.Unmarshal([]byte(planes.PlaneJSON(int64(id))))
 98	if err != nil {
 99		panic(errUnknownPlane)
100	}
101	str := func(k string) string {
102		v, err := node.GetKey(k)
103		if err != nil {
104			return ""
105		}
106		s, err := v.GetString()
107		if err != nil {
108			return ""
109		}
110		return s
111	}
112	return plane{
113		name: str("name"), image: str("image"), tier: str("tier"),
114		model: str("model"), pilot: str("pilot"), laser: str("laser"),
115	}
116}
117
118// clean keeps a field from breaking a line: no "|" and no line break. The
119// planes realm's names and URLs have none; this is a guard, not a filter.
120func clean(s string) string {
121	return strings.NewReplacer("|", " ", "\n", " ", "\r", " ").Replace(s)
122}
123
124// Render shows the listings on gnoweb.
125func Render(_ string) string {
126	var b strings.Builder
127	b.WriteString("# gnofly planes on Bazaar\n\n")
128	b.WriteString("The [gnofly](https://gnofly.xyz) planes for sale on [Bazaar](https://bazaar.gnomi.fun), the collection `gnofly`. ")
129	b.WriteString("A listed plane is held by this realm until it is bought or the listing is cancelled. ")
130	b.WriteString("Each sale pays Bazaar's fee and the planes' royalty to the gnofly treasury, the rest to the seller.\n\n")
131	if !registered {
132		b.WriteString("Not registered on Bazaar yet.\n\n")
133	}
134	if paused {
135		b.WriteString("New listings are paused: listed planes can still be bought or cancelled.\n\n")
136	}
137	if listings.Size() == 0 {
138		b.WriteString("No plane is for sale.\n")
139		return b.String()
140	}
141	b.WriteString("| Plane | Tier | Price | Seller |\n|---|---|---|---|\n")
142	listings.Iterate("", "", func(k string, v any) bool {
143		id, _ := strconv.Atoi(k)
144		l := v.(*listing)
145		p := planeInfo(id)
146		b.WriteString("| [#" + k + " " + p.name + "](https://gnofly.xyz/nft/collections/planes/" + strconv.Itoa(id) + ") | " +
147			p.tier + " | " + gnot(l.price) + " | " + l.seller.String() + " |\n")
148		return false
149	})
150	return b.String()
151}
152
153// gnot writes ugnot as GNOT: 1000000 is "1 GNOT", 1500000 "1.5 GNOT".
154func gnot(ugnot int64) string {
155	whole, frac := ugnot/1_000_000, ugnot%1_000_000
156	if frac == 0 {
157		return strconv.FormatInt(whole, 10) + " GNOT"
158	}
159	f := strings.TrimRight(strconv.FormatInt(frac+1_000_000, 10)[1:], "0")
160	return strconv.FormatInt(whole, 10) + "." + f + " GNOT"
161}