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

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

json.gno

5.09 Kb · 148 lines
  1package planes
  2
  3import (
  4	"strings"
  5
  6	"gno.land/p/onbloc/json/v0"
  7)
  8
  9// The JSON documents are built with gno.land/p/onbloc/json/v0, which quotes
 10// every string. Keys come out in the order they are written: a gno map
 11// iterates in insertion order, and the library keeps an object's members in
 12// one. Numbers are float64 in the library and printed without exponent or
 13// decimal point when whole, which every number here is, far under 2^53.
 14
 15// PlaneJSON returns one plane as a JSON object, minted or not:
 16//
 17//	{ "id": 42, "name": "Storm Saint", "model": "delta", "pilot": "gnome",
 18//	  "body": "#1e1b4b", "wing": "#1e293b", "accent": "#00f5ff", "tier": "epic",
 19//	  "laser": "#39ff14", "image": "https://gnofly.xyz/nft/collections/planes/042.png", "owner": "g1..." }
 20//
 21// The output has no whitespace. "owner" is "" for a plane not minted, or
 22// burned. An id outside 1 to 777 returns "". Which plane a pilot flies is
 23// not on-chain: the game server checks the owner when a pilot picks one.
 24func PlaneJSON(id int64) string {
 25	p, ok := getPlane(id)
 26	if !ok {
 27		return ""
 28	}
 29	b := json.Builder()
 30	writePlane(b, p)
 31	b.WriteString("image", imageURL(id))
 32	b.WriteString("owner", ownerOf(id).String())
 33	return encode(b.Node())
 34}
 35
 36// PlanesOf returns the ids of the planes addr owns as a JSON array, in
 37// ascending order: [3,42,700]. It is [] for an address without planes.
 38func PlanesOf(addr address) string {
 39	ids := []*json.Node{}
 40	if n, err := Token.BalanceOf(addr); err == nil && n > 0 {
 41		// Ids are dense, 1 to 777: a scan that stops at the last plane of
 42		// addr is cheaper than keeping a list per owner up to date at every
 43		// mint and transfer.
 44		for id := int64(1); id <= supply && int64(len(ids)) < n; id++ {
 45			if ownerOf(id) == addr {
 46				ids = append(ids, json.NumberNode("", float64(id)))
 47			}
 48		}
 49	}
 50	return encode(json.ArrayNode("", ids))
 51}
 52
 53// maxMintedPage is the most ids MintedJSON looks at in one call.
 54const maxMintedPage = int64(200)
 55
 56// MintedJSON returns who owns the minted planes among the ids offset+1 to
 57// offset+limit, as a JSON array in id order:
 58//
 59//	[{"id":3,"owner":"g1..."},{"id":42,"owner":""},...]
 60//
 61// Ids not minted are left out; "owner" is "" for a burned plane. limit is
 62// capped at 200, so four calls (offsets 0, 200, 400, 600) cover the 777,
 63// whatever was minted: planes are drawn at random, minted ids are not a
 64// prefix. It is what the website's grid reads. An empty range is [].
 65func MintedJSON(offset, limit int64) string {
 66	if limit > maxMintedPage {
 67		limit = maxMintedPage
 68	}
 69	entries := []*json.Node{}
 70	if offset >= 0 && limit > 0 {
 71		last := offset + limit
 72		if last > supply {
 73			last = supply
 74		}
 75		for id := offset + 1; id <= last; id++ {
 76			if !wasMinted(id) {
 77				continue
 78			}
 79			entry := json.Builder().
 80				WriteNumber("id", float64(id)).
 81				WriteString("owner", ownerOf(id).String())
 82			entries = append(entries, entry.Node())
 83		}
 84	}
 85	return encode(json.ArrayNode("", entries))
 86}
 87
 88// CollectionJSON returns the state of the collection as one JSON object:
 89//
 90//	{ "name": "gnofly planes", "symbol": "PLANE",
 91//	  "supply": 777, "minted": 12, "burned": 0,
 92//	  "price": 77000000, "open": true, "cap": 7,
 93//	  "royaltyBps": 500, "treasury": "g1...",
 94//	  "imageBase": "https://gnofly.xyz/nft/collections/planes/",
 95//	  "tiers": { "common": { "supply": 500, "minted": 8 },
 96//	             "rare": { "supply": 200, "minted": 3 },
 97//	             "epic": { "supply": 70, "minted": 1 },
 98//	             "legendary": { "supply": 7, "minted": 0 } } }
 99//
100// The output has no whitespace. "minted" counts every mint so far, burned
101// planes included; "minted" minus "burned" is what exists.
102func CollectionJSON() string {
103	b := json.Builder().
104		WriteString("name", tokenName).
105		WriteString("symbol", tokenSymbol).
106		WriteNumber("supply", float64(supply)).
107		WriteNumber("minted", float64(Minted())).
108		WriteNumber("burned", float64(Minted()-Token.TotalSupply())).
109		WriteNumber("price", float64(price)).
110		WriteBool("open", mintOpen).
111		WriteNumber("cap", float64(mintCap)).
112		WriteNumber("royaltyBps", float64(RoyaltyBps())).
113		WriteString("treasury", roles.Treasury().String()).
114		WriteString("imageBase", imageBase).
115		WriteObject("tiers", func(tb *json.NodeBuilder) {
116			for i, tier := range strings.Split(tiers, ",") {
117				tb.WriteObject(tier, func(t *json.NodeBuilder) {
118					t.WriteNumber("supply", float64(tierSupply[i]))
119					t.WriteNumber("minted", float64(tierMinted[i]))
120				})
121			}
122		})
123	return encode(b.Node())
124}
125
126// writePlane writes the nine fields of a plane, in the order of a row, so
127// that a caller can add fields after them.
128func writePlane(b *json.NodeBuilder, p plane) {
129	b.WriteNumber("id", float64(p.id)).
130		WriteString("name", p.name).
131		WriteString("model", p.model).
132		WriteString("pilot", p.pilot).
133		WriteString("body", p.body).
134		WriteString("wing", p.wing).
135		WriteString("accent", p.accent).
136		WriteString("tier", p.tier).
137		WriteString("laser", p.laser)
138}
139
140// encode marshals a document built above. It cannot fail on a document
141// built from the builder, so a failure is a bug and panics.
142func encode(n *json.Node) string {
143	out, err := json.Marshal(n)
144	if err != nil {
145		panic(err)
146	}
147	return string(out)
148}