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

render.gno

7.32 Kb · 198 lines
  1package pad
  2
  3import (
  4	"strconv"
  5	"strings"
  6
  7	"gno.land/p/g1n4pl5uc4yt5r96m9w6fmdznx3x0jyg8l6arhmt/gnomi/ammmath"
  8	"gno.land/p/nt/ufmt/v0"
  9)
 10
 11// Render provides on-chain discovery (gnoweb). Read-only by convention.
 12//
 13// Paths:
 14//
 15//	""              - home / list
 16//	token/{id}      - launch detail
 17//	help            - API help
 18func Render(path string) string {
 19	path = strings.Trim(path, "/")
 20	if path == "" {
 21		return renderHome()
 22	}
 23	if path == "help" {
 24		return renderHelp()
 25	}
 26	if strings.HasPrefix(path, "token/") {
 27		id := strings.TrimPrefix(path, "token/")
 28		return renderToken(id)
 29	}
 30	return "> [!WARNING]\n> Path not found: " + sanitize(path) + "\n"
 31}
 32
 33func sanitize(s string) string {
 34	s = strings.ReplaceAll(s, "`", "'")
 35	s = strings.ReplaceAll(s, "<", "")
 36	s = strings.ReplaceAll(s, ">", "")
 37	return s
 38}
 39
 40func renderHome() string {
 41	out := "# gnomi\n\n"
 42	out += "Meme launchpad - WUGNOT bonding curve -> remaining tokens + raised WUGNOT seed Gnoswap LP (auto-list).\n\n"
 43	if !inited {
 44		out += "> [!CAUTION]\n> Protocol not initialized. Call Init first.\n\n"
 45		return out
 46	}
 47	out += ufmt.Sprintf("- Launches: **%d**\n", launches.Size())
 48	if protocolAddr.IsValid() {
 49		out += ufmt.Sprintf("- Protocol treasury: `%s`\n", protocolAddr.String())
 50	}
 51	out += ufmt.Sprintf("- Protocol fees pending (claim/push): **%d** ugnot\n", protocolFees)
 52	out += ufmt.Sprintf("- Protocol fees paid (lifetime): **%d** ugnot\n\n", protocolFeesPaid)
 53	out += "## Markets\n\n"
 54	if launches.Size() == 0 {
 55		out += "_No launches yet. Call Create with name, symbol, uri and bond._\n"
 56		return out
 57	}
 58	launches.Iterate("", "", func(key string, value any) bool {
 59		l := value.(*Launch)
 60		st := "curve"
 61		if l.Status == StatusGraduated {
 62			st = "graduated"
 63		}
 64		out += ufmt.Sprintf("- [%s ($%s)](token/%s) - %s - raised %d ugnot - buyers %d\n",
 65			sanitize(l.Name), sanitize(l.Symbol), l.ID, st, l.RaisedUgnot, l.BuyerCount)
 66		return false
 67	})
 68	out += "\n[Help](help)\n"
 69	return out
 70}
 71
 72func renderToken(id string) string {
 73	id = sanitize(id)
 74	l, ok := launches.Get(id).(*Launch)
 75	if !ok {
 76		return "> [!WARNING]\n> Unknown launch " + id + "\n\n[Home](./)\n"
 77	}
 78	st := "bonding curve"
 79	if l.Status == StatusGraduated {
 80		st = "graduated pool (LP locked)"
 81	}
 82	out := ufmt.Sprintf("# %s ($%s)\n\n", sanitize(l.Name), sanitize(l.Symbol))
 83	out += ufmt.Sprintf("- **ID:** %s\n", l.ID)
 84	out += ufmt.Sprintf("- **Status:** %s\n", st)
 85	out += ufmt.Sprintf("- **Creator:** %s\n", l.Creator.String())
 86	if l.URI != "" {
 87		out += ufmt.Sprintf("- **URI:** %s\n", sanitize(l.URI))
 88	}
 89	out += ufmt.Sprintf("- **Unique buyers:** %d\n", l.BuyerCount)
 90	out += ufmt.Sprintf("- **Creator fees (claimable):** %d ugnot\n\n", l.CreatorFees)
 91
 92	if l.Status == StatusCurve {
 93		remaining := CurveSupply - l.RealSold
 94		pct := int64(0)
 95		if graduationThreshold() > 0 {
 96			pct = l.RaisedUgnot * 100 / graduationThreshold()
 97			if pct > 100 {
 98				pct = 100
 99			}
100		}
101		spot := ammmath.SpotPriceUgnotPerToken(l.VirtualUgnot, l.VirtualToken)
102		out += "## Bonding curve\n\n"
103		out += ufmt.Sprintf("- Raised: **%d** / %d ugnot (%d%%)\n", l.RaisedUgnot, graduationThreshold(), pct)
104		out += ufmt.Sprintf("- Tokens sold: **%d** / %d\n", l.RealSold, CurveSupply)
105		out += ufmt.Sprintf("- Remaining on curve: **%d**\n", remaining)
106		out += ufmt.Sprintf("- Spot (ugnot/token x1e6): **%d**\n", spot)
107		out += "\nProgress toward graduation:\n\n"
108		out += progressBar(pct) + "\n"
109	} else {
110		spot := ammmath.SpotPriceUgnotPerToken(l.PoolUgnot, l.PoolToken)
111		out += "## Liquidity (post-graduate)\n\n"
112		out += ufmt.Sprintf("- Raised capital (record): **%d** ugnot\n", l.PoolUgnot)
113		out += ufmt.Sprintf("- LP tokens (curve-spot sized): **%d**\n", l.PoolToken)
114		if l.LeftoverTokens > 0 {
115			out += ufmt.Sprintf("- Leftover on pad (not in LP): **%d**\n", l.LeftoverTokens)
116		}
117		out += ufmt.Sprintf("- Spot (ugnot/token x1e6): **%d**\n", spot)
118		if l.GnoswapListed {
119			out += ufmt.Sprintf("- **Gnoswap listed:** `%s`\n", sanitize(l.GnoswapPoolPath))
120			out += ufmt.Sprintf("- Position NFT id: **%d** (pad-owned, locked)\n", l.GnoswapPositionID)
121			out += ufmt.Sprintf("- Fee WUGNOT spent: **%d** | LP WUGNOT: **%d**\n", l.FeeWugnotSpent, l.LiqWugnotUsed)
122			out += "\n> [!NOTE]\n> Trade on Gnoswap router - pad SwapBuy/SwapSell disabled after auto-list.\n"
123		} else {
124			out += ufmt.Sprintf("- Gnoswap: %s\n", sanitize(l.GnoswapNote))
125			out += "\n> [!NOTE]\n> Internal CPMM. List: wugnot.Deposit -> Approve(pad) -> RetryListGnoswap (LP ugnot reimbursed; prefer GNS for fee).\n"
126			out += ufmt.Sprintf("- ListNeed: `%s`\n", listNeedOf(l))
127		}
128	}
129
130	out += "\n## Trade\n\n"
131	out += "Use wallet MsgCall (payable txs):\n\n"
132	if l.Status == StatusCurve {
133		out += ufmt.Sprintf("- Buy(id) with -send ugnot - id=%s\n", l.ID)
134		out += "- Sell(id, tokensIn)\n"
135		out += "- Graduate(id) when threshold met\n"
136	} else if l.GnoswapListed {
137		out += "- Trade on Gnoswap router (pad SwapBuy/Sell disabled)\n"
138		out += ufmt.Sprintf("- Pool: `%s`\n", sanitize(l.GnoswapPoolPath))
139	} else {
140		out += "- SwapBuy(id) with -send ugnot\n"
141		out += "- SwapSell(id, tokensIn)\n"
142		out += ufmt.Sprintf("- RetryListGnoswap(id) - auto-pull WUGNOT/GNS from caller if Approved - id=%s\n", l.ID)
143	}
144	out += "\n[Home](./) | [Help](help)\n"
145	return out
146}
147
148func progressBar(pct int64) string {
149	if pct < 0 {
150		pct = 0
151	}
152	if pct > 100 {
153		pct = 100
154	}
155	filled := int(pct / 5)
156	bar := "["
157	for i := 0; i < 20; i++ {
158		if i < filled {
159			bar += "#"
160		} else {
161			bar += "."
162		}
163	}
164	bar += "] " + strconv.FormatInt(pct, 10) + "%"
165	return bar
166}
167
168func renderHelp() string {
169	out := "# gnomi API\n\n"
170	out += "## Lifecycle\n\n"
171	out += "1. Init() - once; caller becomes protocol treasury\n"
172	out += "2. Create(name, symbol, uri) + bond from bond realm (promo/normal)\n"
173	out += "3. Buy(id, minTokensOut) / Sell(id, amount, minUgnotOut) - bonding curve\n"
174	out += "4. Auto or Graduate(id) at threshold\n"
175	out += "5. Graduate seeds remaining tokens + raised as liquidity\n"
176	out += "   -> auto Gnoswap CreatePool+Mint if pad has WUGNOT inventory\n"
177	out += "   -> else locked internal CPMM (SwapBuy/SwapSell)\n"
178	out += "6. Transfer / Approve / TransferFrom - GRC20\n"
179	out += "7. Fees:\n"
180	out += "   - Creator: ClaimCreatorFees(id) - only creator, **must claim**\n"
181	out += "   - Protocol: ClaimProtocolFees() (treasury) or PushProtocolFees() (anyone -> treasury)\n"
182	out += "   - TransferProtocol(newAddr) / WithdrawProtocolUgnot(amount) - treasury ops\n"
183	out += "   - Gnoswap CreatePool GNS fee goes to Gnoswap, not pad treasury\n\n"
184	out += "## Params (MVP)\n\n"
185	out += ufmt.Sprintf("- Total supply: %d\n", TotalSupply)
186	out += ufmt.Sprintf("- Curve sale: %d\n", CurveSupply)
187	out += ufmt.Sprintf("- LP tokens at grad: TotalSupply - RealSold (was PoolSeed=%d)\n", PoolSeed)
188	out += ufmt.Sprintf("- Graduation: %d ugnot\n", graduationThreshold())
189	out += ufmt.Sprintf("- Fee: %d BPS\n", FeeBPS)
190	out += ufmt.Sprintf("- Create bond (live): %d ugnot\n\n", requiredCreateBond())
191	out += "## Design\n\n"
192	out += "- Remaining tokens + raised GNOT -> liquidity at graduate\n"
193	out += "- Part of WUGNOT inventory swaps to GNS for CreatePool fee\n"
194	out += "- Permanent LP lock (pad holds Gnoswap position NFT)\n"
195	out += "- EOA + OriginSend payment guards\n\n"
196	out += "[Home](./)\n"
197	return out
198}