readable.name
AgentsDocs

Introduction

Readable is a registry of numbers and usernames for wallets and AI agents. A number is digits after a prefix, such as +0 4242; a username is a word under an extension, written word.extension. This page calls both names. A name points at a wallet, its holder owns it as an NFT on Base Sepolia, and any app can look it up without asking anyone.

A wallet address is 42 characters that nobody can say aloud or check by eye. A name is short enough to read out, type on a phone and recognise when it is wrong. A number carries no SIM, no country and no identity: it reaches a wallet and nothing else, so it is safe to publish.

This is a rehearsal on Base Sepolia, a test network. Names registered here are for testing and carry no value.

How it works

The whole registry is one contract. Registering a name mints a token to your wallet, and everything about the name is read from that contract.

  • One registry on one chain. Every name lives on Base Sepolia, so a name has exactly one holder.
  • A name is an NFT. It sits in your wallet like any other token, and you can transfer or sell it.
  • It is held for a paid term. You pay for one to 10 years and renew before the term ends.
  • It points at a wallet. At first that is the wallet holding it. The holder can point it at another.
  • It carries records. A display name, an avatar, a website, where an AI agent answers, and addresses on chains that aren’t EVM.
  • Only its holder changes it, and an editor it names. The registry has no function that takes a name from its holder, for the registry’s owner either: see what the owner can do.

Numbers

A number is written +0 42421230: the prefix +0, then the digits, unbroken. No country’s phone code starts with 0, so a Readable number can never be mistaken for a phone number.

Length
2 to 12 digits
Check digit
The last digit, in numbers of 8 digits and longer
Stored as
42421230
Shortest
+0 42
  • Spaces and dashes don’t count. +0 4242 1230, +0-4242-1230 and 42421230 are all +0 42421230. The registry stores the digits alone.
  • No number starts with 0. A zero in front would add nothing to the check digit and make a twin of the number after it, so 04242 is read as +0 4242, its prefix written without the plus.
  • Short numbers are the rare ones. Only 100 numbers have 2 digits, and they are priced to match. Long numbers cost very little.
  • Long numbers check themselves. From 8 digits, the last digit is worked out from the others, so one mistyped digit fails instead of reaching a stranger. See the check digit.

Usernames and extensions

A username is a word under an extension, written word.extension. The registry’s owner adds each extension onchain, and this site offers whichever ones the registry holds.

  • Letters of one script. Latin, Greek, Cyrillic, Armenian, Hebrew, Arabic and Georgian letters, and Chinese, Japanese and Korean characters, with digits and hyphens. A hyphen can’t start or end a name.
  • No look-alikes. A Greek, Cyrillic or Armenian name needs a letter that can’t pass for a Latin one, so nobody can register a copy of a Latin name drawn in other letters.
  • Lowercase. Alice and alice are the same name.
  • No emoji. A name with an emoji in it is refused under every extension.

These rules live in a contract of their own, which the registry’s owner can replace to take more scripts. New rules apply to new registrations: a name already held keeps its label, and is renewed by the length it was registered with.

An extension belongs to this registry alone. If it is spelled like the ending of another naming system, the two are unrelated, and the same name can belong to different people in each.

Get a name

  1. Search. Type the number or username you want on the home page. Its card says whether it is available, taken or reserved, and if it can’t be a name, why.
  2. Choose the years. Connect a wallet and pick one to 10 years. The card shows the total.
  3. Pay in ETH. The name is minted to your wallet in the same transaction, and points at your wallet from that moment.

It is one transaction from your wallet, which sends the exact price with it. The network fee on Base Sepolia is a small part of a cent of ETH on top.

No ETH yet? Where Coinbase’s onramp serves the chain, the payment dialog offers to buy it by card, delivered to the wallet you are paying from.

Prices

A name is priced by the year and by its length: the shorter it is, the more it costs. The registry holds the price list, and its owner can change it. A change applies to registrations and renewals from then on, never to years already paid for.

Manage a name

My names lists the names your wallet holds. The wallet holding a name, or the editor it names, signs each change, and while the site’s relayer is running it pays no fee: the wallet signs a message instead of a transaction and the relayer sends it. A smart wallet’s change goes to the site’s paymaster instead, when there is one. Renewing is a payment, so it is a transaction of its own.

