Skip to content

AENS (æternity naming system)

Introduction

This guide shows you how to perform all the operations that you need within the lifecycle of æternity naming system (AENS) using the SDK.

If you successfully claimed a name it will expire after 180000 key blocks (~375 days). You will need to update your name before it expires!

1. Claim a name

Claiming an AENS name requires you (at least) 2 transactions:

  • First you need to perform a pre-claim by providing a commitmentId (hash).
    • The commitmentId is calculated with a random salt and the provided name. The SDK automatically generates the random salt, calculates the commitmentId and includes it into the NamePreclaimTx.
  • After the NamePreclaimTx transaction has been mined you will be able to perform the actual claim of the name. When performing the actual claim via a NameClaimTx you will, depending on the length of the name:
    • immediately become owner of that name
    • initiate an auction

Pre-claim

// imports

const aeSdk = new AeSdk({ ... }) // init the SDK instance with AeSdk class

const name = 'testNameForTheGuide.chain'

const preClaimTx = await aeSdk.aensPreclaim(name)
console.log(preClaimTx)

/*
{
  blockHash: 'mh_2UsggiUaQQmEPjxLnXkXHpm1WawTWqZb1jBx6UzsQNHgirWAwd',
  blockHeight: 449499,
  hash: 'th_48RktjEutZC8TCubaq9YhjaF1cKLV9D3VRCJyzJLs7oSp4Ry6',
  signatures: [
    'sg_Da98k2EKMun1TkE7ytonRypvJwaKg9iBjp8rNcYuodFXqzRqwjQyuFQP5DhxWUpTYRzSTurrNtDmUft8eyTStjCyNqFf8'
  ],
  tx: {
    accountId: 'ak_2519mBsgjJEVEFoRgno1ryDsn3BEaCZGRbXPEjThWYLX9MTpmk',
    commitmentId: 'cm_2igvW9egddKh77gDdE8mjotmL8PzE7Tf2Q639Q5stUqWQoEfap',
    fee: 16620000000000n,
    nonce: 18,
    type: 'NamePreclaimTx',
    version: 1
  },
  rawTx: 'tx_+JkLAfhCuEBgFzAL0bPDufmzDq0558vaKtrIyRpNxCYVtkgnJjBrxDpQZHkfbwG+oRuBUAfgfrAKF0lO9mRI1zq0H6bXIq8KuFH4TyEBoQGMyNToF1flcYDSVsPl5DZ9ZY3FJWRpDRQYD32quWBEAhKhA+Jawe/spFaw823K+U59CWx+xD1i34gngUPiAAKJqv/fhg8dpTI4AAAGc20E',
  height: 449499,
  claim: [Function: claim],
  salt: 7595805618692717,
  commitmentId: 'cm_2igvW9egddKh77gDdE8mjotmL8PzE7Tf2Q639Q5stUqWQoEfap'
}
*/

Note:

  • After transaction is included, you have 300 key blocks to broadcast claim transaction with the same salt and it should be signed with the same private key as pre-claim.
    • As the pre-claim is required to avoid front running it is recommended to wait with the actual claim until at least 1 key block has been mined so that nobody knows which name you aim to claim.
    • The corresponding claim cannot be included in the same key block anyway. The protocol doesn't allow that.
  • You should check if the name is still available before performing a pre-claim. The protocol itself doesn't reject a pre-claim transaction if the name isn't available anymore.
  • As you can see above in the logs the result (preClaimTx) of the aensPreclaim has bound a claim function that you can make use of to perform the actual claim.
    • In case you want to perform the actual claim at a later point you should remember the salt that has been used for the pre-claim

Claim

This example assumes that you perform the actual claim manually by having remembered the required salt. In most cases you'd probably use the claim function that is bound to the result of the aensPreclaim function directly (see above).

Depending on the length of the name in punycode the actual claim will result in direct ownership of the AENS name or start an auction:

  • Name length > 12: ownership of the name is immediately transferred to your account
  • Name length <= 12: an auction is started
const salt = 7595805618692717 // salt from pre-claim transaction

// the minimum `nameFee` will be automatically generated by sdk
// in case you want to provide a custom `nameFee` you can so so by providing it in an options object
// in case of starting the auction `nameFee` will be the starting bid
const claimTx = await aeSdk.aensClaim(name, salt)
console.log(claimTx)

