# 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](../transaction-options.md#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](../transaction-options.md#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); ```