Selendra Naming Service - Human-readable names for Selendra addresses
SNS (Selendra Naming Service) is a decentralized naming system that maps human-readable names like alice.sel to machine-readable identifiers such as addresses, content hashes, and metadata.
- Simple - Easy to register, use, and integrate
- Secure - Prevent front-running, squatting, and theft
- Decentralized - No single point of failure
- Extensible - Support multiple record types and chains
┌─────────────────────────────────────────────────────────────────┐
│ User / dApp │
└─────────────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ SDK (Viem) │
│ TypeScript/JavaScript interface for all SNS operations │
└─────────────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Smart Contracts │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
│ │ SNSRegistry │───▶│PublicResolver │───▶│ Records │ │
│ │ (Core) │ │ (Lookup) │ │ (Data) │ │
│ └───────────────┘ └───────────────┘ └───────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
│ │BaseRegistrar │ │ PriceOracle │ │ Reverse │ │
│ │ (ERC-721) │ │ (Pricing) │ │ Registrar │ │
│ └───────────────┘ └───────────────┘ └───────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────┐ │
│ │ Controller │ │
│ │(Registration) │ │
│ └───────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
The central contract storing domain ownership and resolver mappings.
Responsibilities:
- Store owner for each node (namehash)
- Store resolver address for each node
- Emit events for ownership/resolver changes
- Support operator approvals
Key Data:
node (bytes32) → Record {
owner: address
resolver: address
ttl: uint64
}
NFT representation of .sel domain ownership.
Responsibilities:
- Mint NFT when domain registered
- Track expiration times
- Handle domain transfers
- Manage authorized controllers
Key Features:
tokenId = labelhash(name)(e.g., keccak256("alice"))- Grace period: 90 days after expiry
- Tradeable on NFT marketplaces
User-facing registration interface with anti-frontrun protection.
Responsibilities:
- Implement commit-reveal scheme
- Calculate and collect fees
- Validate name requirements
- Interact with BaseRegistrar
Commit-Reveal Flow:
1. User calls makeCommitment(name, owner, duration, secret, ...)
2. User calls commit(commitment) - stores hash on-chain
3. Wait 60 seconds (MIN_COMMITMENT_AGE)
4. User calls register(...) with same parameters + payment
5. Controller validates, charges, and mints NFT
Stores and retrieves records for domains.
Supported Records:
| Type | Function | Example |
|---|---|---|
| Address (EVM) | addr(node) |
0x742d35... |
| Address (Multi-chain) | addr(node, coinType) |
Various formats |
| Text | text(node, key) |
email, avatar, url |
| Content Hash | contenthash(node) |
ipfs://Qm... |
| Name | name(node) |
alice.sel (reverse) |
Calculates registration costs based on name length.
Pricing Tiers:
| Length | Price/Year | Rationale |
|---|---|---|
| 3 chars | 1,000 SEL | Premium short names |
| 4 chars | 250 SEL | Valuable short names |
| 5+ chars | 50 SEL | Standard names |
Discounts:
- 10% off for 2+ year registrations
Maps addresses back to names for display.
Use Cases:
- Show "alice.sel" instead of "0x742d35..."
- Primary name for wallet display
- Identity verification
Hierarchical hashing algorithm (EIP-137):
namehash("") = 0x0000...0000 (32 zero bytes)
namehash("sel") = keccak256(namehash("") + keccak256("sel"))
namehash("alice.sel") = keccak256(namehash("sel") + keccak256("alice"))
Simple hash of a single label:
labelhash("alice") = keccak256("alice")
Root (0x00...00) → Deployer
└── sel → BaseRegistrar
└── alice.sel → User
└── bob.sel → User
The commit-reveal scheme prevents miners/validators from:
- Seeing desired name in mempool
- Front-running with their own registration
Timing:
- Minimum commitment age: 60 seconds
- Maximum commitment age: 24 hours
| Action | Authorized |
|---|---|
| Register new name | Anyone (via Controller) |
| Modify records | Owner or approved operator |
| Transfer domain | Owner or approved operator |
| Add controller | Registry owner (governance) |
90-day grace period after expiry:
- Owner can still renew
- Domain not available to others
- Prevents accidental loss
┌─────────┐ ┌────────────┐ ┌───────────┐ ┌──────────┐
│ User │────▶│ Controller │────▶│ Registrar │────▶│ Registry │
└─────────┘ └────────────┘ └───────────┘ └──────────┘
│ │ │ │
│ 1. commit() │ │ │
│───────────────▶│ │ │
│ │ │ │
│ [wait 60s] │ │ │
│ │ │ │
│ 2. register() │ │ │
│───────────────▶│ │ │
│ │ 3. register() │ │
│ │──────────────────▶│ │
│ │ │ 4. setOwner() │
│ │ │───────────────▶│
│ │ │ │
│ 5. NFT minted │ │ │
│◀───────────────│ │ │
┌─────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ User │────▶│ SDK │────▶│ Registry │────▶│ Resolver │
└─────────┘ └──────────┘ └──────────┘ └──────────┘
│ │ │ │
│ resolve("alice.sel") │ │
│───────────────▶│ │ │
│ │ resolver(node) │ │
│ │───────────────▶│ │
│ │ │ │
│ │◀───────────────│ │
│ │ 0x... │ │
│ │ │ │
│ │ addr(node) │
│ │───────────────────────────────▶│
│ │ │
│ │◀───────────────────────────────│
│ 0x742d35... │ │
│◀───────────────│ │
MetaMask doesn't natively support ENS on custom networks. We solve this with a MetaMask Snap that provides custom name resolution.
How it works:
User types "alice.sel" → MetaMask → SNS Snap → Selendra RPC → Address
- User types
.seldomain in MetaMask send field - MetaMask routes to SNS Snap via
onNameLookuphandler - Snap computes namehash (EIP-137) and queries SNS Registry
- Resolver returns mapped address
- MetaMask displays resolved address
Snap Permissions:
endowment:name-lookup- Custom name resolution for.selTLDendowment:network-access- RPC calls to Selendra
Supported Chains:
eip155:1961- Selendra Mainneteip155:1953- Selendra Testnet
// Display .sel name instead of address
const name = await sns.lookupAddress(userAddress);
display(name || shortenAddress(userAddress));// Resolve payment address
const payTo = await sns.resolveName("merchant.sel");
await sendPayment(payTo, amount);// Show name in transaction history
const name = await sns.lookupAddress(txFrom);
displayTx({ from: name || txFrom, ... });company.sel
├── alice.company.sel
├── bob.company.sel
└── support.company.sel
Store addresses for multiple chains:
- SEL (coin type 1961)
- ETH (coin type 60)
- BTC (coin type 0)
Community control over:
- Pricing changes
- Reserved name policies
- Protocol upgrades