/*
{
  blockHash: 'mh_UvtuVNvK7k29G3anF48W4hgUMaFFLc1wKir55FFifgT49Egk',
  blockHeight: 449507,
  hash: 'th_2sWNusGUf2wuRn7d453wPWTAe8pKVbtNF4DqakvippsxjvF8g1',
  signatures: [
    'sg_LYsVj9zUoKFPDiSvFUWG5QaRhnqQ525YA314xMVzRhApYuPnFyMWgAQGpChKHHWyMbykBwVRrmoQtTENTFCaCoMj17CBK'
  ],
  tx: {
    accountId: 'ak_2519mBsgjJEVEFoRgno1ryDsn3BEaCZGRbXPEjThWYLX9MTpmk',
    fee: 16800000000000n,
    name: 'testNameForTheGuide.chain',
    nameFee: 2865700000000000000n,
    nameSalt: 7595805618692717,
    nonce: 19,
    type: 'NameClaimTx',
    version: 2
  },
  rawTx: 'tx_+KILAfhCuECVba2mm3Un3nrN9U+LpuB2wzqDDmAzXwvc+9sVkpkcALzqm14p7kGqyt8pwjGTyKeLM9lp+KgXWF4PHmHz0WAPuFr4WCACoQGMyNToF1flcYDSVsPl5DZ9ZY3FJWRpDRQYD32quWBEAhOZdGVzdE5hbWVGb3JUaGVHdWlkZS5jaGFpboca/FhPr2JtiCfFAveE1kAAhg9HjghAAADilhgr',
  id: 'nm_1Cz5HGY8PMWZxNrM6s51CtsJZDU3DDT1LdmpEipa3DRghyGz5',
  owner: 'ak_2519mBsgjJEVEFoRgno1ryDsn3BEaCZGRbXPEjThWYLX9MTpmk',
  pointers: [],
  ttl: 629507,
  update: [Function (anonymous)],
  transfer: [Function (anonymous)],
  revoke: [Function (anonymous)],
  extendTtl: [Function (anonymous)]
}
*/

Note:

  • The nameFee that is required will be correctly calculated automatically for the initial claim.
  • In case the claim triggers an auction the required nameFee is locked by the protocol.
    • If you win the auction the nameFee is permanently deducted from your accounts balance and effectively burned.
      • It will be credited to ak_11111111111111111111111111111111273Yts which nobody can access. This reduces the total supply of AE over time.
    • If somebody else outbids you the provided nameFee is immediately released and returned to your account.

Bid during an auction

In case there is an auction running for a name you want to claim you need to place a bid.

import { computeBidFee, computeAuctionEndBlock } from '@aeternity/aepp-sdk'

const name = 'auctiontest1.chain'

const startFee = ... // request from middleware, e.g. https://testnet.aeternity.io/mdw/name/auctiontest1.chain
const increment = 0.05 // 5%, the minimum required increment

// startFee is OPTIONAL and defaults to minimum calculated fee for the name in general
// startFee MUST be at least the nameFee of the last bid
// increment is OPTIONAL and defaults to 0.05
const nameFee = computeBidFee(name, { startFee, increment })
const bidTx = await aeSdk.aensBid(name, nameFee)

console.log(bidTx)
console.log(`BID PLACED AT ${bidTx.blockHeight} WILL END AT ${computeAuctionEndBlock(name, bidTx.blockHeight)}`)

/*
{
  blockHash: 'mh_Y5LDYPtPWzcop2FCbxnwTSwjWo9d3rYcBBcChCEHfzHo2YZpm',
  blockHeight: 449693,
  hash: 'th_2nZshewM7FtKSsDEP4zXPsGCe9cdxaFTRrcNjJyE22ktjGidZR',
  signatures: [
    'sg_7G49bziWjjZAjayBhFffRyfLbmFczs2foJgXcx1n1Vo7iGELQqggdH4w72MqWbNUbM9Jv2EBJnd2LFjv6LJhqTmvJBWyr'
  ],
  tx: {
    accountId: 'ak_2519mBsgjJEVEFoRgno1ryDsn3BEaCZGRbXPEjThWYLX9MTpmk',
    fee: 16520000000000n,
    name: 'auctiontest1.chain',
    nameFee: 3159434250000000000n,
    nameSalt: 0,
    nonce: 24,
    type: 'NameClaimTx',
    version: 2
  },
  rawTx: 'tx_+JQLAfhCuEAv2HQoyLa7krHpcQAsPpWdP+6PJYzdPSifmwNIgpWZtyrdgVD+IGMzzAqxkOQEDeAwc/Oh+dfXPBZpNslCNFgBuEz4SiACoQGMyNToF1flcYDSVsPl5DZ9ZY3FJWRpDRQYD32quWBEAhiSYXVjdGlvbnRlc3QxLmNoYWluAIgr2JC2AnPkAIYPBly7UAAAm/NY1g==',
  nameFee: BigNumber { s: 1, e: 18, c: [ 31594, 34250000000000 ] }
}
BID PLACED AT 449693 WILL END AT 479453
*/

