PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/r/g1wx60nkcp5lfsxmedeg7sfs47tzxkath05u63t4/trialmint2/stable

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
stable
Namespace
g1wx60nkcp5lfsxmedeg7sfs47tzxkath05u63t4 / trialmint2
Files
2 (gnomod.toml)
Exported functions
53
Module
gno.land/r/g1wx60nkcp5lfsxmedeg7sfs47tzxkath05u63t4/trialmint2/stable
gno
0.9

Files (2)

  • gnomod.tomltoml
  • stable.gnogno
stable.gnogno
1package stable23import (4	runtime "chain/runtime"5	unsaferealm "chain/runtime/unsafe"6	"encoding/base64"7	"strconv"8	"strings"910	"gno.land/p/nt/avl/v0"11	"gno.land/p/nt/grc721/v0"12	"gno.land/p/nt/seqid/v0"13	campaign "gno.land/r/g1wx60nkcp5lfsxmedeg7sfs47tzxkath05u63t4/trialmint2/campaign"14	artpkg "gno.land/p/g1wx60nkcp5lfsxmedeg7sfs47tzxkath05u63t4/art3d2"15	"gno.land/p/nt/ufmt/v0"16)1718const (19	CollectionName   = "Trial Mint"20	CollectionSymbol = "TRIAL"2122	realmRelPath = "/r/g1wx60nkcp5lfsxmedeg7sfs47tzxkath05u63t4/trialmint2/stable"2324	summaryPageSize = 1025	gnowebListLimit = 1026	pageWindow         = 227	tokenPageImageSize = "420"2829	RevealDelayBlocks = 530)3132var (33	token  *grc721.Token34	ledger *grc721.PrivateLedger // private: only this realm mints, burns and moves tokens3536	collectionOwner address = "g1wx60nkcp5lfsxmedeg7sfs47tzxkath05u63t4"3738	approvedVersion string3940	mintPriceUgnot int64 = 1000000000000 // 100 GNOT, per request4142	whitelistBps avl.Tree4344	royaltyBps       int64   = 50045	royaltyRecipient address = "g1wx60nkcp5lfsxmedeg7sfs47tzxkath05u63t4"4647	mintedIDs []grc721.TokenID4849	mintedTraitCombos avl.Tree // combo key -> true50	tokenComboKeys    avl.Tree // tid.String() -> comboKey string51	mintCountByWallet avl.Tree // address.String() -> int645253	mintingOpenMirror       = true54	maxMintsPerWalletMirror int64 = 05556	burnedRecords avl.Tree // tid.String() -> burnRecord57	burnedIDs     []grc721.TokenID5859	claimRecords []claimRecord6061	sealedData avl.Tree // tid.String() -> traitPreset62	mintHeight avl.Tree // tid.String() -> int646364	sealedImage string65)6667type traitPreset struct {68	Chain    string69	ShapeIdx int70	ColorIdx int71	Metal    string72	Active   bool73	Rarity   string // tier fixed at mint by the version realm (v5.rarityTier)74}7576type claimRecord struct {77	Wallet     address78	Bps        int6479	MintHeight int6480	Tid        grc721.TokenID81}8283type burnRecord struct {84	BurnedBy   address85	BurnHeight int6486	Traits     traitPreset87}8889func (r burnRecord) metadata() Metadata {90	if r.Traits.Chain == "" {91		return Metadata{Name: "Untitled"}92	}93	return traitOnlyMetadata(r.Traits)94}9596func init(cur realm) {97	token, ledger = grc721.NewToken(CollectionName, CollectionSymbol, seqid.ID(0), cur)98	svg := `<svg xmlns="http://www.w3.org/2000/svg" width="170" height="170" viewBox="0 0 170 170">` +99		`<rect width="170" height="170" fill="#0a0b10"/>` +100		`<circle cx="85" cy="78" r="34" fill="none" stroke="#555" stroke-width="4"/>` +101		`<text x="85" y="83" font-size="12" text-anchor="middle" fill="#999" font-family="monospace">SEALED</text>` +102		`</svg>`103	sealedImage = "data:image/svg+xml;base64," + base64.StdEncoding.EncodeToString([]byte(svg))104}105106type Trait struct {107	TraitType string108	Value     string109}110111type Metadata struct {112	Name            string113	Description     string114	Image           string115	BackgroundColor string116	Attributes      []Trait117}118119func Name() string      { return CollectionName }120func Symbol() string    { return CollectionSymbol }121func TokenCount() int64 { return token.TotalSupply() }122func TokenID() string   { return token.ID() }123124func GetName() string   { return CollectionName }125func GetSymbol() string { return CollectionSymbol }126127func OwnerOf(tid grc721.TokenID) (address, error)     { return token.OwnerOf(tid) }128func BalanceOf(owner address) (int64, error)          { return token.BalanceOf(owner) }129func GetApproved(tid grc721.TokenID) (address, error) { return token.GetApproved(tid) }130func IsApprovedForAll(owner, operator address) bool   { return token.IsApprovedForAll(owner, operator) }131132func ApprovedVersion() string { return approvedVersion }133func Owner() address          { return collectionOwner }134func MintPrice() int64        { return mintPriceUgnot }135136func MintedIDs() []grc721.TokenID { return mintedIDs }137func TotalBurned() int64          { return int64(len(burnedIDs)) }138139func MintingOpen() bool        { return mintingOpenMirror }140func MaxMintsPerWallet() int64 { return maxMintsPerWalletMirror }141142func MintsByWallet(owner address) int64 {143	n, _ := mintCountByWallet.Get(owner.String()).(int64)144	return n145}146147func IsTraitComboAvailable(comboKey string) bool {148	return !mintedTraitCombos.Has(comboKey)149}150151func WhitelistBps(wallet address) (bps int64, ok bool) {152	if !whitelistBps.Has(wallet.String()) {153		return 0, false154	}155	return whitelistBps.Get(wallet.String()).(int64), true156}157158func WhitelistCount() int64 { return int64(whitelistBps.Size()) }159160func WhitelistEntry(index int) (wallet string, bps int64) {161	w, v := whitelistBps.GetByIndex(index)162	return w, v.(int64)163}164165func EffectivePrice(caller address) int64 {166	if v, ok := whitelistBps.Get(caller.String()).(int64); ok {167		return mintPriceUgnot * v / 10000168	}169	if bps, _, ok := campaign.Peek(caller); ok {170		return mintPriceUgnot * bps / 10000171	}172	return mintPriceUgnot173}174175func RoyaltyInfo(tid grc721.TokenID, salePrice int64) (address, int64, error) {176	if _, err := token.OwnerOf(tid); err != nil {177		return "", 0, err178	}179	return royaltyRecipient, salePrice * royaltyBps / 10000, nil180}181182func RoyaltyBps() int64         { return royaltyBps }183func RoyaltyRecipient() address { return royaltyRecipient }184185func callerAddress() address {186	return unsaferealm.PreviousRealm().Address()187}188189func assertOwner() {190	if callerAddress() != collectionOwner {191		panic("unauthorized: caller is not the collection owner")192	}193}194195func SetApprovedVersion(cur realm, pkgpath string) {196	assertOwner()197	approvedVersion = pkgpath198}199200func SetMintPrice(cur realm, ugnot int64) {201	assertOwner()202	if ugnot < 0 {203		panic("price cannot be negative")204	}205	mintPriceUgnot = ugnot206}207208func AddToWhitelist(cur realm, wallet address, bps int64) {209	assertOwner()210	if bps < 0 || bps > 10000 {211		panic("bps must be between 0 and 10000")212	}213	whitelistBps.Set(wallet.String(), bps)214}215216func AddToWhitelistBatch(cur realm, walletsCSV string, bps int64) {217	assertOwner()218	if bps < 0 || bps > 10000 {219		panic("bps must be between 0 and 10000")220	}221	for _, w := range strings.Split(walletsCSV, ",") {222		w = strings.TrimSpace(w)223		if w == "" {224			continue225		}226		whitelistBps.Set(w, bps)227	}228}229230func RemoveFromWhitelist(cur realm, wallet address) {231	assertOwner()232	whitelistBps.Remove(wallet.String())233}234235func SetRoyaltyBps(cur realm, bps int64) {236	assertOwner()237	if bps < 0 || bps > 10000 {238		panic("bps must be between 0 and 10000")239	}240	royaltyBps = bps241}242243func SetRoyaltyRecipient(cur realm, recipient address) {244	assertOwner()245	royaltyRecipient = recipient246}247248func assertCallerIsApprovedVersion(cur realm) {249	caller := cur.Previous().PkgPath()250	if approvedVersion == "" {251		panic("no version realm has been approved yet")252	}253	if caller != approvedVersion {254		panic("unauthorized: caller is not the approved version realm")255	}256}257258func RecordMint(cur realm, to address, payer address, tid grc721.TokenID, chainName string, shapeIdx, colorIdx int, metal string, active bool, comboKey string, rarity string) grc721.TokenID {259	assertCallerIsApprovedVersion(cur)260	if mintedTraitCombos.Has(comboKey) {261		panic("trait combination already minted")262	}263	checkErr(ledger.Mint(to, tid))264	mintedTraitCombos.Set(comboKey, true)265	tokenComboKeys.Set(tid.String(), comboKey)266	mintCountByWallet.Set(to.String(), MintsByWallet(to)+1)267	mintedIDs = append(mintedIDs, tid)268269	height := runtime.ChainHeight()270	mintHeight.Set(tid.String(), height)271	sealedData.Set(tid.String(), traitPreset{272		Chain: chainName, ShapeIdx: shapeIdx, ColorIdx: colorIdx, Metal: metal, Active: active, Rarity: rarity,273	})274275	if _, hasOverride := whitelistBps.Get(payer.String()).(int64); !hasOverride {276		if bps, ok := campaign.ConsumeDiscount(cross(cur), payer); ok {277			claimRecords = append(claimRecords, claimRecord{278				Wallet:     payer,279				Bps:        bps,280				MintHeight: height,281				Tid:        tid,282			})283		}284	}285	return tid286}287288func ClaimCount() int64 { return int64(len(claimRecords)) }289290func ClaimAt(index int) (wallet address, bps int64, mintHeight_ int64, tid string) {291	r := claimRecords[index]292	return r.Wallet, r.Bps, r.MintHeight, r.Tid.String()293}294295func ReportMintingOpen(cur realm, open bool) {296	assertCallerIsApprovedVersion(cur)297	mintingOpenMirror = open298}299300func ReportMaxMintsPerWallet(cur realm, max int64) {301	assertCallerIsApprovedVersion(cur)302	maxMintsPerWalletMirror = max303}304305func Age(tid grc721.TokenID) int64 {306	h := mintHeight.Get(tid.String())307	if h == nil {308		return -1309	}310	return runtime.ChainHeight() - h.(int64)311}312313func IsRevealed(tid grc721.TokenID) bool {314	if _, isTraitPreset := sealedData.Get(tid.String()).(traitPreset); !isTraitPreset {315		return true316	}317	age := Age(tid)318	return age >= 0 && age >= RevealDelayBlocks319}320321func currentMetadata(tid grc721.TokenID) (Metadata, error) {322	if _, err := token.OwnerOf(tid); err != nil {323		return Metadata{}, err324	}325	tp, ok := sealedData.Get(tid.String()).(traitPreset)326	if !ok {327		return Metadata{}, grc721.ErrInvalidTokenId328	}329	if !IsRevealed(tid) {330		return sealedMetadata(), nil331	}332	return assembledMetadata(tid, tp), nil333}334335func sealedMetadata() Metadata {336	return Metadata{Name: "Sealed", Image: sealedImage, BackgroundColor: "0a0b10", Attributes: []Trait{{TraitType: "Status", Value: "Sealed"}}}337}338339func listRow(tid grc721.TokenID) (name, image string) {340	tp, ok := sealedData.Get(tid.String()).(traitPreset)341	if !ok {342		return "Untitled", ""343	}344	if !IsRevealed(tid) {345		return "Sealed", sealedImage346	}347	name = traitOnlyMetadata(tp).Name348	svg := artpkg.RenderTokenThumbSVG(tp.Chain, tp.Metal, tp.ShapeIdx, tp.ColorIdx, tp.Active, tid.String())349	return name, "data:image/svg+xml;base64," + base64.StdEncoding.EncodeToString([]byte(svg))350}351352func assembledMetadata(tid grc721.TokenID, tp traitPreset) Metadata {353	svg, stone, _, colorName := artpkg.RenderTokenSVG(tp.Chain, tp.Metal, tp.ShapeIdx, tp.ColorIdx, tp.Active, tid.String())354	shapeName := artpkg.ShapeNames[tp.ShapeIdx]355	if stone == "Pearl" {356		shapeName = artpkg.PearlShapeNames[tp.ShapeIdx]357	}358	image := "data:image/svg+xml;base64," + base64.StdEncoding.EncodeToString([]byte(svg))359	status := "Sunset"360	if tp.Active {361		status = "Active"362	}363	return Metadata{364		Name:            pieceDisplayName(colorName, stone, shapeName, tp.Metal, tp.Chain),365		Description:     ufmt.Sprintf("%s %s %s set in %s %s ring, minted on %s.", article(colorName), colorName, shapeName, strings.ToLower(article(tp.Metal)), tp.Metal, tp.Chain),366		Image:           image,367		BackgroundColor: "0a0b10",368		Attributes: []Trait{369			{TraitType: "Chain", Value: tp.Chain},370			{TraitType: "Stone", Value: stone},371			{TraitType: "Shape", Value: shapeName},372			{TraitType: "Color", Value: colorName},373			{TraitType: "Ring", Value: tp.Metal},374			{TraitType: "Status", Value: status},375			{TraitType: "Rarity", Value: tp.Rarity},376		},377	}378}379380func article(word string) string {381	if word != "" && strings.ContainsRune("AEIOUaeiou", rune(word[0])) {382		return "An"383	}384	return "A"385}386387func pieceDisplayName(colorName, stone, shape, metal, chain string) string {388	piece := colorName + " " + stone + " " + shape389	if stone == "Quartz" || stone == "" {390		piece = colorName + " " + shape391	}392	return piece + " · " + metal + " Ring · " + chain393}394395func colorNameFor(stone string, colorIdx int) string {396	pairs := strings.Split(artpkg.ColorPaletteCSV(stone), ";")397	if colorIdx < 0 || colorIdx >= len(pairs) {398		return ""399	}400	parts := strings.SplitN(pairs[colorIdx], ":", 2)401	if len(parts) != 2 {402		return ""403	}404	return parts[1]405}406407func traitOnlyMetadata(tp traitPreset) Metadata {408	stone := artpkg.StoneForChain(tp.Chain)409	shapeName := artpkg.ShapeNames[tp.ShapeIdx]410	if stone == "Pearl" {411		shapeName = artpkg.PearlShapeNames[tp.ShapeIdx]412	}413	colorName := colorNameFor(stone, tp.ColorIdx)414	status := "Sunset"415	if tp.Active {416		status = "Active"417	}418	return Metadata{419		Name:            pieceDisplayName(colorName, stone, shapeName, tp.Metal, tp.Chain),420		Description:     ufmt.Sprintf("%s %s %s set in %s %s ring, minted on %s.", article(colorName), colorName, shapeName, strings.ToLower(article(tp.Metal)), tp.Metal, tp.Chain),421		BackgroundColor: "0a0b10",422		Attributes: []Trait{423			{TraitType: "Chain", Value: tp.Chain},424			{TraitType: "Stone", Value: stone},425			{TraitType: "Shape", Value: shapeName},426			{TraitType: "Color", Value: colorName},427			{TraitType: "Ring", Value: tp.Metal},428			{TraitType: "Status", Value: status},429			{TraitType: "Rarity", Value: tp.Rarity},430		},431	}432}433434func TokenURI(tid grc721.TokenID) (string, error) {435	metadata, err := currentMetadata(tid)436	if err != nil {437		return "", err438	}439	return metadata.Image, nil440}441442func TokenMetadata(tid grc721.TokenID) (string, error) {443	if _, err := token.OwnerOf(tid); err != nil {444		return "", err445	}446	m := sealedMetadata()447	if tp, ok := sealedData.Get(tid.String()).(traitPreset); ok && IsRevealed(tid) {448		m = traitOnlyMetadata(tp)449	}450	m.Image = ""451	return metadataJSON(m, "traitType", "backgroundColor"), nil452}453454func TokenJSON(tid grc721.TokenID) (string, error) {455	metadata, err := currentMetadata(tid)456	if err != nil {457		return "", err458	}459	return "data:application/json;base64," + base64.StdEncoding.EncodeToString([]byte(metadataJSON(metadata, "trait_type", "background_color"))), nil460}461462func jsonEscape(s string) string {463	var b strings.Builder464	for i := 0; i < len(s); i++ {465		c := s[i]466		switch c {467		case '"':468			b.WriteString(`\"`)469		case '\\':470			b.WriteString(`\\`)471		default:472			b.WriteByte(c)473		}474	}475	return b.String()476}477478func metadataJSON(m Metadata, traitKey, bgKey string) string {479	var b strings.Builder480	b.WriteString(`{"name":"`)481	b.WriteString(jsonEscape(m.Name))482	b.WriteString(`","description":"`)483	b.WriteString(jsonEscape(m.Description))484	b.WriteString(`"`)485	if m.Image != "" {486		b.WriteString(`,"image":"`)487		b.WriteString(jsonEscape(m.Image))488		b.WriteString(`"`)489	}490	if m.BackgroundColor != "" {491		b.WriteString(`,"` + bgKey + `":"`)492		b.WriteString(jsonEscape(m.BackgroundColor))493		b.WriteString(`"`)494	}495	b.WriteString(`,"attributes":[`)496	for i, t := range m.Attributes {497		if i > 0 {498			b.WriteString(",")499		}500		b.WriteString(`{"` + traitKey + `":"`)501		b.WriteString(jsonEscape(t.TraitType))502		b.WriteString(`","value":"`)503		b.WriteString(jsonEscape(t.Value))504		b.WriteString(`"}`)505	}506	b.WriteString("]}")507	return b.String()508}509510func Approve(cur realm, to address, tid grc721.TokenID) {511	checkErr(ledger.Approve(callerAddress(), to, tid))512}513514func SetApprovalForAll(cur realm, operator address, approved bool) {515	checkErr(ledger.SetApprovalForAll(callerAddress(), operator, approved))516}517518func TransferFrom(cur realm, from, to address, tid grc721.TokenID) {519	checkErr(ledger.TransferFrom(callerAddress(), from, to, tid))520}521522func SafeTransferFrom(cur realm, from, to address, tid grc721.TokenID) {523	checkErr(ledger.TransferFrom(callerAddress(), from, to, tid))524}525526func Burn(cur realm, tid grc721.TokenID) {527	caller := callerAddress()528	owner, err := token.OwnerOf(tid)529	checkErr(err)530	if caller != owner {531		panic(grc721.ErrCallerIsNotOwner)532	}533534	key := tid.String()535	archived, _ := sealedData.Get(key).(traitPreset)536537	if comboKey, ok := tokenComboKeys.Get(key).(string); ok {538		mintedTraitCombos.Remove(comboKey)539		tokenComboKeys.Remove(key)540	}541	sealedData.Remove(key)542	mintHeight.Remove(key)543544	checkErr(ledger.Burn(tid))545546	burnedRecords.Set(key, burnRecord{547		BurnedBy:   caller,548		BurnHeight: runtime.ChainHeight(),549		Traits:     archived,550	})551	burnedIDs = append(burnedIDs, tid)552}553554func traitsToCSV(attrs []Trait) string {555	pairs := make([]string, len(attrs))556	for i, a := range attrs {557		pairs[i] = a.TraitType + "=" + a.Value558	}559	return strings.Join(pairs, ";")560}561562func TokenSummary(tid grc721.TokenID) (string, error) { return tokenSummary(tid, false) }563564func thumbSummary(tid grc721.TokenID) (string, error) { return tokenSummary(tid, true) }565566func tokenSummary(tid grc721.TokenID, thumb bool) (string, error) {567	owner, err := token.OwnerOf(tid)568	if err != nil {569		return "", err570	}571	var metadata Metadata572	if tp, ok := sealedData.Get(tid.String()).(traitPreset); thumb && ok && IsRevealed(tid) {573		metadata = traitOnlyMetadata(tp)574		svg := artpkg.RenderTokenThumbSVG(tp.Chain, tp.Metal, tp.ShapeIdx, tp.ColorIdx, tp.Active, tid.String())575		metadata.Image = "data:image/svg+xml;base64," + base64.StdEncoding.EncodeToString([]byte(svg))576	} else {577		metadata, err = currentMetadata(tid)578		if err != nil {579			return "", err580		}581	}582	enc := base64.StdEncoding583	fields := []string{584		tid.String(),585		owner.String(),586		enc.EncodeToString([]byte(metadata.Name)),587		enc.EncodeToString([]byte(metadata.Description)),588		enc.EncodeToString([]byte(metadata.Image)),589		"", // external URL: none590		enc.EncodeToString([]byte(traitsToCSV(metadata.Attributes))),591		enc.EncodeToString([]byte(metadata.BackgroundColor)),592	}593	return strings.Join(fields, "|"), nil594}595596func CollectionSummary(page int) string {597	total := len(mintedIDs)598	if page < 1 {599		page = 1600	}601	startK := (page - 1) * summaryPageSize602	endK := startK + summaryPageSize603	if endK > total {604		endK = total605	}606	var lines []string607	for k := startK; k < endK; k++ {608		tid := mintedIDs[total-1-k]609		line, err := thumbSummary(tid)610		if err != nil {611			continue612		}613		lines = append(lines, line)614	}615	return strings.Join(lines, "\n")616}617618const (619	walletScanChunk = 100620	walletRenderCap = summaryPageSize621)622623func WalletSummary(owner address, startIdx int64) (summary string, nextIdx int64) {624	total := len(mintedIDs)625	start := int(startIdx)626	if start < 0 {627		start = 0628	}629	end := start + walletScanChunk630	if end > total {631		end = total632	}633	var lines []string634	k := start635	for ; k < end; k++ {636		if len(lines) >= walletRenderCap {637			break638		}639		tid := mintedIDs[total-1-k]640		tokenOwner, err := token.OwnerOf(tid)641		if err != nil || tokenOwner != owner {642			continue643		}644		line, err := thumbSummary(tid)645		if err != nil {646			continue647		}648		lines = append(lines, line)649	}650	return strings.Join(lines, "\n"), int64(k)651}652653func BurnedTokenSummary(tid grc721.TokenID) (string, error) {654	raw := burnedRecords.Get(tid.String())655	rec, ok := raw.(burnRecord)656	if !ok {657		return "", grc721.ErrInvalidTokenId658	}659	m := rec.metadata()660	enc := base64.StdEncoding661	fields := []string{662		tid.String(),663		rec.BurnedBy.String(),664		ufmt.Sprintf("%d", rec.BurnHeight),665		enc.EncodeToString([]byte(m.Name)),666		enc.EncodeToString([]byte(m.Description)),667		"", // image: a burn keeps none668		"", // external URL: none669		enc.EncodeToString([]byte(traitsToCSV(m.Attributes))),670		enc.EncodeToString([]byte(m.BackgroundColor)),671	}672	return strings.Join(fields, "|"), nil673}674675func bigTokenImage(image string) string {676	const prefix = "data:image/svg+xml;base64,"677	if !strings.HasPrefix(image, prefix) {678		return image679	}680	raw, err := base64.StdEncoding.DecodeString(image[len(prefix):])681	if err != nil {682		return image683	}684	svg := string(raw)685	resized := strings.Replace(svg, `width="170.00" height="170.00"`, `width="`+tokenPageImageSize+`" height="`+tokenPageImageSize+`"`, 1)686	if resized == svg {687		resized = strings.Replace(svg, `width="170" height="170"`, `width="`+tokenPageImageSize+`" height="`+tokenPageImageSize+`"`, 1)688	}689	return prefix + base64.StdEncoding.EncodeToString([]byte(resized))690}691692func shortAddr(a string) string {693	if len(a) <= 14 {694		return a695	}696	return a[:6] + "…" + a[len(a)-4:]697}698699func renderMintedListPage(b *strings.Builder, page int) {700	total := len(mintedIDs)701	if total == 0 {702		return703	}704	totalPages := (total + gnowebListLimit - 1) / gnowebListLimit705	if page < 1 {706		page = 1707	}708	if page > totalPages {709		page = totalPages710	}711	startK := (page - 1) * gnowebListLimit712	endK := startK + gnowebListLimit713	if endK > total {714		endK = total715	}716717	b.WriteString("\n## Minted so far\n\n")718	if totalPages > 1 {719		b.WriteString(ufmt.Sprintf("_page %d of %d -- %d minted total_\n\n", page, totalPages, total))720	}721	b.WriteString("| Image | Index | Name | Owner |\n|:---:|:---:|---|---|\n")722	for k := startK; k < endK; k++ {723		tid := mintedIDs[total-1-k]724		link := realmRelPath + ":token/" + tid.String()725		owner, err := token.OwnerOf(tid)726		if err != nil {727			raw := burnedRecords.Get(tid.String())728			rec, ok := raw.(burnRecord)729			if !ok {730				continue // shouldn't happen: burned but no archive record731			}732			name := rec.metadata().Name733			b.WriteString(ufmt.Sprintf("| Burned | [#%s](%s) | %s (burned at block %d) | %s |\n", tid.String(), link, name, rec.BurnHeight, shortAddr(rec.BurnedBy.String())))734			continue735		}736		name, image := listRow(tid)737		imgCell := ""738		if image != "" {739			imgCell = ufmt.Sprintf("[![%s](%s)](%s)", name, image, link)740		}741		b.WriteString(ufmt.Sprintf("| %s | [#%s](%s) | %s | %s |\n", imgCell, tid.String(), link, name, shortAddr(owner.String())))742	}743	b.WriteString("\n")744745	renderPageLinks(b, page, totalPages, "page")746}747748func renderPageLinks(b *strings.Builder, page, totalPages int, linkBase string) {749	if totalPages <= 1 {750		return751	}752	b.WriteString("\n")753	parts := []string{}754	if page > 1 {755		parts = append(parts, ufmt.Sprintf("[<< First](%s:%s/1)", realmRelPath, linkBase))756		parts = append(parts, ufmt.Sprintf("[<- newer](%s:%s/%d)", realmRelPath, linkBase, page-1))757	}758	lastShown := 0759	for p := 1; p <= totalPages; p++ {760		if p != 1 && p != totalPages && (p < page-pageWindow || p > page+pageWindow) {761			continue762		}763		if lastShown != 0 && p != lastShown+1 {764			parts = append(parts, "...")765		}766		if p == page {767			parts = append(parts, ufmt.Sprintf("**%d**", p))768		} else {769			parts = append(parts, ufmt.Sprintf("[%d](%s:%s/%d)", p, realmRelPath, linkBase, p))770		}771		lastShown = p772	}773	if page < totalPages {774		parts = append(parts, ufmt.Sprintf("[older ->](%s:%s/%d)", realmRelPath, linkBase, page+1))775		parts = append(parts, ufmt.Sprintf("[Last >>](%s:%s/%d)", realmRelPath, linkBase, totalPages))776	}777	b.WriteString(strings.Join(parts, " - "))778	b.WriteString("\n")779}780781func renderBurnedListPage(b *strings.Builder, page int) {782	total := len(burnedIDs)783	if total == 0 {784		return785	}786	totalPages := (total + gnowebListLimit - 1) / gnowebListLimit787	if page < 1 {788		page = 1789	}790	if page > totalPages {791		page = totalPages792	}793	startK := (page - 1) * gnowebListLimit794	endK := startK + gnowebListLimit795	if endK > total {796		endK = total797	}798799	b.WriteString("\n## Burned tokens\n\n")800	if totalPages > 1 {801		b.WriteString(ufmt.Sprintf("_page %d of %d -- %d burned total_\n\n", page, totalPages, total))802	}803	b.WriteString("| Index | Name | Burned By | Block |\n|:---:|---|---|---|\n")804	for k := startK; k < endK; k++ {805		tid := burnedIDs[total-1-k]806		raw := burnedRecords.Get(tid.String())807		rec, ok := raw.(burnRecord)808		if !ok {809			continue810		}811		name := rec.metadata().Name812		b.WriteString(ufmt.Sprintf("| #%s | %s | %s | %d |\n", tid.String(), name, shortAddr(rec.BurnedBy.String()), rec.BurnHeight))813	}814	b.WriteString("\n")815816	renderPageLinks(b, page, totalPages, "burned/page")817}818819func renderBurnedTokenPage(tid grc721.TokenID) string {820	raw := burnedRecords.Get(tid.String())821	rec, ok := raw.(burnRecord)822	if !ok {823		return render404()824	}825	m := rec.metadata()826	name := m.Name827	var b strings.Builder828	b.WriteString(ufmt.Sprintf("# %s #%s (burned)\n\n[<- back to collection](%s)\n\n", CollectionName, tid.String(), realmRelPath))829	b.WriteString("## Details\n\n")830	b.WriteString(ufmt.Sprintf("* **Collection**: %s\n", CollectionName))831	b.WriteString(ufmt.Sprintf("* **Name**: %s\n", name))832	b.WriteString(ufmt.Sprintf("* **Index**: #%s\n", tid.String()))833	if m.Description != "" {834		b.WriteString(ufmt.Sprintf("* **Description**: %s\n", m.Description))835	}836	b.WriteString(ufmt.Sprintf("* **Status**: burned by %s at block %d\n", rec.BurnedBy, rec.BurnHeight))837	b.WriteString(ufmt.Sprintf("* **Archive**: [all burned tokens](%s:burned)\n", realmRelPath))838	if len(m.Attributes) > 0 {839		b.WriteString("\n## Traits\n\n")840		for _, attr := range m.Attributes {841			b.WriteString(ufmt.Sprintf("* **%s**: %s\n", attr.TraitType, attr.Value))842		}843	}844	return b.String()845}846847func render404() string {848	return ufmt.Sprintf("# 404\n\n[<- back to collection](%s)\n", realmRelPath)849}850851func Render(path string) string {852	if path == "" {853		var b strings.Builder854		b.WriteString(token.RenderHome())855		b.WriteString("\n## About\n\n")856		b.WriteString(CollectionName + ": each piece's Chain trait names a gno.land chain generation, and its stone is that chain's real stone -- Topaz, Sapphire, Pearl, Diamond for mainnet, Quartz for the testnets without one. Only real cuts and colours; every image is drawn on-chain. Delayed-reveal mint (real metadata sealed for a short delay after minting).\n\n")857		b.WriteString(ufmt.Sprintf("* **Owner**: %s\n", Owner()))858		b.WriteString(ufmt.Sprintf("* **Assembled trait combinations minted**: %d\n", int64(mintedTraitCombos.Size())))859		b.WriteString("* **Max supply**: unlimited\n")860		if mintingOpenMirror {861			b.WriteString("* **Minting**: open\n")862		} else {863			b.WriteString("* **Minting**: closed\n")864		}865		if mintPriceUgnot > 0 {866			b.WriteString(ufmt.Sprintf("* **Mint price**: %d ugnot\n", mintPriceUgnot))867		} else {868			b.WriteString("* **Mint price**: free\n")869		}870		b.WriteString(ufmt.Sprintf("* **Reveal delay**: %d block(s)\n", RevealDelayBlocks))871		if maxMintsPerWalletMirror > 0 {872			b.WriteString(ufmt.Sprintf("* **Max mints per wallet**: %d\n", maxMintsPerWalletMirror))873		} else {874			b.WriteString("* **Max mints per wallet**: unlimited\n")875		}876		if royaltyBps > 0 {877			b.WriteString(ufmt.Sprintf("* **Secondary sale royalty**: %d.%02d%% (EIP-2981 RoyaltyInfo)\n", royaltyBps/100, royaltyBps%100))878		}879		if len(burnedIDs) > 0 {880			b.WriteString(ufmt.Sprintf("* **Burned**: %d -- [view burned tokens ->](%s:burned)\n", len(burnedIDs), realmRelPath))881		}882883		renderMintedListPage(&b, 1)884		return b.String()885	}886887	parts := strings.Split(path, "/")888	if len(parts) == 2 && parts[0] == "page" {889		page, err := strconv.Atoi(parts[1])890		if err != nil || page < 1 {891			return render404()892		}893		var b strings.Builder894		b.WriteString(ufmt.Sprintf("# %s -- minted tokens\n\n[<- back to collection](%s)\n", CollectionName, realmRelPath))895		renderMintedListPage(&b, page)896		return b.String()897	}898	if parts[0] == "burned" {899		page := 1900		if len(parts) == 3 && parts[1] == "page" {901			p, err := strconv.Atoi(parts[2])902			if err != nil || p < 1 {903				return render404()904			}905			page = p906		} else if len(parts) != 1 {907			return render404()908		}909		var b strings.Builder910		b.WriteString(ufmt.Sprintf("# %s -- burned tokens\n\n[<- back to collection](%s)\n", CollectionName, realmRelPath))911		renderBurnedListPage(&b, page)912		return b.String()913	}914	if len(parts) == 2 && parts[0] == "token" {915		tid := grc721.TokenID(parts[1])916		owner, err := token.OwnerOf(tid)917		if err != nil {918			return renderBurnedTokenPage(tid)919		}920		metadata, _ := currentMetadata(tid)921		var b strings.Builder922		b.WriteString(ufmt.Sprintf("# %s #%s\n\n[<- back to collection](%s)\n\n", CollectionName, tid.String(), realmRelPath))923		image := metadata.Image924		name := metadata.Name925		if name == "" {926			name = "Untitled"927		}928		if image != "" {929			b.WriteString(ufmt.Sprintf("![%s](%s)\n\n", name, bigTokenImage(image)))930		}931932		b.WriteString("## Details\n\n")933		b.WriteString(ufmt.Sprintf("* **Collection**: %s\n", CollectionName))934		b.WriteString(ufmt.Sprintf("* **Name**: %s\n", name))935		b.WriteString(ufmt.Sprintf("* **Index**: #%s\n", tid.String()))936		if metadata.Description != "" {937			b.WriteString(ufmt.Sprintf("* **Description**: %s\n", metadata.Description))938		}939		b.WriteString(ufmt.Sprintf("* **Owner**: %s\n", owner))940		b.WriteString(ufmt.Sprintf("* **Revealed**: %t (age %d block(s))\n", IsRevealed(tid), Age(tid)))941942		if len(metadata.Attributes) > 0 {943			b.WriteString("\n## Traits\n\n")944			for _, attr := range metadata.Attributes {945				b.WriteString(ufmt.Sprintf("* **%s**: %s\n", attr.TraitType, attr.Value))946			}947		}948		return b.String()949	}950951	return render404()952}953954func checkErr(err error) {955	if err != nil {956		panic(err)957	}958}959

Functions

  • AddToWhitelist(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, wallet string, bps int64)

  • AddToWhitelistBatch(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, walletsCSV string, bps int64)

  • Age(tid string) int64

  • Approve(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, to string, tid string)

  • ApprovedVersion() string

  • BalanceOf(owner string) (int64, interface {Error func() string})

  • Burn(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, tid string)

  • BurnedTokenSummary(tid string) (string, interface {Error func() string})

  • ClaimAt(index int) (wallet string, bps int64, mintHeight_ int64, tid string)

  • ClaimCount() int64

  • CollectionSummary(page int) string

  • EffectivePrice(caller string) int64

  • GetApproved(tid string) (string, interface {Error func() string})

  • GetName() string

  • GetSymbol() string

  • IsApprovedForAll(owner string, operator string) bool

  • IsRevealed(tid string) bool

  • IsTraitComboAvailable(comboKey string) bool

  • MaxMintsPerWallet() int64

  • MintedIDs() []gno.land/p/nt/grc721/v0.TokenID

  • MintingOpen() bool

  • MintPrice() int64

  • MintsByWallet(owner string) int64

  • Name() string

  • Owner() string

  • OwnerOf(tid string) (string, interface {Error func() string})

  • RecordMint(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, to string, payer string, tid string, chainName string, shapeIdx int, colorIdx int, metal string, active bool, comboKey string, rarity string) string

  • RemoveFromWhitelist(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, wallet string)

  • Render(path string) string

  • ReportMaxMintsPerWallet(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, max int64)

  • ReportMintingOpen(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, open bool)

  • RoyaltyBps() int64

  • RoyaltyInfo(tid string, salePrice int64) (string, int64, interface {Error func() string})

  • RoyaltyRecipient() string

  • SafeTransferFrom(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, from string, to string, tid string)

  • SetApprovalForAll(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, operator string, approved bool)

  • SetApprovedVersion(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, pkgpath string)

  • SetMintPrice(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, ugnot int64)

  • SetRoyaltyBps(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, bps int64)

  • SetRoyaltyRecipient(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, recipient string)

  • Symbol() string

  • TokenCount() int64

  • TokenID() string

  • TokenJSON(tid string) (string, interface {Error func() string})

  • TokenMetadata(tid string) (string, interface {Error func() string})

  • TokenSummary(tid string) (string, interface {Error func() string})

  • TokenURI(tid string) (string, interface {Error func() string})

  • TotalBurned() int64

  • TransferFrom(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, from string, to string, tid string)

  • WalletSummary(owner string, startIdx int64) (summary string, nextIdx int64)

  • WhitelistBps(wallet string) (bps int64, ok bool)

  • WhitelistCount() int64

  • WhitelistEntry(index int) (wallet string, bps int64)

Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

Rendered

RenderedRawgnoweb ↗

vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.