@sorandomains/lookup (0.4.x) adds that layer — and because some of it can’t be answered from chain state alone, every method below tells you exactly which trust bucket it draws from:
- Chain-verified — read from the contracts; can’t be forged by anyone, including Soran.
- Hint-discovered, chain-verified — a hosted indexer suggests candidates, the SDK verifies each one on chain. The hint can omit; it can never forge.
- Indexed — served by the indexer, marked as informational. Used only where the chain stores nothing (timestamps, history).
Every name a wallet holds
holder_of_node, which the contract expiry-gates. A hostile or buggy hint can hide a name from the list; it cannot put a name into your wallet. Without hintUrl the method throws a CONFIG error explaining this. Up to 40 candidates are verified per call; treat a 40-name result as possibly partial.
All reverse names, not just one
reverseLookup answers with one display name. reverseNames returns every contract-verified reverse claim, with the cross-namespace primary flagged:
reverseLookup; the primary is included even when its namespace wasn’t probed.
Standard profiles
Text records are free-form — so the SDK standardizes which keys everyone looks for:- Values are holder-authored free text. The chain proves who published them, not that they’re true. Escape them, and don’t auto-link without scheme checks.
- Publishing: the holder signs
set_texton the namespace’s resolver with these keys — one call with@sorandomains/holder’ssetProfile, or any Soroban client. Use the standard keys and every Soran-aware wallet can render the profile.
text(name, key) but are not enumerable on chain — that’s a contract-storage property, not an SDK gap.
One-call aggregates
identity() renders a name’s page: the details() aggregate, the holder’s published profile, and three enrichments — the holder’s primary name, the pay-to address’s verified display name, and the namespace owner’s primary name (who runs this namespace, as a name instead of a G-address). Core fields are fail-closed; the three enrichments degrade to null on transient failure rather than blanking the page.
walletProfile() renders an address’s page. It works with only primaryId configured (you get the primary + its profile); reverse probing and holdings enumeration light up as you configure sources. Expect roughly twenty simulations per aggregate call — cache briefly.
Registration dates and history
The contracts store no timestamps — that’s deliberate (consensus state stays minimal).history() answers from the deployment’s indexer and says so:
ledger and txHash, so anything that matters can be verified independently against the chain. Requires hintUrl; outages throw rather than pretending a name has no history.
Running your own hint server
hintUrl doesn’t have to point at Soran’s hosted API. Because the hint role is low-stakes by design — the SDK verifies every candidate on chain, so a hint can omit but never forge — a namespace owner can run their own discovery source with no database and no auth: examples/hint-server in the SDK repo is a complete implementation in one file. It follows your Registrar’s chain events, persists a JSON index, serves the endpoints above for your namespace, and bootstraps names older than your RPC’s event-retention window from a simple seed.json. Prefer serverless? The Cloudflare Worker edition is the same server with zero dependencies — Cron Trigger for indexing, KV for state, and Soroban RPC’s JSON mode instead of an XDR library.