PathrockNetwork Gno Explorer
HomeBlocksTransactionsRealmsPackagesValidatorsAnalytics

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/gnoland/coins

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
coins
Namespace
gnoland
Files
3 (gnomod.toml)
Exported functions
1
Module
gno.land/r/gnoland/coins
gno
0.9

Files (3)

  • gnomod.tomltoml
  • coins.gnogno
  • coins_test.gnogno
coins.gnogno
1// Package coins provides simple helpers to retrieve information about coins2// on the Gno.land blockchain.3//4// The primary goal of this realm is to allow users to check their token balances without5// relying on external tools or services. This is particularly valuable for new networks6// that aren't yet widely supported by public explorers or wallets. By using this realm,7// users can always access their balance information directly through the gnodev.8//9// While currently focused on basic balance checking functionality, this realm could10// potentially be extended to support other banker-related workflows in the future.11// However, we aim to keep it minimal and focused on its core purpose.12//13// This is a "Render-only realm" - it exposes only a Render function as its public14// interface and doesn't maintain any state of its own. This pattern allows for15// simple, stateless information retrieval directly through the blockchain's16// rendering capabilities.17package coins1819import (20	"chain/banker"21	"chain/runtime"22	"net/url"23	"strconv"24	"strings"2526	"gno.land/p/leon/coinsort/v0"27	"gno.land/p/leon/ctg/v0"28	"gno.land/p/moul/md/v0"29	"gno.land/p/moul/mdtable/v0"30	"gno.land/p/nt/mux/v0"31	"gno.land/p/nt/ufmt/v0"3233	"gno.land/r/sys/users"34)3536var router *mux.Router3738func init() {39	router = mux.NewRouter()4041	router.HandleFunc("", func(res *mux.ResponseWriter, req *mux.Request) {42		res.Write(renderHomepage())43	})4445	router.HandleFunc("balances", func(res *mux.ResponseWriter, req *mux.Request) {46		res.Write(renderBalances(req))47	})4849	router.HandleFunc("convert/{address}", func(res *mux.ResponseWriter, req *mux.Request) {50		res.Write(renderConvertedAddress(req.GetVar("address")))51	})5253	// Coin info54	router.HandleFunc("supply/{denom}", func(res *mux.ResponseWriter, req *mux.Request) {55		// banker := banker.NewReadonlyBanker()56		// res.Write(renderAddressBalance(banker, denom, denom))57		res.Write("The total supply feature is coming soon.")58	})5960	router.NotFoundHandler = func(res *mux.ResponseWriter, req *mux.Request) {61		res.Write("# 404\n\nThat page was not found. Would you like to [**go home**?](/r/gnoland/coins)")62	}63}6465func Render(path string) string {66	return router.Render(path)67}6869func renderHomepage() string {70	return strings.Replace(`# Gno.land Coins Explorer7172This is a simple, readonly realm that allows users to browse native coin balances. Check your coin balance below!7374<gno-form path="balances">75	<gno-input name="address" type="text" placeholder="Valid bech32 address (e.g. g1..., cosmos1..., osmo1...)" />76	<gno-input name="coin" type="text" placeholder="Coin (e.g. ugnot)"" />77</gno-form>7879Here are a few more ways to use this app:8081- ~/r/gnoland/coins:balances?address=g1...~ - show full list of coin balances of an address82	- [Example](/r/gnoland/coins:balances?address=g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5)83- ~/r/gnoland/coins:balances?address=g1...&coin=ugnot~ - shows the balance of an address for a specific coin84	- [Example](/r/gnoland/coins:balances?address=g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5&coin=ugnot)85- ~/r/gnoland/coins:convert/<bech32_addr>~ - convert a bech32 address to a Gno address86	- [Example](/r/gnoland/coins:convert/cosmos1jg8mtutu9khhfwc4nxmuhcpftf0pajdh6svrgs)87- ~/r/gnoland/coins:supply/<denom>~ - shows the total supply of denom88	- Coming soon!8990`, "~", "`", -1)91}9293func renderBalances(req *mux.Request) string {94	out := "# Balances\n\n"9596	input := req.Query.Get("address")97	coin := req.Query.Get("coin")9899	if input == "" && coin == "" {100		out += "Please input a valid address and coin denomination.\n\n"101		return out102	}103104	if input == "" {105		out += "Please input a valid bech32 address.\n\n"106		return out107	}108109	originalInput := input110	var wasConverted bool111112	// Try to validate or convert113	if !address(input).IsValid() {114		addr, err := ctg.ConvertAnyToGno(input)115		if err != nil {116			return out + ufmt.Sprintf("Tried converting `%s` to a Gno address but failed. Please try with a valid bech32 address.\n\n", input)117		}118		input = addr.String()119		wasConverted = true120	}121122	if wasConverted {123		out += ufmt.Sprintf("> [!NOTE]\n>  Automatically converted `%s` to its Gno equivalent.\n\n", originalInput)124	}125126	banker_ := banker.NewReadonlyBanker()127	balances := banker_.GetCoins(address(input))128129	if len(balances) == 0 {130		out += "This address currently has no coins."131		return out132	}133134	if coin != "" {135		return renderSingleCoinBalance(coin, input, originalInput, wasConverted, balances.AmountOf(coin))136	}137138	user, _ := users.ResolveAny(input)139	name := "`" + input + "`"140	if user != nil {141		name = user.RenderLink("")142	}143144	out += ufmt.Sprintf("This page shows full coin balances of %s at block #%d\n\n",145		name, runtime.ChainHeight())146147	// Determine sorting148	if getSortField(req) == "balance" {149		coinsort.SortByBalance(balances)150	}151152	// Create table153	denomColumn := renderSortLink(req, "denom", "Denomination")154	balanceColumn := renderSortLink(req, "balance", "Balance")155	table := mdtable.Table{156		Headers: []string{denomColumn, balanceColumn},157	}158159	if isSortReversed(req) {160		for _, b := range balances {161			table.Append([]string{b.Denom, strconv.Itoa(int(b.Amount))})162		}163	} else {164		for i := len(balances) - 1; i >= 0; i-- {165			table.Append([]string{balances[i].Denom, strconv.Itoa(int(balances[i].Amount))})166		}167	}168169	out += table.String() + "\n\n"170	return out171}172173// amount is taken from the balances the caller already read, rather than read again.174// Beyond saving the read, it keeps an unvalidated denom out of the banker: denom is175// the "coin" query parameter, and GetCoin panics on a malformed one, so on a render176// path any URL could otherwise break the page.177func renderSingleCoinBalance(denom, addr, origInput string, wasConverted bool, amount int64) string {178	out := "# Coin balance\n\n"179180	if wasConverted {181		out += ufmt.Sprintf("> [!NOTE]\n>  Automatically converted `%s` to its Gno equivalent.\n\n", origInput)182	}183184	user, _ := users.ResolveAny(addr)185	name := "`" + addr + "`"186	if user != nil {187		name = user.RenderLink("")188	}189190	out += ufmt.Sprintf("%s has `%d%s` at block #%d\n\n",191		name, amount, denom, runtime.ChainHeight())192193	out += "[View full balance list for this address](/r/gnoland/coins:balances?address=" + addr + ")"194195	return out196}197198func renderConvertedAddress(addr string) string {199	out := "# Address converter\n\n"200201	gnoAddress, err := ctg.ConvertAnyToGno(addr)202	if err != nil {203		out += err.Error()204		return out205	}206207	user, _ := users.ResolveAny(gnoAddress.String())208	name := "`" + gnoAddress.String() + "`"209	if user != nil {210		name = user.RenderLink("")211	}212213	out += ufmt.Sprintf("`%s` on Cosmos matches %s on gno.land.\n\n", addr, name)214	out += "[[View `ugnot` balance for this address]](/r/gnoland/coins:balances?address=" + gnoAddress.String() + "&coin=ugnot) - "215	out += "[[View full balance list for this address]](/r/gnoland/coins:balances?address=" + gnoAddress.String() + ")"216	return out217}218219// Helper functions for sorting and pagination220func getSortField(req *mux.Request) string {221	field := req.Query.Get("sort")222	switch field {223	case "denom", "balance":224		return field225	}226	return "denom"227}228229func isSortReversed(req *mux.Request) bool {230	return req.Query.Get("order") != "asc"231}232233func renderSortLink(req *mux.Request, field, label string) string {234	currentField := getSortField(req)235	currentOrder := req.Query.Get("order")236237	newOrder := "desc"238	if field == currentField && currentOrder != "asc" {239		newOrder = "asc"240	}241242	query := make(url.Values)243	for k, vs := range req.Query {244		query[k] = append([]string(nil), vs...)245	}246247	query.Set("sort", field)248	query.Set("order", newOrder)249250	if field == currentField {251		if currentOrder == "asc" {252			label += " ↑"253		} else {254			label += " ↓"255		}256	}257258	return md.Link(label, "?"+query.Encode())259}260

Functions

  • Render(path string) string

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.