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/moul/gns/v0

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
v0
Namespace
moul / gns
Files
13 (README)(gnomod.toml)
Exported functions
55
Module
gno.land/r/moul/gns/v0
gno
0.9

Files (13)

  • README.mdmarkdown
  • gnomod.tomltoml
  • gns.gnogno
  • asserts_test.gnogno
  • gns_test.gnogno
  • helpers_test.gnogno
  • policy_test.gnogno
  • records_test.gnogno
  • registration_test.gnogno
  • render_test.gnogno
  • reverse_test.gnogno
  • security_test.gnogno
  • subnames_test.gnogno
README.mdPreviewRaw
# GNS — Gno Name Service

GNS is an **ENS-equivalent naming system expressed as a single Gno-native
realm**. It provides the capabilities mainstream ENS users rely on —
registration, renewal, expiry & grace, forward/reverse resolution, primary
names, typed and arbitrary records, subnames with policies, delegated
operators, pagination and events — but implements all of it
through **one realm, one ownership model, one record model, one authorization
function and one registration lifecycle**, rather than a collection of
emulated ENS contracts.

Package path: `gno.land/r/moul/gns/v0` · possible future target: `gno.land/r/gnoland/gns`.

Built and tested against **gno 0.9 / master** (`chain`, `chain/banker`,
`chain/runtime`, `gno.land/p/nt/avl/v0`).

## What is and isn't compatible with ENS

GNS deliberately does **not** aim for byte-for-byte ENS/Ethereum compatibility.

**Equivalent user capabilities** (different implementation):

| ENS capability | GNS |
|---|---|
| Register / renew second-level name | `Register`, `Renew` |
| Expiration + grace period | stored expiry + configurable grace |
| Commit–reveal | `Commit` + `Register` (see preimage below) |
| Owner / transfer | `OwnerOf`, `Transfer` |
| Resolver records | built-in typed records (no pluggable resolver) |
| Multichain addresses | `SetCoinAddress` keyed by coin type |
| Text / content hash / pubkey / ABI / interface | typed setters/getters |
| Arbitrary records | `SetRecord` with reverse-DNS namespaces |
| Reverse resolution + primary name | `SetPrimaryName` / `PrimaryName` (forward-verified) |
| Subnames + subname registrar | hierarchical names + `RegistrationPolicy` |
| Wrapped / emancipated names | explicit `ControlPolicy` flags + `LockPolicy` |
| Delegated managers | `SetOperator` with per-permission grants |
| Multicall | not in-realm — loop a typed setter from one `gnokey maketx run` |
| Wildcard resolution | `Resolve(..., NearestAncestor)` |
| Events | append-only `Event` log + native `chain.Emit` |

**Explicitly NOT included** (Ethereum-specific or out of scope for v1): DNSSEC
import, DNS registrar, CCIP-Read, L2 resolution, NFT/ERC-721 ownership, Ethereum
ABI compatibility, Unicode/emoji names, governance DAO, auctions, secondary
marketplace, and arbitrary custom-resolver execution.

## Names

- Second-level names are stored **string-native and suffix-free**: `alice`,
  not `alice.gno`. Display clients may append a `.gno` suffix; it is presentation
  only.
- Hierarchy is `label.parent` (`wallet.alice`, `prod.api.company`).
- Labels: lowercase `a–z`, digits `0–9`, and `-` (not leading/trailing).
  1–63 bytes per label, ≤255 bytes and ≤16 labels total. Input is lowercased;
  **non-ASCII is rejected**. All canonicalization happens in `Normalize`.

## Lifecycle

`Available → Committed → Active → Grace → Expired → (recycled)`, plus `Deleted`
for removed subnames and `Reserved` for admin-held names.

- **Active**: `now < ExpiresAt`. Owner and permitted operators may mutate.
- **Grace**: `ExpiresAt ≤ now < GraceEndsAt`. Only renewal by the existing owner;
  not registrable by others.
- **Expired**: `now ≥ GraceEndsAt`. Registrable again; old state is cleared on
  re-registration and `Generation` is incremented so clients can detect the
  replacement. Subnames carry `ParentGeneration` and do **not** silently
  survive a recycled parent.

## Commit–reveal

The commitment binds the reveal so observers can neither copy nor front-run it:

```
commitment = sha256hex( name | owner | duration | secret | recordsHash | policyRevision )
```

where `|` is `"|"`, `owner` is the bech32 string, integers are base-10, and
`name` is the **normalized** name. Use the on-chain helper `MakeCommitment(...)`
to compute it identically to what `Register` recomputes at reveal.

## Pricing

Deterministic and boring — no oracle, no USD, no auction:

```
price = duration × BasePricePerSecond × lengthMultiplier(label)
```

Default length multipliers: 1→100, 2→25, 3→5, 4→2, 5+→1. `Price(name, duration)`
returns a quote; `Register` always recomputes from state and rejects a stale
`policyRevision`.

