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 acommitmentId
(hash). - The
commitmentId
is calculated with a randomsalt
and the provided name. The SDK automatically generates the randomsalt
, calculates thecommitmentId
and includes it into theNamePreclaimTx
. - After the
NamePreclaimTx
transaction has been mined you will be able to perform the actualclaim
of the name. When performing the actualclaim
via aNameClaimTx
you will, depending on the length of the name: - immediately become owner of that name
- initiate an auction
Pre-claim
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 broadcastclaim
transaction with the samesalt
and it should be signed with the same private key aspre-claim
. - As thepre-claim
is required to avoid front running it is recommended to wait with the actualclaim
until at least 1 key block has been mined so that nobody knows which name you aim to claim. - The correspondingclaim
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 apre-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 accountName length
<= 12: anauction
is started
// 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.
- In case the
claim
triggers an auction the requirednameFee
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.
- It will be credited to
- 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 = 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 least5%
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
,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
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
totrue
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 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:
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:
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:
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 and perform AENS related actions on behalf of your account.
This functionality could for example be used to build an AENS marketplace.
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);