Note:

  • It is required to provide a nameFee that is at least 5% higher than the current bid.
    • The node doesn't expose an API to request the nameFee of the last bid. You need to receive that information from the middleware that you ideally host yourself.

2. Update a name

Now that you own your AENS name you might want to update it in order to:

  • Set pointers to accounts, oracles, contracts or channels.
  • Extend the TTL before it expires.
    • By default a name will have a TTL of 180000 key blocks (~375 days). It cannot be extended longer than 180000 key blocks.

Set pointers & update TTL

import { getDefaultPointerKey } from '@aeternity/aepp-sdk'

const name = 'testNameForTheGuide.chain'
const oracle = 'ok_2519mBsgjJEVEFoRgno1ryDsn3BEaCZGRbXPEjThWYLX9MTpmk'
const pointers = {
  account_pubkey: 'ak_2519mBsgjJEVEFoRgno1ryDsn3BEaCZGRbXPEjThWYLX9MTpmk',
  customKey: 'ak_2519mBsgjJEVEFoRgno1ryDsn3BEaCZGRbXPEjThWYLX9MTpmk',
  [getDefaultPointerKey(oracle)]: oracle, // the same as `oracle_pubkey: oracle,`
  contract_pubkey: 'ct_2519mBsgjJEVEFoRgno1ryDsn3BEaCZGRbXPEjThWYLX9MTpmk',
  channel: 'ch_2519mBsgjJEVEFoRgno1ryDsn3BEaCZGRbXPEjThWYLX9MTpmk',
}

// using aeSdk directly (instance of AeSdk class)
const nameUpdateTx = await aeSdk.aensUpdate(name, pointers)

// OR using the instance of a name
const nameInstance = await aeSdk.aensQuery(name)
const nameUpdateTx = await nameInstance.update(pointers)

console.log(nameUpdateTx)

/*
{
  blockHash: 'mh_AiaGtTWBRys7EX5apAidShyCw1kefocuWt49DgLE2fWNCgKvq',
  blockHeight: 449855,
  hash: 'th_LLef19g2mLWQfG7Ds1XZNGyb3SGvi8PGvk7mJczpD9iToBcqL',
  signatures: [
    'sg_Zm7kp1oBnXGnHwdzsim9nTXtrGoV4bWJhcC1nkw9f3eMPibVaYQRoM829iNrPandAfs8TK7ogEkYByxQx11xCAFMpwUBz'
  ],
  tx: {
    accountId: 'ak_2519mBsgjJEVEFoRgno1ryDsn3BEaCZGRbXPEjThWYLX9MTpmk',
    clientTtl: 84600,
    fee: 17800000000000n,
    nameId: 'nm_1Cz5HGY8PMWZxNrM6s51CtsJZDU3DDT1LdmpEipa3DRghyGz5',
    nameTtl: 180000,
    nonce: 27,
    pointers: [ [Object] ],
    type: 'NameUpdateTx',
    version: 1
  },
  rawTx: 'tx_+NQLAfhCuED6aLVAlIfcqczlZ4XzOjG3U23l5+egPFDVEKTSMuL0Zj18jUG6ctJxGGQubNoANFcwQl79T7w0dG4+FV7Qn3AKuIz4iiIBoQGMyNToF1flcYDSVsPl5DZ9ZY3FJWRpDRQYD32quWBEAhuhAgB4Gsn+dalOdWvHXJd+3LBBiO9HsZoHIF2/aQ4n/bbagwK/IPLxjmFjY291bnRfcHVia2V5oQGMyNToF1flcYDSVsPl5DZ9ZY3FJWRpDRQYD32quWBEAoMBSniGEDBirVAAAJ7I9GA=',
  id: 'nm_1Cz5HGY8PMWZxNrM6s51CtsJZDU3DDT1LdmpEipa3DRghyGz5',
  owner: 'ak_2519mBsgjJEVEFoRgno1ryDsn3BEaCZGRbXPEjThWYLX9MTpmk',
  pointers: [
    {
      id: 'ak_2519mBsgjJEVEFoRgno1ryDsn3BEaCZGRbXPEjThWYLX9MTpmk',
      key: 'account_pubkey'
    }
  ],
  ttl: 629855,
  update: [Function (anonymous)],
  transfer: [Function (anonymous)],
  revoke: [Function (anonymous)],
  extendTtl: [Function (anonymous)]
}
*/

