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.

home.gno

12.52 Kb · 242 lines
  1// Package home is 67's site on gno.land: what the exchange is, the open
  2// markets read live from the markets realm, how a market works and what the
  3// contract does. It holds no funds and no keys. It is private, so the same
  4// deployer can publish a new version at the same address.
  5package home
  6
  7import (
  8	"chain/runtime"
  9	"chain/runtime/unsafe"
 10	"strings"
 11	"time"
 12
 13	"gno.land/r/nym-sixseven067/markets"
 14)
 15
 16// appURL is the exchange interface, where Adena signs purchases and claims.
 17const appURL = "https://the67.click"
 18
 19const tick = "`"
 20
 21// homePath and marketsPath are gnoweb paths, captured at deployment.
 22var (
 23	homePath    = strings.TrimPrefix(unsafe.CurrentRealm().PkgPath(), "gno.land")
 24	marketsPath = strings.TrimSuffix(homePath, "/home") + "/markets"
 25)
 26
 27// Render serves three pages: the home page, ":how" and ":contract".
 28func Render(path string) string {
 29	switch path {
 30	case "":
 31		return renderHome()
 32	case "how":
 33		return renderHow()
 34	case "contract":
 35		return renderContract()
 36	}
 37	return "# Page not found\n\nThere is nothing at this address.\n\n[Home](" + homePath + ")\n"
 38}
 39
 40func renderHome() string {
 41	var out strings.Builder
 42	out.WriteString("# 67\n\n")
 43	out.WriteString("Questions about the world, priced in GNOT. A share in the outcome that happens pays 1 GNOT.\n\n")
 44	out.WriteString("[Open the exchange](" + appURL + "/#/markets)\n\n")
 45
 46	out.WriteString("## Markets\n\n")
 47	list := markets.ListMarkets()
 48	if len(list) == 0 {
 49		out.WriteString("No markets are listed yet.\n\n")
 50	} else {
 51		out.WriteString("| Question | Yes, GNOT | No, GNOT | Status |\n")
 52		out.WriteString("| :--- | ---: | ---: | :--- |\n")
 53		for _, m := range list {
 54			out.WriteString("| [" + escapeText(m.Question) + "](" + marketsPath + ":" + m.ID + ") | ")
 55			if m.Finalized {
 56				out.WriteString("– | – | " + status(m) + " |\n")
 57			} else {
 58				out.WriteString(priceText(m.YesPricePpm) + " | " + priceText(1000000-m.YesPricePpm) + " | " + status(m) + " |\n")
 59			}
 60		}
 61		out.WriteString("\n")
 62	}
 63
 64	out.WriteString("## Four calls settle a market\n\n")
 65	out.WriteString("<gno-columns>\n")
 66	out.WriteString("**CreateMarket**\n\nThe question, its rules and the oracle are fixed in the realm. Nothing edits them later.\n\n<gno-columns-sep>\n\n")
 67	out.WriteString("**Buy**\n\nYou pay the exact quoted price. The realm refuses a sale it could not pay out.\n\n<gno-columns-sep>\n\n")
 68	out.WriteString("**FinalizeByOracle**\n\nWhen Polymarket's result is final, the market's oracle records it, once.\n\n<gno-columns-sep>\n\n")
 69	out.WriteString("**Claim**\n\nEach winning share pays 1 GNOT from the market's collateral, straight to your address.\n")
 70	out.WriteString("</gno-columns>\n\n")
 71
 72	out.WriteString("[How it works](" + homePath + ":how) and [what the contract does](" + homePath + ":contract), in full.\n\n")
 73	out.WriteString(footer())
 74	return out.String()
 75}
 76
 77func renderHow() string {
 78	var out strings.Builder
 79	out.WriteString("# A share pays 1 GNOT if it's right\n\n")
 80	out.WriteString("Everything else on 67 follows from that sentence: what a price means, why every market holds collateral, and when you get paid.\n\n")
 81
 82	out.WriteString("## The question\n\n")
 83	out.WriteString("Every question is copied word for word from a Polymarket market, rules included. When it is listed, the realm stores the question, the rules, their hash, the Polymarket condition and both outcome tokens. None of it can be edited later.\n\n")
 84	out.WriteString("Copying the words is not a link to Polymarket's money: there is no shared order book and no shared liquidity.\n\n")
 85
 86	out.WriteString("## The price\n\n")
 87	out.WriteString("Prices are 67's own fixed quotes. The operator follows Polymarket's midpoint for the Yes token and publishes a new quote when it moves. Each quote is numbered and kept in the market's history.\n\n")
 88	out.WriteString("Yes and No always add up to 1 GNOT, so a Yes price of 0.42 is also a 42% quote. It is a price, not a forecast.\n\n")
 89	out.WriteString("```\nshares = floor(spend × 1,000,000 ÷ price)\ncost   = ceil(shares × price ÷ 1,000,000)\n```\n\n")
 90
 91	out.WriteString("## The review\n\n")
 92	out.WriteString("A purchase carries the price the buyer reviewed and pays the exact cost. If the quote moved in the meantime, the realm refuses and only the network fee is spent.\n\n")
 93
 94	out.WriteString("## The collateral\n\n")
 95	out.WriteString("Each market holds its own collateral: reserve funding first, then the cost of every purchase. The realm refuses any purchase after which the larger side could not be paid in full.\n\n")
 96	out.WriteString("Only winning claims take collateral out. There is no withdrawal function, not even for the administrator.\n\n")
 97	out.WriteString("```\nmax(Yes shares, No shares) ≤ collateral   (all in ugnot)\n```\n\n")
 98
 99	out.WriteString("## The result\n\n")
100	out.WriteString("Each market names its oracle when it is listed. Only that address can finalize it, once, and only by repeating the market's full source binding with a hash of the evidence. Trading stops in the same transaction.\n\n")
101	out.WriteString("A Polymarket price near 100% is not a result. Until the oracle finalizes, nothing pays.\n\n")
102
103	out.WriteString("## The claim\n\n")
104	out.WriteString("When a market is final, each winning share pays 1 GNOT from that market's collateral, straight to your address. A losing position closes with zero, and a second claim is refused.\n\n")
105
106	out.WriteString("## Where to trade\n\n")
107	out.WriteString("In [the app](" + appURL + "/#/markets), where the Adena wallet signs each purchase after you review it. ")
108	out.WriteString("Or directly with gnokey: ask the realm for the exact cost with " + tick + "Quote(id, outcome, microShares)" + tick + ", then call [Buy](" + marketsPath + "$help&func=Buy) with that amount.\n\n")
109	out.WriteString("There is no selling yet: a position is held until its market is final.\n\n")
110
111	out.WriteString("[Home](" + homePath + ")\n")
112	return out.String()
113}
114
115type call struct {
116	name, who, signature, text string
117	refusals                   []string
118}
119
120var calls = []call{
121	{"CreateMarket", "Administrator", "CreateMarket(cur, id, question, rules, conditionID, yesTokenID, noTokenID string, closesAt, yesPricePpm int64, oracle address)",
122		"Lists a market and freezes every field used to match the external result: the question, the rules and their hash, the Polymarket condition, both outcome tokens, the closing time and the oracle. There is no function that edits any of them afterwards.",
123		[]string{"administrator only", "market already exists", "closing time must be in the future", "invalid fixed price"}},
124	{"Fund", "Anyone", "Fund(cur, id string)",
125		"Adds reserve collateral to one market. The money belongs to that market only and nothing withdraws it, not even the administrator.",
126		[]string{"market funding closed"}},
127	{"SetQuote", "Administrator", "SetQuote(cur, id string, yesPricePpm, expectedVersion int64)",
128		"Publishes a new fixed Yes price while trading is open and adds it to the quote history. The expected version stops an operator from overwriting a quote they have not seen.",
129		[]string{"trading closed", "quote version changed", "quote unchanged"}},
130	{"Buy", "Any account", "Buy(cur, id, outcome string, microShares, expectedPricePpm int64)",
131		"Buys Yes or No shares at the price the buyer reviewed. The payment must be the exact cost, and the market's collateral must cover the larger side if it wins.",
132		[]string{"trading closed", "price does not match review", "send the exact quoted ugnot amount", "insufficient market collateral"}},
133	{"FinalizeByOracle", "The market's oracle", "FinalizeByOracle(cur, id, conditionID, rulesHash, yesTokenID, noTokenID, winner, evidenceHash string)",
134		"Records the final outcome once, with a hash of the evidence. The call must repeat the market's whole source binding, so a result for another question cannot land here. Trading stops in the same transaction.",
135		[]string{"oracle only", "outcome already final", "source binding mismatch"}},
136	{"Claim", "A position holder", "Claim(cur, id string) int64",
137		"Pays the caller's winning shares, 1 GNOT each, from that market's collateral. A losing position closes with zero; a second claim is refused.",
138		[]string{"outcome is not final", "no position", "position already claimed"}},
139	{"ProposeAdministrator", "Administrator", "ProposeAdministrator(cur, next address)",
140		"Starts a two-step handover. Nothing changes until the proposed address accepts from its own account, so a mistyped address cannot take over. Markets keep the oracle they were created with.",
141		[]string{"administrator only", "invalid administrator"}},
142	{"AcceptAdministrator", "The proposed address", "AcceptAdministrator(cur)",
143		"Completes the handover.",
144		[]string{"pending administrator only"}},
145}
146
147func renderContract() string {
148	var out strings.Builder
149	out.WriteString("# One realm, every rule\n\n")
150	out.WriteString("67 is one Gno package, [" + tick + "gno.land" + marketsPath + tick + "](" + marketsPath + "). It holds each market's collateral, the published quotes, every position and the payouts. The app only reads it and asks Adena to sign calls to it.\n\n")
151	out.WriteString("Administrator " + string(markets.Administrator()) + "\n\n")
152
153	out.WriteString("## Units\n\n")
154	out.WriteString("All amounts are whole " + tick + "ugnot" + tick + ": one GNOT is 1,000,000 ugnot. A share is 1,000,000 micro-shares, and a winning micro-share pays 1 ugnot, so a winning share pays exactly 1 GNOT.\n\n")
155	out.WriteString("Prices are parts per million of that payout. A Yes price of " + tick + "420000" + tick + " means 0.42 GNOT per share; the No price is always the rest, " + tick + "580000" + tick + ".\n\n")
156
157	out.WriteString("## What each call does\n\n")
158	out.WriteString("Each function checks its own conditions and stops the whole transaction when one fails. The refusals are its exact messages.\n\n")
159	for _, c := range calls {
160		out.WriteString("### [" + c.name + "](" + marketsPath + "$help&func=" + c.name + ")\n\n")
161		out.WriteString(c.who + ". " + c.text + "\n\n")
162		out.WriteString(tick + c.signature + tick + "\n\n")
163		out.WriteString("Refuses with " + tick + strings.Join(c.refusals, tick+", "+tick) + tick + ".\n\n")
164	}
165
166	out.WriteString("## Read without signing\n\n")
167	for _, fn := range []string{"MarketsJSON()", "MarketJSON(id)", "PositionsJSON(owner)", "QuoteHistory(id)", "Quote(id, outcome, microShares)", "Render(path)"} {
168		out.WriteString("- " + tick + fn + tick + "\n")
169	}
170	out.WriteString("\nEvents for indexers: " + tick + "MarketCreated" + tick + ", " + tick + "QuoteUpdated" + tick + ", " + tick + "Funded" + tick + ", " + tick + "Bought" + tick + ", " + tick + "Finalized" + tick + ", " + tick + "Claimed" + tick + ", " + tick + "AdministratorProposed" + tick + ", " + tick + "AdministratorAccepted" + tick + ".\n\n")
171
172	out.WriteString("## What it cannot do\n\n")
173	out.WriteString("- Edit a question, its rules, its source or its oracle\n")
174	out.WriteString("- Withdraw collateral, for anyone\n")
175	out.WriteString("- Sell or cancel a position\n")
176	out.WriteString("- Learn a result by itself: only the pinned oracle can finalize\n\n")
177
178	out.WriteString("[Source of the realm](" + marketsPath + "$source)\n\n")
179	out.WriteString("[Home](" + homePath + ")\n")
180	return out.String()
181}
182
183func footer() string {
184	var out strings.Builder
185	if runtime.ChainID() != "gnoland-1" {
186		out.WriteString("> [!NOTE] Testnet\n")
187		out.WriteString("> This is " + runtime.ChainID() + ", a test network: test GNOT has no value.\n\n")
188	}
189	out.WriteString("Questions, rules and results come from Polymarket. 67 is independent and not affiliated with Polymarket.\n")
190	return out.String()
191}
192
193func status(m markets.Market) string {
194	if m.Finalized {
195		return m.Winner + " won"
196	}
197	if time.Now().Unix() >= m.ClosesAt {
198		return "Awaiting the result"
199	}
200	return "Open until " + time.Unix(m.ClosesAt, 0).UTC().Format("2 January 2006")
201}
202
203// priceText formats millionths like the app: at least two decimals, no
204// trailing zeros beyond them (420000 is "0.42", 425000 is "0.425").
205func priceText(value int64) string {
206	whole := value / 1000000
207	fraction := value % 1000000
208	text := ""
209	for i := 0; i < 6; i++ {
210		text = string(rune('0'+fraction%10)) + text
211		fraction /= 10
212	}
213	for len(text) > 2 && strings.HasSuffix(text, "0") {
214		text = text[:len(text)-1]
215	}
216	return itoa(whole) + "." + text
217}
218
219func itoa(n int64) string {
220	if n == 0 {
221		return "0"
222	}
223	text := ""
224	for n > 0 {
225		text = string(rune('0'+n%10)) + text
226		n /= 10
227	}
228	return text
229}
230
231// escapeText keeps question text literal in Markdown, tables included.
232func escapeText(s string) string {
233	var out strings.Builder
234	for _, c := range s {
235		switch c {
236		case '\\', '`', '*', '_', '[', ']', '<', '>', '|', '#', '~', '!':
237			out.WriteByte('\\')
238		}
239		out.WriteRune(c)
240	}
241	return out.String()
242}