/p/moul/mygnoscan/v0
gno.land/p/moul/mygnoscan
Builds links into a mygnoscan block explorer, from inside a realm.
A realm knows things its reader cannot see: which chain it is on, what its own address is, which block it was last written at. Turning any of those into a link means knowing the explorer's route table, and a route table copied into thirty realms is thirty places to fix when a route moves. This is the one place.
1s := mygnoscan.Default()
2
3s.Realm("gno.land/r/moul/home") // .../realm/r/moul/home?network=mainnet
4s.Realm("r/moul/home", mygnoscan.TabSource) // ...&tab=source
5s.RealmFunc("r/moul/config", "Set") // source tab, at one function
6s.Address(addr) // an account
7s.Block(runtime.ChainHeight()) // the block we are in
8s.Tx(hash) // one transaction, base64 hash
9s.Proposal(7) // one GovDAO proposal
10s.Page(mygnoscan.PageGas) // a list page
11
12s.RealmFooter("gno.land/r/moul/home") // the markdown line for a Render
Default() is DefaultBase (moul's instance) on whichever network answers for
the running chain. New(base) points somewhere else, WithNetwork(id)
overrides the network. Nothing here reads chain state beyond ChainDomain,
ChainID and ChainHeight, and nothing writes: building a link is free.
For a realm that wants the target to be changeable without a redeploy, read
the base from r/moul/config instead of calling
Default(): config.Scanner() returns exactly this type, configured.
The routes are measured
Every path was read out of the explorer's own router (route() in its
single-page frontend) on 2026-09-22, not inferred from clicking the UI. Two
realm tabs that the UI still redirects are deliberately absent because they no
longer exist: ?tab=graph (folded into deps) and ?tab=inert (folded into
the default tab, and dropped from the URL).
Three things that will bite otherwise
- An unknown path renders the home page. The explorer's router falls
through to
homerather than to a 404, so a misspelled route is not visible as an error: the link works and goes somewhere else. That is why the page names are constants andPageis the only door to them. - Omitting
?network=is not the same as asking for mainnet. The explorer then answers for every chain it indexes at once, which looks plausible and is wrong. AScanneron a chainNetworkFordoes not know emits no network parameter, soNewunder gnodev (chain-iddev) produces links to the blend.WithNetworkis how you fix that. - The network ids belong to the instance, not to the chain.
DefaultBasenames mainnetmainnet; the upstream default configuration names the same chaingnoland1.NetworkFormaps chain-ids to whatDefaultBaseserves, so pointingNewat another instance usually means settingWithNetworktoo.
A path a caller typed cannot break out of the link
Everything here can end up inside a markdown link, so a path carrying ) would
close the link early and render the rest as page text. url.PathEscape does
not help: ( and ) are legal URL sub-delims and it leaves both alone.
So the two shapes are handled differently, and neither trusts its input:
- Package paths and page names are refused, not escaped, because escaping
their separators would break the route.
Realm,RealmFunc,RealmFile,RealmFooterandPagefall back to a list page for anything outside[A-Za-z0-9._/-]or with an empty segment. - Hashes, token keys and addresses are escaped with
encodeURIComponentsemantics, which is what the explorer'sdecodeURIComponentround-trips.addressis a string type and nothing stops a realm casting a caller's input into one, so it gets the same treatment.
Link(text, target) does not escape its text. Pass a constant. A label a
caller typed belongs in ui.Inline first.
CurrentRealm and CurrentRealmFooter stack-walk (unsafe.CurrentRealm), so
inside a helper borrowed by another realm they name the borrower. That is
the right answer for a link and the wrong one for authorisation: never branch
on them. A realm calling through a shared config realm should pass its own path
explicitly, because the stack seen from in there has the config realm on it.
Part of moul/gno-contracts — moul's versioned gno.land contracts. See the repository for the full catalog, build/test tooling, and usage.
⚠️ Disclaimer: provided as-is, without warranty; not security-audited. Full disclaimer: DISCLAIMER.