Note:

Extend TTL while keeping pointers

In case you want to extend a name using a custom TTL and keep the current pointers you can do this as follows:

const name = 'testNameForTheGuide.chain'

// using aeSdk directly (instance of AeSdk class
const nameUpdateTx = await aeSdk.aensUpdate(name, {}, { nameTtl: 100000, extendPointers: true })

// OR using the instance of a name
const nameInstance = await aeSdk.aensQuery(name)
const nameUpdateTx = await nameInstance.update({}, { nameTtl: 100000, extendPointers: true })

console.log(nameUpdateTx)

/*
{
  blockHash: 'mh_2mbAuBtyPp7wN6hrcvkYD7LZRSegwZZKYnLcNQkGVGRZ3uVxW6',
  blockHeight: 449860,
  hash: 'th_2wXE8i5BUFCUPuioKwAuwNysm6RPQo8STf5m91CuT3LTh7q4ko',
  signatures: [
    'sg_E69tfR3STWi5Dg6yUHQj5D5waTCEhoFmb1LYWGU2bsCnuLkMgH1KqhRKrCZaatDMyg5szgABDuU1r6ZD5K74qXWW5pMJ'
  ],
  tx: {
    accountId: 'ak_2519mBsgjJEVEFoRgno1ryDsn3BEaCZGRbXPEjThWYLX9MTpmk',
    clientTtl: 100000,
    fee: 17800000000000n,
    nameId: 'nm_1Cz5HGY8PMWZxNrM6s51CtsJZDU3DDT1LdmpEipa3DRghyGz5',
    nameTtl: 100000,
    nonce: 28,
    pointers: [ [Object] ],
    type: 'NameUpdateTx',
    version: 1
  },
  rawTx: 'tx_+NQLAfhCuEABuZTyU+W5xmBpQn5GCJiSpK6fI0pYoMIQ5IqD2bs6pJ+oyU7HDGoSU0Mn6tsfYOn+MVRsxLR6yM1Vyv0c4JQNuIz4iiIBoQGMyNToF1flcYDSVsPl5DZ9ZY3FJWRpDRQYD32quWBEAhyhAgB4Gsn+dalOdWvHXJd+3LBBiO9HsZoHIF2/aQ4n/bbagwGGoPLxjmFjY291bnRfcHVia2V5oQGMyNToF1flcYDSVsPl5DZ9ZY3FJWRpDRQYD32quWBEAoMBhqCGEDBirVAAANKYAm0='
}
*/

Note:

  • If you provide a non-empty array of pointers to that function while using extendPointers the SDK will merge the existing pointers with those provided.
    • In case there already exists an account pointer and you provide an accounts address in the array the old account pointer will be overwritten
  • If you provide an empty array and don't set extendPointers to true all of your current pointers will be removed.

3. Transfer ownership of a name

In some cases you might want to transfer the ownership of a name to another account. Of course this is also possible and you can do that as follows:

const recipient = 'ak_...'

// using aeSdk directly (instance of AeSdk class)
const nameTransferTx = await aeSdk.aensTransfer(name, recipient)

// OR using the instance of a name
const nameInstance = await aeSdk.aensQuery(name)
const nameTransferTx = await nameInstance.transfer(recipient)

console.log(nameTransferTx)

