alice.nova instead of GDHNO4WK…. On Soran, every reverse answer is contract-verified and unspoofable — here’s why, and how to use it.
Why reverse answers can’t be spoofed
Reverse records store the plaintext name on chain, on each namespace’s resolver. The resolver’sname_of(address) answers with that name only while two gates both pass, enforced by the contract on every read:
- The name is live — its ownership generation matches (not expired, not reissued, not transferred).
- The forward record matches —
resolve(name)still points back at the address.
null from the contract itself. There is no hint service, indexer, or SDK heuristic in the trust path, and no client-side re-check needed.
reverseLookup
reverseLookup(address, namespaces?) answers in two steps:
1
PrimaryName first (when configured)
When
primaryId is set (the testnet preset ships one), the SDK asks the PrimaryName contract first. A non-null primary wins outright — it is the address’s declared cross-namespace display name, and it is already contract-verified (see below).2
Per-namespace reverse records
With no primary, the SDK probes candidate namespaces’ resolvers for
name_of(address) — in parallel, with deterministic priority: the first non-null answer in your configured order wins.- The per-call
namespacesparam — an explicit list wins outright, and[]deliberately disables the namespace probes (the primary step still runs). - The
reverseNamespacesconstructor option. - A namespace list fetched from
hintUrl, if set — liveness only. The hint never carries answers, just which namespaces to ask; a lying or offline hint can hide a name (→null), never forge one. Hinted lists are validated and capped client-side.
reverseLookup returns null.
Failure semantics
Namespace-probe failures are fail-closed: a chain-read error throws aSoranError rather than reporting “no reverse record”. The one deliberate exception: if the primary read itself fails, the lookup silently falls through to the namespace probes — the optional primary layer must not break the baseline flow that predates it. If you need to distinguish “no primary” from “primary unreadable”, call primaryOf directly.
reverseVerify — checking a candidate
When you already have a claimed name for an address (from a payment memo, a directory, user input), verify it in one call:false; malformed names throw SoranError.
primaryOf — the cross-namespace display name
An address may declare one primary name on the platform-deployed, immutable PrimaryName contract — a single display name across namespaces (a user holdingalice.nova and alice.stellar picks one to show everywhere).
name_of gates on every read, answering null the moment the stored name stops verifying. The answer arrives pre-verified; no client-side re-check exists or is needed.
primaryOf is strictly fail-closed: a transport or ABI failure throws SoranError and never masquerades as “no primary”. It returns null only for a genuine no-primary answer, an unconfigured primaryId, or a malformed address.
Setting or clearing a primary (
set_primary / clear_primary) — like writing reverse records — is an address-authorized transaction built and signed by the user’s wallet. The SDK only reads.Cost and caching for list rendering
Eachprimary_of read performs two cross-contract calls in simulation. When rendering a list of addresses (history, contacts), apply brief client-side caching — seconds, the same discipline as resolverCacheTtlMs. Correctness never depends on the cache: the contract re-verifies on every read, so staleness can only delay noticing a change, never produce a wrong name.
Next
Assurance
Verify a resolution can’t be changed underneath you.
SDK reference
Every method, typed.