Point to a wallet
Where the name delivers. Hold the name in a wallet you keep safe and point it at the one you use.
Make primary
The one name apps show in place of your address. A wallet has one primary name at a time.
Records
What the name says about you or your agent, saved together in one change.
Editor
Another wallet, such as the agent the name stands for, that can point it, fill in its records and set its other chains. It can’t move the name, and it goes when the name changes hands.
Renew
Adds years to the term.
Remind me to renew
A calendar file with a reminder a month and a week before the name expires. The site keeps no address and sends nothing.

Watching a name. Someone else’s name can be watched from its card. My names lists the names you watch, in this browser only, with the day each returns to the public unless its holder renews it, and a calendar file for that day.

Pay a name

Anyone can send ETH to a name from its page or its search card: +0 4242 instead of a 0x… address. The wallet the name points to is looked up again the moment you sign, so a name that changed hands since the page loaded pays its new holder, and a name that reaches nobody is refused.

Share and prove

Every name has a page of its own, /names/+0-4242, with its contact card, a Pay button and a code to scan. A link to it shows the name in its preview wherever it is pasted.

A badge puts the name on any site or README, linked to its page:

Markdown
[![+0 4242 on readable.name](https://www.readable.name/api/badge/+0-4242)](https://www.readable.name/names/+0-4242)

Proven accounts. Records say what their holder writes. To prove an X or GitHub account is yours, the wallet holding the name signs a statement, the account posts it, and the post’s link goes into the records. The site checks that the post is the account’s and the signature the current holder’s, and shows a check beside the account. A transfer clears the records, proofs included.

Renewal and expiry

A name is yours until its term ends. Renewing adds years to the end of the term, so renewing early loses nothing. Anyone can pay for a renewal, not only the holder.

During the term
The name resolves, and its holder can point it, fill it in and transfer it.
For 30 days after
The grace period. The name reaches nobody and can’t be changed or transferred, but it is still held: it can be renewed, and nobody else can register it.
After that
Anyone can register it. It starts clean, pointing at its new holder, with no records.

A name stops delivering the moment its term ends, not at the end of the grace period. Renew before the date on the name’s card.

Transfers

A name moves like any NFT: send it from your wallet, or sell it on a marketplace. The registry resets it as it moves, so a name that changes hands never keeps delivering to whoever held it before.

  • It points at its new holder. Wherever the previous holder had pointed it is forgotten.
  • Its records are cleared. The previous holder’s avatar, website and agent payment address are gone.
  • Its editor is removed. Whoever the previous holder let edit the name no longer can.
  • It stops being the sender’s primary name. The new holder makes it theirs if they want it to be.
  • The term stays. The new holder gets the years that were left. A name whose term has ended can’t be transferred.

The registry

Everything below reads one contract. It needs no key, no account and no permission: any wallet, app or agent can call it.

Network
Base Sepolia (chain id 84532)
Token
Readable (READ), ERC-721
Paid in
ETH, the price sent with the transaction

What to read

resolve(tokenId)
The wallet a name delivers to. The zero address when it reaches nobody.
lookup(namespaceId, label)
Everything about one name in a single read: whether it is valid, available, who holds it, until when, and its price.
tokenIdOf(namespaceId, label)
The token id of a name.
primaryNameOf(account)
The name a wallet shows in place of its address.
namesOf(account)
The token ids a wallet holds.
recordOf(tokenId)
A name’s label, where it points, and when its term began and ends. Where it points outlives the term: send with resolve.
text(tokenId, key)
One stored text of a name.
addr(tokenId, coinType)
Where a name receives on another chain, as ENS keeps it. An EVM chain answers with what resolve answers; any other is empty when none is set.
editorOf(tokenId)
The wallet its holder lets change the name, or the zero address.
generationOf(tokenId)
How many times the name has had a new holder, by registration or transfer. Key what belongs to one holder by the token id and this.
getNamespaces()
The number ranges and extensions, with their rules.
getPrices(namespaceId)
A namespace’s yearly prices, from the length in its priceFrom up.

SDK and API

The quickest way to resolve a name is readable-sdk, a small library on viem, or the API this site serves, built on it. Both read the registry and need no key. A number can be written any way people write it: +0 42421230, +0 4242 1230, 42421230 and +0-42421230 are the same.

Shell
npm install readable-sdk viem
JavaScript
import { createReadable } from 'readable-sdk'
import { baseSepolia } from 'viem/chains'

const readable = createReadable({ chain: baseSepolia, registry: '0x7c636853ECFA14e61d3A9Ec0c9501d66D17797D6' })

// '0x…', or null when it reaches nobody
await readable.resolve('+0 42421230')

// Its Bitcoin address, 'bc1q…', or null
await readable.getAddress('+0 42421230', 'btc')

// Its records: { name, job, social: { x }, … }
await readable.getRecords('+0 42421230')

// The primary name a wallet chose, or null
await readable.getName('0x…')

// All of the above in one answer
await readable.getProfile('+0 42421230')

What it reads

resolve(name)
The wallet the name reaches: what most EVM chains use.
getAddress(name, coin)
Its address on another chain: btc, sol, eth, or a coin type.
getRecords(name)
Its records document.
getName(address)
The primary name a wallet chose.
getProfile(name)
The name, its holder, wallet, addresses, records and expiry, in one read.

Each answers null for a name that reaches nobody: never registered, expired, or one that can’t exist. The reads of one call go out together, as a single multicall.

From any other language, ask the API. It answers JSON to any origin.

Shell
curl https://www.readable.name/api/resolve/+0%2042421230

{
  "name": "+0 42421230",
  "tokenId": "…",
  "owner": "0x…",
  "address": "0x…",
  "expiresAt": 1822493531,
  "addresses": { "btc": "bc1q…", "sol": null },
  "records": { "name": "Alice", "social": { "x": "alice" } }
}

curl https://www.readable.name/api/reverse/0x…

{ "address": "0x…", "name": "+0 42421230" }
/api/resolve/<name>
What getProfile answers. 404 when the name reaches nobody.
/api/reverse/<address>
The wallet’s primary name, or null.
/api/verify/<name>
Which accounts in the name’s records are proven to be its holder’s.
/api/agents
Every live name whose records reach an AI agent.
/api/badge/<name>
The name’s badge, an SVG image.

Resolve when you send. The API’s answers are kept for 30 seconds, and a name can change hands in that time. Before money moves, use the SDK, which reads the registry at each call.

For AI agents

A name can reach an AI agent: its records name the endpoint where the agent answers, its ERC-8004 id and where it is paid. Agents lists every name that does.

Agents that speak MCP can use names without any code. Add this site’s MCP server, which needs no key:

JSON
{
  "mcpServers": {
    "readable": {
      "url": "https://www.readable.name/api/mcp"
    }
  }
}

Its tools

resolve_name
The wallet, addresses, holder, expiry and records of a name.
lookup_address
The primary name a wallet chose.
check_name
Whether a name is free, what it costs, or who holds it.
dial_agent
The endpoint, ERC-8004 id and payment address of the agent behind a name.
list_agents
Every agent with a name.
payment_details
The chain, token and recipient to pay a name now.

In code, getAgent(name) of the SDK answers the same as dial_agent.

Names for the agents your app runs. registerFor(namespaceId, label, to, years) buys a name straight into another wallet, the agent’s or its user’s, at the price register charges. Its holder can then make the agent its editor with setEditor(tokenId, agent), so the agent keeps its own endpoint and payment address up to date and can never sell the name. Keep a rating or a reputation by the token id and generationOf(tokenId), so it never passes to whoever holds the name next.

In wallets and apps

Where a name works outside this site. Readable is new, and few apps read its names yet: building a registry is the easy part, and a name is only as useful as the apps that read it. Those marked Next are built and wait for their launch, those marked Planned are not built yet, and the roadmap says what comes after. Any app can read names today through the SDK and API, without asking anyone.

Tunnel
New chat takes +0 4242 where it takes an address. The contact keeps the number and the wallet it reached, and warns if they part.
Hup
A profile shows the number its wallet chose as its primary name.
Mini apps
The site runs inside Hup with the wallet the app already has.
ENS · Next
Through a gateway on this site, a wallet that reads ENS resolves 4242.readable.eth to the wallet +0 4242 points to, and its text records to the name’s records.
MetaMask · Next
The Readable Snap resolves +0 4242 typed into MetaMask’s send field, and shows the name of a wallet that chose one.
PayPost · Planned
Send an invoice to +0 4242, and pay one.

Resolve a name

Resolving is three steps: find the namespace, reduce what was typed to the label the registry stores, and ask where it points. This is +0 42421230 with viem.

JavaScript
import { createPublicClient, http, parseAbi, zeroAddress } from 'viem'
import { baseSepolia } from 'viem/chains'

const registry = '0x7c636853ECFA14e61d3A9Ec0c9501d66D17797D6'
const abi = parseAbi([
  'struct Namespace { uint256 id; uint8 kind; string name; uint8 minLength; uint8 maxLength; uint8 checkDigitFrom; uint8 priceFrom; bool paused; address manager; uint8 managerShare; string metadata; uint64 createdAt; }',
  'function getNamespaces() view returns (Namespace[])',
  'function tokenIdOf(uint256 namespaceId, string label) view returns (uint256)',
  'function resolve(uint256 tokenId) view returns (address)',
  'function primaryNameOf(address account) view returns (uint256 tokenId, string name)',
  'function text(uint256 tokenId, string key) view returns (string)',
  'function addr(uint256 tokenId, uint256 coinType) view returns (bytes)',
])

const client = createPublicClient({ chain: baseSepolia, transport: http() })
const read = (functionName, args) => client.readContract({ address: registry, abi, functionName, args })

// 1. Find the namespace. Numbers are kind 1, named by the digits of their prefix
const namespaces = await read('getNamespaces')
const numbers = namespaces.find((namespace) => namespace.kind === 1 && namespace.name === '0')

// 2. Reduce what was typed to the stored label: the prefix and every separator go
const label = '+0 42421230'.replace(/^\+0/, '').replace(/\D/g, '')

// 3. Resolve. The zero address means nobody: never registered, or expired
const tokenId = await read('tokenIdOf', [numbers.id, label])
const wallet = await read('resolve', [tokenId])
if (wallet === zeroAddress) throw new Error('This number reaches nobody')

For a username, the label is the word before the dot, in lowercase and in Unicode’s composed form: word.normalize('NFC').toLowerCase(). The namespace is the one of kind 0 named by the extension.

Deliver with resolve, not with lookup. lookup answers what a search card shows, and keeps naming the holder through the grace period. resolve stops the moment the term ends.

From a contract

A contract on Base Sepolia resolves a number in one line. This one pays ETH to whoever a number reaches, and refuses when it reaches nobody.

Solidity
interface IReadable {
    function tokenIdOf(uint256 namespaceId, string calldata label) external view returns (uint256);
    function resolve(uint256 tokenId) external view returns (address);
}

contract PayANumber {
    IReadable constant READABLE = IReadable(0x7c636853ECFA14e61d3A9Ec0c9501d66D17797D6);
    // The id of the number namespace, from getNamespaces()
    uint256 constant NUMBERS = 1;

    error NobodyThere(string digits);
    error NotDelivered();

    /// @param digits The number without its prefix or spaces
    function pay(string calldata digits) external payable {
        address to = READABLE.resolve(READABLE.tokenIdOf(NUMBERS, digits));
        if (to == address(0)) revert NobodyThere(digits);
        (bool delivered, ) = to.call{value: msg.value}("");
        if (!delivered) revert NotDelivered();
    }
}

tokenIdOf reverts for a namespace that doesn’t exist and for a label with a character its namespace never allows. A number that is well formed and was never registered resolves to the zero address.

From a wallet to its name

To show a name where you would show an address, read the wallet’s primary name. The registry answers only with a name the wallet holds now and whose term has not ended.

JavaScript
const [tokenId, name] = await read('primaryNameOf', [account])

// No token id and an empty name: the account has chosen none, or the one it chose has expired
if (tokenId === 0n) showAddress(account)
else showName(name)
  • The name arrives ready to show. The registry writes a number as this site does, +0 42421230, and a username as word.extension.
  • Holding is not pointing. A primary name says who the wallet is. If you need the name to deliver to that same wallet, compare resolve(tokenId) with it.
  • namesOf includes names whose term has ended. They stay in the wallet until someone else registers them. Check expiresAt in recordOf.

Records

A name’s records are one JSON document, stored under the text key records. One document means a holder saves everything in a single transaction and an app reads it in a single call.

JSON
{
  "v": 1,
  "avatar": "ipfs://…",
  "name": "Alice",
  "job": "Designer",
  "about": "Making things readable",
  "email": "alice@example.com",
  "url": "https://alice.example",
  "location": "Lisbon",
  "birthday": "1990-05-14",
  "social": {
    "x": "alice",
    "telegram": "alice_lisbon",
    "github": "alice",
    "hup": "alice",
    "up": "0x…"
  },
  "agent": {
    "endpoint": "https://alice.example/mcp",
    "erc8004": "412",
    "payment": "0x…"
  }
}
avatar
Photo. A link to an image, or one uploaded here and pinned on IPFS.
name
Display name
job
Job title
about
About
email
Email
url
Website
location
Location. A city or a country, never a street address.
birthday
Birthday
social.x
X handle, without the @. Read at https://x.com/<handle>.
social.telegram
Telegram handle, without the @. Read at https://t.me/<handle>.
social.github
GitHub handle, without the @. Read at https://github.com/<handle>.
social.hup
Hup handle, without the @. Read at https://www.hup.social/@<handle>.
social.up
A LUKSO Universal Profile, by its address. The contact card shows the name and picture it publishes.
agent.endpoint
Agent endpoint. Where the agent answers.
agent.erc8004
Agent id (ERC-8004)
agent.payment
Agent payment address
JavaScript
const stored = await read('text', [tokenId, 'records'])

// Written by the holder, so anything can be in it
let records = {}
try {
  records = JSON.parse(stored || '{}')
} catch {}

const endpoint = records.agent?.endpoint
  • Every field is optional, and a name with no records answers with an empty text.
  • Your app can keep its own keys. This site leaves the keys it doesn’t know as they are when a holder saves.
  • Records are what the holder says. Nothing checks them. Treat a link or a payment address as a claim by whoever holds the name.
  • Where a name points is not a record. It is what resolve answers, and the holder sets it with setTarget.
  • Addresses on other chains are not records either. They have a call of their own: see other chains.

Other chains

A name can also carry an address on chains that aren’t EVM, kept as ENS keeps them (ENSIP-9). addr(tokenId, coinType) answers one, in the chain’s own binary form, and empty bytes when the holder set none. Base Sepolia and most other EVM chains use what resolve answers, and addr gives the same for any EVM chain’s coin type.

Coin types the site offers

Bitcoin
0
Solana
501

A coin type is a chain’s number in SLIP-44, the list wallets use to tell chains apart in their key paths (m/44'/0'/… for Bitcoin). The registry takes any coin type but an EVM chain’s, so another chain is a change to an app, not to the contract.

A coin type is not a chain id. A chain id tells EVM networks apart so a transaction signed for one can’t run on another; a coin type names a chain’s address format. Ethereum’s chain id is 1 and its coin type 60; Bitcoin and Solana have no chain id. ENS gives an EVM chain the coin type 0x80000000 | chainId (ENSIP-11), so Base Sepolia, chain 84532, is 2147568180. Readable stores nothing under those: asked for one of them, or for 60, addr answers with the address resolve gives, and setAddrs refuses them.

Turn the bytes into text with @ensdomains/address-encoder, whose coder for each chain knows its coin type.

JavaScript
import { btc, sol } from '@ensdomains/address-encoder/coins'
import { hexToBytes } from 'viem'

// Each coder knows its chain's coin type and turns the stored bytes back into text
const addressOn = async (coin) => {
  const bytes = await read('addr', [tokenId, BigInt(coin.coinType)])
  // 0x: the holder set none on that chain
  return bytes === '0x' ? null : coin.encode(hexToBytes(bytes))
}

const bitcoin = await addressOn(btc) // 'bc1q…'
const solana = await addressOn(sol)

The holder sets them with setAddrs: a token id and a list of coin types with their bytes. The registry stores the bytes as they come and never reads them, so check an address before sending it.

JavaScript
import { btc, sol } from '@ensdomains/address-encoder/coins'
import { bytesToHex, parseAbi } from 'viem'

const abi = parseAbi([
  'struct CoinAddress { uint256 coinType; bytes value; }',
  'function setAddrs(uint256 tokenId, CoinAddress[] entries)',
])

// Several chains in one transaction. Empty bytes remove one;
// a chain left out keeps its address
await wallet.writeContract({
  address: registry,
  abi,
  functionName: 'setAddrs',
  args: [
    tokenId,
    [
      { coinType: BigInt(btc.coinType), value: bytesToHex(btc.decode('bc1q…')) },
      { coinType: BigInt(sol.coinType), value: '0x' },
    ],
  ],
})
  • They belong to one holding, as records do. A transfer or a new registration starts with none, and an expired name answers with none.
  • They are what the holder says. Nothing proves the holder controls the address.

Token ids and NFT data

A name’s token id is the hash of its namespace id and its label, so an app can work it out without a call.

JavaScript
import { encodePacked, keccak256 } from 'viem'

// keccak256(abi.encodePacked(namespaceId, label)) in Solidity
const tokenId = BigInt(keccak256(encodePacked(['uint256', 'string'], [1n, '42421230'])))

What a wallet or a marketplace shows for the token comes from the link the registry gives as tokenURI. The registry’s owner sets where that link leads, and this site serves it: the name as its title, and a picture of the name.

/nft/<id>
The token’s title, description, picture and traits, as JSON.
/nft/<id>/image
The picture, as SVG.

The check digit

Numbers of 8 digits and longer end in a check digit, worked out from the digits before it by the Luhn formula that payment cards use. You choose all the digits but the last.

Of the ten numbers that differ only in their last digit, one is valid. A single mistyped digit, and most swaps of two neighbours, give a number that can’t exist, so the mistake is caught before anything is sent.

JavaScript
// Luhn: from the right, double every second digit, and the sum must end in 0
const luhnValid = (digits) => {
  let sum = 0
  for (let i = 0; i < digits.length; i++) {
    let digit = Number(digits[digits.length - 1 - i])
    if (i % 2 === 1) {
      digit *= 2
      if (digit > 9) digit -= 9
    }
    sum += digit
  }
  return sum % 10 === 0
}

// The one digit that completes the digits before it
const checkDigitFor = (body) => [...'0123456789'].find((digit) => luhnValid(body + digit))

const valid = (digits) => digits.length < 8 || luhnValid(digits)

The registry runs the same check: lookup answers a wrong check digit with the validity InvalidCheckDigit (6), and register refuses it. Check in your app first, and the reader learns about the typo without a call.

Events

An indexer follows these.

NameRegistered
A name was registered, by payment or as a grant from the owner.
NameRenewed
A term was extended.
TargetChanged
A name points at another wallet. A transfer emits it too.
PrimaryNameChanged
A wallet chose its primary name, or lost it. Token id 0 means none.
TextChanged
A stored text was written.
AddressChanged
An address on another chain was set or removed. An empty value means none.
EditorChanged
A holder named an editor, or removed it. The zero address means none.
Transfer
The ERC-721 event: a name changed hands.
NamespaceAdded, PricesUpdated
What the registry sells, and for how much.

Treat a transfer and a new registration as a reset. Both clear the name’s stored texts, addresses and editor without a TextChanged, AddressChanged or EditorChanged for each. An index that keeps the old values would show the previous holder’s records.

In your app

A name can change hands or run out. Five habits keep that from hurting anyone.

  • Resolve when you use it. Ask the registry at the moment of sending, not once at sign-up.
  • Remember who it was. When someone saves a contact, keep the number together with the address it reached, and warn them if the two no longer match.
  • Nobody means stop. The zero address is never somewhere to send.
  • Check the digits first. Validate a number’s length and check digit before asking the chain.
  • Write it as one number. The prefix, a space, then the digits with no spaces between them: +0 42421230.

What the owner can do

The registry has an owner: one wallet that decides what is sold. What it can do ends where a registered name begins.

The owner can

Add extensions
And number ranges, each with its rules.
Replace the name rules
Which characters a username can hold, for registrations from then on. A name already held keeps its label.
Change prices
For registrations and renewals from then on. A renewal costs the price of its day, so a price its holder won’t pay ends the name with its term.
Reserve names
Keep names nobody holds out of public registration.
Grant a name
Register an available name for someone without payment.
Pause
Stop new registrations in one namespace, or in all. Renewals never stop, and names already held keep working.
Change settings
The grace period, where payments go, the forwarder, and where the NFT’s title and picture come from.

The owner can’t

Take a name
The registry has no function that moves or ends a name someone holds, and no forwarder can move one.
Change a name
Where it points and its records are changed by its holder and the editor it names, its primary status by its holder alone, with the one exception below.
Shorten a term
Years paid for are kept.

The forwarder. A holder’s or an editor’s change to a name without ETH (where it points, its primary status, its records, its other chains) reaches the registry through one forwarder contract, which the registry trusts to say who signed it. It is believed for those changes alone: purchases, transfers, approvals, the choice of an editor and every owner function answer to the wallet that calls them, so no forwarder can move a name, name an editor or act as the owner. The owner can replace the forwarder, and one it wrote could change any live name’s target, records and addresses. That is the setting to watch: every change is announced by TrustedForwarderUpdated.

Roadmap

Testnet

NumbersNumbers from 2 digits, with search, registration, renewal, records and transfers.

Next

UsernamesWords under extensions, written word.extension, which the registry’s owner adds onchain.

Testnet

Add a contact by numberIn Tunnel, the encrypted chat: type a number where you would paste an address, and be warned when the number changes hands.

Planned

Bill and pay a numberIn PayPost: send an invoice to a number, and pay one.

Testnet

Dial an agentA number that reaches an AI agent, with its endpoint and payment address in the number’s records, found through the MCP server and the agents directory.