@aeternity/aepp-sdk
Version:
SDK for the æternity blockchain
366 lines (341 loc) • 13 kB
JavaScript
/*
* ISC License (ISC)
* Copyright (c) 2018 aeternity developers
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/**
* Aens module - routines to interact with the æternity naming system
*
* The high-level description of the naming system is
* https://github.com/aeternity/protocol/blob/master/AENS.md in the protocol
* repository.
* @module @aeternity/aepp-sdk/es/ae/aens
* @export Aens
* @example import { Aens } from '@aeternity/aepp-sdk'
*/
import * as R from 'ramda'
import { salt } from '../utils/crypto'
import {
commitmentHash,
ensureNameValid,
getMinimumNameFee,
classify,
isAuctionName,
validatePointers, encode, produceNameId
} from '../tx/builder/helpers'
import Ae from './'
import { CLIENT_TTL, NAME_FEE, NAME_TTL } from '../tx/builder/schema'
/**
* Revoke a name
* @instance
* @function
* @alias module:@aeternity/aepp-sdk/es/ae/aens
* @category async
* @param {String} name Name hash
* @param {Object} [options={}] options
* @param {(String|Object)} [options.onAccount] onAccount Make operation on specific account from sdk(you pass publickKey) or
* using provided KeyPair(Can be keypair object or MemoryAccount)
* @param {(Number|String|BigNumber)} [options.fee] fee
* @param {(Number|String|BigNumber)} [options.ttl] ttl
* @param {(Number|String|BigNumber)} [options.nonce] nonce
* @return {Promise<Object>} Transaction result
* @example
* const name = 'test.chain'
* const nameObject = await sdkInstance.aensQuery(name)
*
* await sdkInstance.aensRevoke(name, { fee, ttl , nonce })
* // or
* await nameObject.revoke({ fee, ttl, nonce })
*/
async function revoke (name, options = {}) {
ensureNameValid(name)
const opt = R.merge(this.Ae.defaults, options)
const nameRevokeTx = await this.nameRevokeTx(R.merge(opt, {
nameId: produceNameId(name),
accountId: await this.address(opt)
}))
return this.send(nameRevokeTx, opt)
}
/**
* Update a name
* @instance
* @function
* @category async
* @alias module:@aeternity/aepp-sdk/es/ae/aens
* @param {String} name AENS name
* @param {String[]} pointers Array of name pointers. Can be oracle|account|contract|channel public key
* @param {Object} [options={}]
* @param {Boolean} [options.extendPointers=false] extendPointers Get the pointers from the node and merge with provided one. Pointers with the same type will be overwrited
* @param {(String|Object)} [options.onAccount] onAccount Make operation on specific account from sdk(you pass publickKey) or
* using provided KeyPair(Can be keypair object or MemoryAccount)
* @param {(Number|String|BigNumber)} [options.fee] fee
* @param {(Number|String|BigNumber)} [options.ttl] ttl
* @param {(Number|String|BigNumber)} [options.nonce] nonce
* @param {(Number|String|BigNumber)} [options.nameTtl=50000] nameTtl Name ttl represented in number of blocks (Max value is 50000 blocks)
* @param {(Number|String|BigNumber)} [options.clientTtl=84600] clientTtl a suggestion as to how long any clients should cache this information
* @return {Promise<Object>}
* @throws Invalid pointer array error
* @example
* const name = 'test.chain'
* const pointersArray = ['ak_asd23dasdas...,' 'ct_asdf34fasdasd...']
* const nameObject = await sdkInstance.aensQuery(name)
*
* await sdkInstance.aensUpdate(name, pointersArray, { nameTtl, ttl, fee, nonce, clientTtl })
* // or
* await nameObject.update(pointersArray, { nameTtl, ttl, fee, nonce, clientTtl })
*/
async function update (name, pointers = [], options = { extendPointers: false }) {
ensureNameValid(name)
const opt = R.merge(this.Ae.defaults, options)
if (!validatePointers(pointers)) throw new Error('Invalid pointers array')
pointers = [
...options.extendPointers ? (await this.getName(name)).pointers : [],
...pointers.map(p => R.fromPairs([['id', p], ['key', classify(p)]]))
].reduce((acc, el) => [...acc.filter(p => p.key !== el.key), el], [])
const nameUpdateTx = await this.nameUpdateTx(R.merge(opt, {
nameId: produceNameId(name),
accountId: await this.address(opt),
pointers
}))
return this.send(nameUpdateTx, opt)
}
/**
* Transfer a domain to another account
* @instance
* @function
* @category async
* @alias module:@aeternity/aepp-sdk/es/ae/aens
* @param {String} name AENS name
* @param {String} account Recipient account publick key
* @param {Object} [options={}]
* @param {(String|Object)} [options.onAccount] onAccount Make operation on specific account from sdk(you pass publickKey) or
* using provided KeyPair(Can be keypair object or MemoryAccount)
* @param {(Number|String|BigNumber)} [options.fee] fee
* @param {(Number|String|BigNumber)} [options.ttl] ttl
* @param {(Number|String|BigNumber)} [options.nonce] nonce
* @return {Promise<Object>} Transaction result
* @example
* const name = 'test.chain'
* const recipientPub = 'ak_asd23dasdas...'
* const nameObject = await sdkInstance.aensQuery(name)
*
* await sdkInstance.aensTransfer(name, recipientPub, { ttl, fee, nonce })
* // or
* await nameObject.transfer(recipientPub, { ttl, fee, nonce })
*/
async function transfer (name, account, options = {}) {
ensureNameValid(name)
const opt = R.merge(this.Ae.defaults, options)
const nameTransferTx = await this.nameTransferTx(R.merge(opt, {
nameId: produceNameId(name),
accountId: await this.address(opt),
recipientId: account
}))
return this.send(nameTransferTx, opt)
}
/**
* Query the AENS name info from the node
* and return the object with info and predefined functions for manipulating name
* @instance
* @function
* @category async
* @alias module:@aeternity/aepp-sdk/es/ae/aens
* @param {String} name
* @param {Object} opt Options
* @return {Promise<Object>}
* @example
* const nameObject = sdkInstance.aensQuery('test.chain')
* console.log(nameObject)
* {
* id, // name hash
* pointers, // array of pointers
* update, // Update name function
* extendTtl, // Extend Ttl name function
* transfer, // Transfer name function
* revoke // Revoke name function
* }
*/
async function query (name, opt = {}) {
ensureNameValid(name)
const o = await this.getName(name)
return Object.freeze(Object.assign(o, {
pointers: o.pointers || [],
update: async (pointers = [], options = {}) => {
return {
...(await this.aensUpdate(name, pointers, R.merge(opt, options))),
...(await this.aensQuery(name))
}
},
transfer: async (account, options = {}) => {
return {
...(await this.aensTransfer(name, account, R.merge(opt, options))),
...(await this.aensQuery(name))
}
},
revoke: async (options = {}) => this.aensRevoke(name, R.merge(opt, options)),
extendTtl: async (nameTtl = NAME_TTL, options = {}) => {
if (!nameTtl || typeof nameTtl !== 'number' || nameTtl > NAME_TTL) throw new Error('Ttl must be an number and less then 50000 blocks')
return {
...(await this.aensUpdate(name, o.pointers.map(p => p.id), { ...R.merge(opt, options), nameTtl })),
...(await this.aensQuery(name))
}
}
}))
}
/**
* Claim a previously preclaimed registration. This can only be done after the
* preclaim step
* @instance
* @function
* @category async
* @alias module:@aeternity/aepp-sdk/es/ae/aens
* @param {String} name
* @param {Number} salt Salt from pre-claim, or 0 if it's a bid
* @param {Object} [options={}] options
* @param {String|Object} [options.onAccount] onAccount Make operation on specific account from sdk(you pass publickKey) or
* using provided KeyPair(Can be keypair object or MemoryAccount)
* @param {Number|String|BigNumber} [options.fee] fee
* @param {Number|String|BigNumber} [options.ttl] ttl
* @param {Number|String|BigNumber} [options.nonce] nonce
* @param {Number|String} [options.nameFee] Name Fee (By default calculated by sdk)
* @param {Number|String} [options.vsn = 2] Transaction vsn from Lima is 2
* @return {Promise<Object>} the result of the claim
* @example
* const name = 'test.chain'
* const salt = preclaimResult.salt // salt from pre-claim transaction
*
* await sdkInstance.aensClaim(name, salt, { ttl, fee, nonce, nameFee })
*/
async function claim (name, salt, options = { vsn: 2 }) {
ensureNameValid(name)
const opt = R.merge(this.Ae.defaults, options)
const minNameFee = getMinimumNameFee(name)
if (opt.nameFee !== this.Ae.defaults.nameFee && minNameFee.gt(opt.nameFee)) {
throw new Error(`the provided fee ${opt.nameFee} is not enough to execute the claim, required: ${minNameFee}`)
}
opt.nameFee = opt.nameFee !== this.Ae.defaults.nameFee ? opt.nameFee : minNameFee
const claimTx = await this.nameClaimTx(R.merge(opt, {
accountId: await this.address(opt),
nameSalt: salt,
name: encode(name, 'nm')
}))
const result = await this.send(claimTx, opt)
if (!isAuctionName(name)) {
delete opt.vsn
const nameInter = opt.waitMined ? await this.aensQuery(name, opt) : {}
return Object.assign(result, nameInter)
}
return { ...result, nameFee: opt.nameFee }
}
/**
* Preclaim a name. Sends a hash of the name and a random salt to the node
* @instance
* @function
* @category async
* @alias module:@aeternity/aepp-sdk/es/ae/aens
* @param {String} name
* @param {Object} [options={}]
* @param {String|Object} [options.onAccount] onAccount Make operation on specific account from sdk(you pass publickKey) or
* using provided KeyPair(Can be keypair object or MemoryAccount)
* @param {Number|String|BigNumber} [options.fee] fee
* @param {Number|String|BigNumber} [options.ttl] ttl
* @param {Number|String|BigNumber} [options.nonce] nonce
* @return {Promise<Object>}
* @example
* const name = 'test.chain'
* const salt = preclaimResult.salt // salt from pre-claim transaction
*
* await sdkInstance.aensPreclaim(name, { ttl, fee, nonce })
* {
* ...transactionResult,
* claim, // Claim function (options={}) => claimTransactionResult
* salt,
* commitmentId
* }
*/
async function preclaim (name, options = {}) {
ensureNameValid(name)
const opt = R.merge(this.Ae.defaults, options)
const _salt = salt()
const height = await this.height()
const commitmentId = commitmentHash(name, _salt)
const preclaimTx = await this.namePreclaimTx(R.merge(opt, {
accountId: await this.address(opt),
commitmentId
}))
const result = await this.send(preclaimTx, opt)
return Object.freeze({
...result,
height,
claim: options => this.aensClaim(name, _salt, { ...options, onAccount: opt.onAccount }),
salt: _salt,
commitmentId
})
}
/**
* Bid to name auction
* @instance
* @function
* @category async
* @alias module:@aeternity/aepp-sdk/es/ae/aens
* @param {String} name Domain name
* @param {String|Number} nameFee Name fee (bid fee)
* @param {Object} [options={}]
* @param {String|Object} [options.onAccount] onAccount Make operation on specific account from sdk(you pass publickKey) or
* using provided KeyPair(Can be keypair object or MemoryAccount)
* @param {Number|String|BigNumber} [options.fee] fee
* @param {Number|String|BigNumber} [options.ttl] ttl
* @param {Number|String|BigNumber} [options.nonce] nonce
* @return {Promise<Object>} Transaction result
* @example
* const name = 'test.chain'
* const bidFee = computeBidFee(name, startFee, incrementPercentage)
*
* await sdkInstance.aensBid(name, 213109412839123, { ttl, fee, nonce })
*/
async function bid (name, nameFee = NAME_FEE, options = {}) {
return this.aensClaim(name, 0, { ...options, nameFee, vsn: 2 })
}
/**
* Aens Stamp
*
* Aens provides name-system related methods atop
* {@link module:@aeternity/aepp-sdk/es/ae--Ae} clients.
* @function
* @alias module:@aeternity/aepp-sdk/es/ae/aens
* @rtype Stamp
* @param {Object} [options={}] - Initializer object
* @return {Object} Aens instance
*/
const Aens = Ae.compose({
methods: {
aensQuery: query,
aensPreclaim: preclaim,
aensClaim: claim,
aensUpdate: update,
aensTransfer: transfer,
aensRevoke: revoke,
aensBid: bid
},
deepProps: {
Ae: {
defaults: {
clientTtl: CLIENT_TTL,
nameTtl: NAME_TTL, // aec_governance:name_claim_max_expiration() => 50000
nameFee: NAME_FEE
}
}
}
})
export default Aens