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.24 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 market\n\n")
128	b.WriteString("Planes of [gnofly](https://gnofly.xyz) for sale. A listed plane is held by this realm until it is bought or the listing is cancelled. ")
129	b.WriteString("Each sale pays the planes realm's royalty to the gnofly treasury, the rest to the seller.\n\n")
130	if paused {
131		b.WriteString("New listings are paused: listed planes can still be bought or cancelled.\n\n")
132	}
133	if listings.Size() == 0 {
134		b.WriteString("No plane is for sale.\n")
135		return b.String()
136	}
137	b.WriteString("| Plane | Tier | Price | Seller |\n|---|---|---|---|\n")
138	listings.Iterate("", "", func(k string, v any) bool {
139		id, _ := strconv.Atoi(k)
140		l := v.(*listing)
141		p := planeInfo(id)
142		b.WriteString("| [#" + k + " " + p.name + "](https://gnofly.xyz/nft/collections/planes/" + strconv.Itoa(id) + ") | " +
143			p.tier + " | " + gnot(l.price) + " | " + l.seller.String() + " |\n")
144		return false
145	})
146	return b.String()
147}
148
149// gnot writes ugnot as GNOT: 1000000 is "1 GNOT", 1500000 "1.5 GNOT".
150func gnot(ugnot int64) string {
151	whole, frac := ugnot/1_000_000, ugnot%1_000_000
152	if frac == 0 {
153		return strconv.FormatInt(whole, 10) + " GNOT"
154	}
155	f := strings.TrimRight(strconv.FormatInt(frac+1_000_000, 10)[1:], "0")
156	return strconv.FormatInt(whole, 10) + "." + f + " GNOT"
157}
158
159// Address is this realm's address: the one sellers approve on the planes
160// realm, and the owner of every listed plane.
161func Address() address { return self }