## Authorization

Every mutation funnels through one internal `authorize(caller, name, permission)`
with a fixed authority order:

1. **Admin** — emergency/protocol operations only; **never** routine power over
   user names or records (admin cannot confiscate).
2. **Direct owner** of an active name.
3. **Active operator** holding the matching permission.
4. **Parent authority**, only where the child `ControlPolicy` allows it.

## Gno-specific deviations from the spec

The spec is written with Ethereum/Go idioms; these are the deliberate,
gno-correct adaptations:

- **Mutations panic, they don't return `error`.** In gno only a panic/abort
  reverts state, so state-changing crossing functions (those taking `cur realm`)
  panic with a **stable machine-readable error code** (`unauthorized`,
  `name_unavailable`, `commitment_missing`, …). Read/quote functions return
  `(value, ok)` or `(value, error)` normally.
- **`avl.Tree` instead of Go maps** for every enumerable collection, so all
  listing APIs (`NamesByOwner`, `Subnames`, `TextKeys`, `CoinTypes`,
  `Operators`, `EventsAfter`) are ordered, bounded, and cursor-based — there is
  no unbounded "return everything" query.
- **String-native storage** rather than namehashes (hashing is used only for
  commitments and event digests).

## Public API (summary)

Read: `Normalize`, `Status`, `Exists`, `OwnerOf`, `GetName`, `Resolve`,
`Address`, `CoinAddress`, `Text`, `ContentHash`, `PublicKey`, `ABI`,
`Interface`, `Record`, `PrimaryName`, `Price`, `MakeCommitment`,
`CommitmentStatus`, `NamesByOwner`, `Subnames`, `TextKeys`, `CoinTypes`,
`Operators`, `EventsAfter`, `EventsForName`, `Render`.

Mutations (crossing): `Commit`, `Register`, `Renew`, `Transfer`,
`CreateSubname`, `DeleteSubname`, `SetRegistrationPolicy`, `LockPolicy`,
`SetOperator`, `RemoveOperator`, `SetPrimaryName`,
`ClearPrimaryName`, and the typed record setters (`SetAddress`, `SetText`,
`SetCoinAddress`, `SetContentHash`, `SetPublicKey`, `SetABI`, `SetInterface`,
`SetRecord`, `SetTTL`).

Admin (two-step transfer): `SetPaused`, `SetRegistrationOpen`, `SetPricing`,
`SetTreasury`, `ReserveName`, `SetLimits`, `TransferAdmin`, `AcceptAdmin`.

## Render explorer

`Render(path)` serves a read-only Markdown explorer:

```
/                 overview + stats
/name/<name>      owner, status, expiry, records, subnames
/address/<g1...>  verified primary name + owned names
/available/<name> availability + price
/events           recent events
/help             API summary
```

## Building & testing

```sh
export GNOROOT=/path/to/gnolang/gno   # a gno master checkout
gno lint .
gno test .
```

The test suite covers the spec's critical invariants: single effective owner,
expired owners lose authority, grace names aren't re-registrable, parents can't
exceed child policy, permanent policies only tighten, forward-verified primary
names, single-use commitments, deterministic overflow-safe pricing, generation
recycling without stale-record leakage, bounded enumeration, and admin
non-confiscation.

> **Testing note.** Because this realm is developed outside the gno examples
> module, it uses local assertion helpers instead of `gno.land/p/nt/uassert`
> (whose working-tree copy fails to preprocess for external packages), and it
> unit-tests rejection paths against the internal error-returning helpers
> (`authorize`, `priceFor`, `available`, `mergeRestrictive`, …) rather than by
> catching crossing-boundary aborts. End-to-end abort/`// Error:` filetests can
> be added once the realm lives in-tree.

## Client caching

Reverse resolution is forward-verified on-chain, so clients and indexers may
safely cache the **user ↔ address** mapping:

- **positive match** (an address has a verified primary name, or a name resolves
  to an address): cache for up to **1 hour**;
- **negative result** (no primary name / no match): cache for only **1 minute**,
  so a freshly-set name becomes visible quickly.

## Status

This is a v1 implementation of the [GNS design spec][spec]. It is staged
for review; it is not deployed.

[spec]: https://gist.github.com/moul/1c160b2cb9ee080714b3d1933ddea60a

<!-- 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.

**Dependency graph:**

