# 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)](https://docs.aeternity.com/developer-documentation/protocol/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

```js
import { AeSdk, Name } from '@aeternity/aepp-sdk'

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

// `getContext` is used to bind AeSdk to Name instance
// e.g. if you change the node in AeSdk it also would be changed in Name.
// Alternatively, you need to provide `onAccount`, `onNode` options.
const name = new Name('testNameForTheGuide.chain', aeSdk.getContext())

const preClaimTx = await name.preclaim()
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
}
*/
```

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.

### Claim

This example assumes that you did a `pre-claim` before and kept an instance of Name.

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

```js
// 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 name.claim();
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'
}
*/
```

Note:

* The `nameFee` that is required will be correctly calculated automatically for the initial claim.
  * It's still possible to pass it as additional param, see [transaction options](https://docs.aeternity.com/developer-documentation/aepp-sdk-js/transaction-options#nameclaimtx).
* 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.

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

const name = new Name('auctiontest1.chain', aeSdk.getContext())

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
// `name.value` is a name as string
const nameFee = computeBidFee(name.value, { startFee, increment })
const bidTx = await name.bid(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=='
}
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](https://github.com/aeternity/ae_mdw) 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`, `channels`, or store binary data.
* 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

```js
import { getDefaultPointerKey, encode, Encoding } from '@aeternity/aepp-sdk';

const oracle = 'ok_2519mBsgjJEVEFoRgno1ryDsn3BEaCZGRbXPEjThWYLX9MTpmk';
const pointers = {
  account_pubkey: 'ak_2519mBsgjJEVEFoRgno1ryDsn3BEaCZGRbXPEjThWYLX9MTpmk',
  customKey: encode(Buffer.from('example data'), Encoding.Bytearray),
  [getDefaultPointerKey(oracle)]: oracle, // the same as `oracle_pubkey: oracle,`
  contract_pubkey: 'ct_2519mBsgjJEVEFoRgno1ryDsn3BEaCZGRbXPEjThWYLX9MTpmk',
  channel: 'ch_2519mBsgjJEVEFoRgno1ryDsn3BEaCZGRbXPEjThWYLX9MTpmk',
};

const nameUpdateTx = await name.update(pointers);
console.log(nameUpdateTx);

/*
{
  blockHash: 'mh_AiaGtTWBRys7EX5apAidShyCw1kefocuWt49DgLE2fWNCgKvq',
  blockHeight: 449855,
  hash: 'th_LLef19g2mLWQfG7Ds1XZNGyb3SGvi8PGvk7mJczpD9iToBcqL',
  signatures: [
    'sg_Zm7kp1oBnXGnHwdzsim9nTXtrGoV4bWJhcC1nkw9f3eMPibVaYQRoM829iNrPandAfs8TK7ogEkYByxQx11xCAFMpwUBz'
  ],
  tx: {
    accountId: 'ak_2519mBsgjJEVEFoRgno1ryDsn3BEaCZGRbXPEjThWYLX9MTpmk',
    clientTtl: 3600,
    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='
}
*/
```

Note:

* If you provide an empty pointers object and don't set `extendPointers` to `true` all of your current pointers will be removed.
* If you provide a non-empty object 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
* It's also possible to pass additional [transaction options](https://docs.aeternity.com/developer-documentation/aepp-sdk-js/transaction-options#nameupdatetx) here, too.

### 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:

```js
const nameUpdateTx = await name.extendTtl(100000);
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='
}
*/
```

## 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:

```js
const recipient = 'ak_...';
const nameTransferTx = await name.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:

```js
const nameRevokeTx = await name.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](https://docs.aeternity.com/developer-documentation/aesophia/docs/sophia_features#aens-interface) and perform AENS related actions on behalf of your account.

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

```js
const contractAddress = 'ct_asd2ks...';
// AENS name
const nameId = 'example.chain';
const commonParams = {
  contractAddress,
  accountAddress: aeSdk.address,
};
let delegation;

// this signature will allow the contract to perform a pre-claim on your behalf
delegation = packDelegation({ tag: DelegationTag.AensPreclaim, ...commonParams });

// this signature will allow the contract to perform
// any name related transaction for a specific name that you own
delegation = packDelegation({ tag: DelegationTag.AensName, nameId, ...commonParams });

// alternatively, you can generate a delegation signature suitable for every name you own
delegation = packDelegation({ tag: DelegationTag.AensWildcard, ...commonParams });

const delegationSignature = await aeSdk.signDelegation(delegation);
```