/*
{
  blockHash: 'mh_ZuYV5tmJM4PhrfxzXfo9e37uqPcYpCiAoEC2uRcqbUXeMSmLH',
  blockHeight: 449982,
  hash: 'th_xXqmZxR7WJDe6YTz1GnHWgNjtcZjpTPA5bVuiQstmd8o6Gg7x',
  signatures: [
    'sg_72fiMzprywNmFtuf1i1FFfhBBXcPHpHDfi1Uv2bDnYayoCRWe1yyzvwLmhM6cfA3TR6pBF9Kj3iyYm3mrC46rvNCpchyi'
  ],
  tx: {
    accountId: 'ak_2519mBsgjJEVEFoRgno1ryDsn3BEaCZGRbXPEjThWYLX9MTpmk',
    fee: 17300000000000n,
    nameId: 'nm_1Cz5HGY8PMWZxNrM6s51CtsJZDU3DDT1LdmpEipa3DRghyGz5',
    nonce: 33,
    recipientId: 'ak_21A27UVVt3hDkBE5J7rhhqnH5YNb4Y1dqo4PnSybrH85pnWo7E',
    type: 'NameTransferTx',
    version: 1
  },
  rawTx: 'tx_+LsLAfhCuEAuFNHG4gniZjJDQtbm5cIfJACkU/NI96mZpwMvdyuxxOYWYxSxUF2NF5oxjmsYLdXWfSrdmgWj40JzZU/TRA4DuHP4cSQBoQGMyNToF1flcYDSVsPl5DZ9ZY3FJWRpDRQYD32quWBEAiGhAgB4Gsn+dalOdWvHXJd+3LBBiO9HsZoHIF2/aQ4n/bbaoQHVzwhADpiCIvJutLAsj4kHdFdGchGm5tlV7bcHScajO4YPu/hayAAABhgUgg=='
}
*/

4. Revoke a name

In case you want to revoke a name prior to its expiration for whatever reason you can do that as follows:

// using aeSdk directly (instance of AeSdk class)
const nameRevokeTx = await aeSdk.aensRevoke(name)

// OR using the instance of a name
const nameInstance = await aeSdk.aensQuery(name)
const nameRevokeTx = await nameInstance.revoke()

console.log(nameRevokeTx)

/*
{
  blockHash: 'mh_2Q5Xqd4vrmwkS98SBkN79k1t9VLNmDX4wk8xnUJYTEvkK3YoH4',
  blockHeight: 450018,
  hash: 'th_2XhJradAVKN2jR2tgNYdKNWikMc8XunqTdUhRPh2TyhAwMqijJ',
  signatures: [
    'sg_Tjbf8SJDQXgBhaYk7AzTTr7d1RF3bDcMp6BMgRoHcHcgBp13HjcMYmZJ8vvqrZ94An9gvQD4NegYrR7eaBLt6jEy99xyM'
  ],
  tx: {
    accountId: 'ak_2519mBsgjJEVEFoRgno1ryDsn3BEaCZGRbXPEjThWYLX9MTpmk',
    fee: 16620000000000,
    nameId: 'nm_1Cz5HGY8PMWZxNrM6s51CtsJZDU3DDT1LdmpEipa3DRghyGz5',
    nonce: 34,
    type: 'NameRevokeTx',
    version: 1
  },
  rawTx: 'tx_+JkLAfhCuEDMWOZac70tEHQ4AHGBIedZepwTvsBjIHk36vhTd5yBepmTemTQcUVrAtbNO3jZVZD4GUOhN+DfXm2lNrpZu+8FuFH4TyMBoQGMyNToF1flcYDSVsPl5DZ9ZY3FJWRpDRQYD32quWBEAiKhAgB4Gsn+dalOdWvHXJd+3LBBiO9HsZoHIF2/aQ4n/bbahg8dpTI4AAB44bma'
}
*/

Note:

  • On revocation the name enters in a revoked state.
  • After a timeout of 2016 key blocks the name will be available for claiming again.

Delegate signature to contract (AENS interface)

It is possible to authorize a Sophia contract to manage an AENS name on behalf of your account. In order to achieve that you need to provide a delegation signature to the contract. The contract will then be able to use the AENS interface and perform AENS related actions on behalf of your account.

This functionality could for example be used to build an AENS marketplace.

// imports

const aeSdk = new AeSdk({ ... }) // init the SDK instance with AeSdk class

const contractAddress = 'ct_asd2ks...'
// AENS name
const name = 'example.chain'
// Sign with a specific account
const onAccount = aeSdk.address

// this signature will allow the contract to perform a pre-claim on your behalf
const preClaimSig = await aeSdk.createDelegationSignature(contractAddress, [])

// this signature will allow the contract to perform
// any name related transaction for a specific name that you own
const aensDelegationSig = await aeSdk.createDelegationSignature(contractAddress, [name], { onAccount })