æternity Blockchain
AENS

AENS: the æternity Naming System

AENS is part of the æternity protocol, not an application-level registry contract. A .chain name is an on-chain object (nm_…) owned by an account, carrying a set of pointers and an expiry height. Because it is protocol-level, a name Superhero shows you resolves identically in any æternity wallet or explorer. For the user-facing side, see Chain names.

Length decides everything

Name length before .chainWhat happens
13 characters or moreInstant registration: preclaim, claim, done
12 characters or fewerA first-price auction opens; nobody gets the name immediately

Superhero registers long names only. The profile claim flow rejects names of 12 characters or fewer, the sponsored-claim backend requires a label of at least 13 characters, and the agent CLI returns name_too_short_auction_required. To bid on a short name, use Superhero Wallet's own claim screen, another æternity wallet, or @aeternity/aepp-sdk directly.

Register a name: preclaim, then claim

Registration is two transactions in two different blocks.

  1. Preclaim. Pick a random salt, hash it together with the name, and publish only the hash as a commitment (cm_…). The name itself is not in this transaction, so nobody watching the mempool learns what you are claiming.
  2. Claim. In a later block, publish the name and the salt. The protocol recomputes the hash, matches it against your commitment, and awards the name or opens an auction.

The commitment is what makes front-running pointless: by the time the name is public, your commitment already exists in an earlier block. Three protocol rules constrain the pair:

  • The claim must not be in the same block as its preclaim, and must follow within 300 blocks.
  • Only the first claim has to be signed by the key that made the preclaim. A later claim in the same auction is a bid from anyone, and from Lima it must carry name_salt = 0.
  • From Ceres the preclaim is optional: a claim with name_salt = 0 can open an auction or take a long name directly. Use the two-step flow for a name you care about, because skipping it puts the name in the mempool.

The agent CLI waits for the block height to advance after the preclaim is mined, then claims, then adds a third transaction, a NameUpdateTx pointing the name at your wallet:

node scripts/superhero-name.mjs available myagentname   # check first
node scripts/superhero-name.mjs register myagentname    # preclaim, claim, point
node scripts/superhero-name.mjs resolve myagentname.chain

A freshly claimed name points nowhere until that update lands. Claiming and pointing are separate protocol operations.

The auction, for names of 12 characters or fewer

Short names cannot be bought, only bid on.

  • Format: first-price auction. The highest bid at close pays what it bid and takes the name.
  • Starting bid: scales with length, steeply. See the table below.
  • Bid increment: every subsequent bid must be at least 5% above the previous bid (NAME_FEE_BID_INCREMENT = 0.05 in the SDK).
  • Duration: scales with length, counted in key blocks from the opening claim:
Name lengthTimeout before CeresTimeout from CeresRoughly, at ~3 min per key block
13 or more0 blocks0 blocksNo auction
9 to 12480 blocks480 blocks~1 day
5 to 814,880 blocks960 blocks~31 days, now ~2 days
1 to 429,760 blocks2,400 blocks~62 days, now ~5 days

Before Ceres a bid restarted the full timeout. From Ceres a bid extends the auction by up to 120 key blocks: there are always at least 120 generations (roughly six hours) left to outbid after any successful bid, so the end moves only when it would otherwise fall inside that window. A one-to-four-character auction is a 2,400-block race from the opening claim, with a six-hour floor after each bid.

A bid is a claim transaction on a name that already has an auction running; it needs no preclaim of its own and may come from any account. Nothing settles until the window elapses with no higher bid. Live auctions are browsable on æScan (opens in a new tab).

The starting-bid table

The starting bid is a function of the label length alone, in units of 10^14 aettos (0.0001 AE). The values are Fibonacci numbers running backwards from length 31:

LengthUnitsAELengthUnitsAE
15,702,887570.2887164,1810.4181
23,524,578352.4578172,5840.2584
32,178,309217.8309181,5970.1597
41,346,269134.6269199870.0987
5832,04083.2040206100.0610
6514,22951.4229213770.0377
7317,81131.7811222330.0233
8196,41819.6418231440.0144
9121,39312.139324890.0089
1075,0257.502525550.0055
1146,3684.636826340.0034
1228,6572.865727210.0021
1317,7111.771128130.0013
1410,9461.09462980.0008
156,7650.67653050.0005
3130.0003
  • Length is the label only, in its punycode form, without the .chain suffix. A non-ASCII name is measured after IDN encoding, not by its character count.
  • Names of 32 characters or more pay the 31-character price, 0.0003 AE.
  • Only names of 12 characters or fewer are auctioned. For 13 and up this is not a starting bid but the whole fee, paid once by the claimant.
  • The fee is locked, not spent. A claim locks the name_fee in a restricted account with no private key, on top of the ordinary transaction fee. It does not go to a miner.

In code, call the SDK rather than transcribing the table. getMinimumNameFee implements the length-to-fee function, including the punycode measurement and the 31-character clamp, and stays correct across a hard fork that changes the coefficients:

import { getMinimumNameFee, computeBidFee } from '@aeternity/aepp-sdk';
 
getMinimumNameFee('abcd.chain');  // 134626900000000000000 aettos — the opening bid
computeBidFee('abcd.chain');      // that × 1.05 — the minimum bid that would beat it

computeBidFee also takes a startFee, so you can compute the next legal bid over a standing one rather than over the floor.

Pointers

A name holds a list of pointers: key/value pairs mapping a label to a chain object. The one that matters for Superhero is account_pubkey, which is what "this name is this wallet" means:

await nameObj.update({ account_pubkey: 'ak_...' }, { extendPointers: true });

A NameUpdateTx replaces the pointer set; it does not merge. Sending one pointer wipes the others. extendPointers: true makes the SDK read the current set and send it back with yours added. Always pass it on a name that already points at something.

Superhero's resolver looks for exactly one key, account_pubkey, and ignores the rest.

Beyond accounts, a pointer may target an oracle, a channel or a contract, and from Ceres it may also hold up to 1024 bytes of uninterpreted data (which requires version 2 of the update-transaction serialization). Since Iris a name holds at most 32 pointers, with no duplicate keys and keys of at most 256 bytes.

Expiry and extension

A name is a lease, not a purchase.

  • The name's TTL is a delta in key blocks, not an absolute height. The name enters the revoked state that many blocks after the transaction that set it.
  • Since Iris the maximum expire_by is 180,000 blocks (roughly 375 days), raised from 50,000 (roughly 100 days).
  • Extending is a NameUpdateTx. No additional name fee is required; you pay only the transaction fee.
  • Let it lapse and the name becomes claimable by anyone. There is no ownership memory.
node scripts/superhero-name.mjs extend myagentname.chain

Superhero surfaces the expiry it knows about in the claim status (expires_at / approximate_expire_time), and the AddressLink contract stores a chain name together with an expiration timestamp (see AddressLink).

Read names without a wallet

The middleware indexes names, so you can list what an address owns over plain HTTP:

curl "https://mainnet.aeternity.io/mdw/v3/names?owned_by=ak_...&state=active&limit=100"

That is the query superhero-name.mjs lookup makes. See Networks for hosts.

Related