# `gno.land/p/moul/mygnoscan`
Builds links into a [mygnoscan](https://mygnoscan.moul.p2p.team) 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.
```go
s := mygnoscan.Default()
s.Realm("gno.land/r/moul/home") // .../realm/r/moul/home?network=mainnet
s.Realm("r/moul/home", mygnoscan.TabSource) // ...&tab=source
s.RealmFunc("r/moul/config", "Set") // source tab, at one function
s.Address(addr) // an account
s.Block(runtime.ChainHeight()) // the block we are in
s.Tx(hash) // one transaction, base64 hash
s.Proposal(7) // one GovDAO proposal
s.Page(mygnoscan.PageGas) // a list page
s.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`](../../../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 `home` rather 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 and `Page` is 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. A `Scanner` on a chain `NetworkFor` does not know emits no network
parameter, so `New` under gnodev (chain-id `dev`) produces links to the
blend. `WithNetwork` is how you fix that.
- **The network ids belong to the instance, not to the chain.** `DefaultBase`
names mainnet `mainnet`; the upstream default configuration names the same
chain `gnoland1`. `NetworkFor` maps chain-ids to what `DefaultBase` serves,
so pointing `New` at another instance usually means setting `WithNetwork`
too.
## 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`,
`RealmFooter` and `Page` fall 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 `encodeURIComponent`
semantics, which is what the explorer's `decodeURIComponent` round-trips.
`address` is 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.
<!-- BEGIN GNOCONTRACTS FOOTER (generated by `make readmes`; do not edit below) -->
---
Part of **[moul/gno-contracts](https://github.com/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](https://github.com/moul/gno-contracts/blob/main/DISCLAIMER.md).
<!-- END GNOCONTRACTS FOOTER -->
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.