![gno.land/r/moul/gns/v0 dependency graph](https://raw.githubusercontent.com/moul/gno-contracts/main/_assets/gno.land/r/moul/gns/v0/deps.png)

> ⚠️ **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 -->

Functions

  • ABI(name string, contentType string) ([]uint8, bool)

  • AcceptAdmin(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})

  • Address(name string) (string, bool)

  • ClearPrimaryName(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})

  • CoinAddress(name string, coinType string) ([]uint8, bool)

  • CoinTypes(name string, cursor string, limit uint16) struct{Items []string; Next string}

  • Commit(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}, commit string)

  • CommitmentStatus(commit string) struct{Exists bool; Committer string; CreatedAt int64; ReadyAt int64; ExpiresAt int64}

  • ContentHash(name string) ([]uint8, bool)

  • CreateSubname(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}, parent string, label string, owner string, options struct{Duration int64; ControlPolicy gno.land/r/moul/gns/v0.ControlPolicy; NativeAddress string})

  • DeleteSubname(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}, name string)

  • EventsAfter(id uint64, limit uint16) struct{Items []gno.land/r/moul/gns/v0.Event; Next string}

  • EventsForName(name string, after uint64, limit uint16) struct{Items []gno.land/r/moul/gns/v0.Event; Next string}

  • Exists(name string) bool

  • GetName(name string) (struct{Canonical string; Owner string; Status string; CreatedAt int64; UpdatedAt int64; ExpiresAt int64; GraceEndsAt int64; Parent string; Label string; Depth uint8; TTL uint64; Generation uint64; Revision uint64; Reserved bool; NativeAddr string}, bool)

  • Interface(name string, interfaceID string) (string, bool)

  • LockPolicy(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}, name string, restrictions struct{OwnerCanTransfer bool; OwnerCanCreateSubnames bool; RecordsMutable bool; ParentCanReclaim bool; ParentCanTransfer bool; ParentCanDelete bool; ParentCanChangePolicy bool; Permanent bool})

  • MakeCommitment(name string, owner string, duration int64, secret string, recordsHash string, policyRevision uint64) (string, interface {Error func() string})

  • NamesByOwner(owner string, cursor string, limit uint16) struct{Items []string; Next string}

  • Normalize(name string) (string, interface {Error func() string})

  • Operators(name string, cursor string, limit uint16) struct{Items []gno.land/r/moul/gns/v0.OperatorView; Next string}

  • OwnerOf(name string) (string, bool)

  • Price(name string, duration int64) (struct{Amount int64; Denom string; ValidUntil int64; Revision uint64}, interface {Error func() string})

  • PrimaryName(addr string) (string, bool)

  • PublicKey(name string) ([]uint8, bool)

  • Record(name string, namespace string, key string) ([]uint8, bool)

  • Register(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}, request struct{Name string; Owner .uverse.address; Duration int64; Secret string; RecordsHash string; PolicyRevision uint64; NativeAddress string; SetPrimary bool}) struct{Canonical string; Owner string; ExpiresAt int64; Generation uint64; Paid int64; Refunded int64}

  • RemoveOperator(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}, name string, operator string)

  • Render(path string) string

  • Renew(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}, name string, duration int64) struct{Canonical string; ExpiresAt int64; Paid int64}

  • ReserveName(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}, name string, reserved bool)

  • Resolve(name string, query struct{Kind string; Key1 string; Key2 string}, mode int) struct{Found bool; Requested string; SourceName string; Value []uint8; Revision uint64; ExpiresAt int64}

  • SetABI(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}, name string, contentType string, value []uint8)

  • SetAddress(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}, name string, addr string)

  • SetCoinAddress(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}, name string, coinType string, value []uint8)

  • SetContentHash(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}, name string, value []uint8)

  • SetInterface(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}, name string, interfaceID string, target string)

  • SetLimits(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}, maxRecords uint16, maxOperators uint16, maxText uint32, maxBinary uint32)

  • SetOperator(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}, name string, operator string, permissions struct{ManageRecords bool; ManageOperators bool; ManageSubnames bool; Renew bool; Transfer bool; ManagePolicy bool; ExpiresAt int64})

  • SetPaused(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}, paused bool)

  • SetPricing(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}, pricing struct{BasePricePerSecond int64; PremiumByLength map[uint8]int64; PaymentDenom string})

  • SetPrimaryName(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}, name string)

  • SetPublicKey(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}, name string, value []uint8)

  • SetRecord(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}, name string, namespace string, key string, value []uint8)

  • SetRegistrationOpen(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)

  • SetRegistrationPolicy(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}, name string, policy struct{Mode string; MinDuration int64; MaxDuration int64; PricePerSecond int64; PaymentDenom string; Allowlist *gno.land/p/nt/avl/v0.Tree; DefaultControlPolicy gno.land/r/moul/gns/v0.ControlPolicy})

  • SetText(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}, name string, key string, value string)

  • SetTreasury(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}, treasury string)

  • SetTTL(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}, name string, ttl uint64)

  • Status(name string) string

  • Subnames(parent string, cursor string, limit uint16) struct{Items []string; Next string}

  • Text(name string, key string) (string, bool)

  • TextKeys(name string, cursor string, limit uint16) struct{Items []string; Next string}

  • Transfer(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}, name string, newOwner string, clearOperators bool, clearRecords bool)

  • TransferAdmin(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}, next 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.