# `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. --- 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).