Skip to content

Commit 0d43804

Browse files
authored
feat(AENS): Add ability to spend by name (#682)
* feat(AENS): Add ability to spend by name * feat(AENS): Spend by name * feat(Lima): add check for abi/vm <-> backend(FATE/AEVM) * debugg * feat(AENS): Fix spend by name * feat(AENS): Fix spend by name * feat(AENS): Fix resolve recipient by name function
1 parent 259ede6 commit 0d43804

File tree

6 files changed

+76
-19
lines changed

6 files changed

+76
-19
lines changed

es/ae/aens.js

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@
2828

2929
import * as R from 'ramda'
3030
import { encodeBase58Check, salt } from '../utils/crypto'
31-
import { commitmentHash } from '../tx/builder/helpers'
31+
import { commitmentHash, isNameValid } from '../tx/builder/helpers'
3232
import Ae from './'
33+
import { CLIENT_TTL, NAME_TTL } from '../tx/builder/schema'
3334

3435
/**
3536
* Transfer a domain to another account
@@ -132,6 +133,7 @@ async function update (nameId, target, options = {}) {
132133
* @return {Promise<Object>}
133134
*/
134135
async function query (name, opt = {}) {
136+
isNameValid(name)
135137
const o = await this.getName(name)
136138
const nameId = o.id
137139

@@ -165,6 +167,7 @@ async function query (name, opt = {}) {
165167
* @return {Promise<Object>} the result of the claim
166168
*/
167169
async function claim (name, salt, options = {}) {
170+
isNameValid(name)
168171
const opt = R.merge(this.Ae.defaults, options)
169172
const claimTx = await this.nameClaimTx(R.merge(opt, {
170173
accountId: await this.address(opt),
@@ -173,11 +176,8 @@ async function claim (name, salt, options = {}) {
173176
}))
174177

175178
const result = await this.send(claimTx, opt)
176-
177-
return {
178-
...result,
179-
...opt.waitMined && await this.aensQuery(name, opt)
180-
}
179+
const nameInter = this.Chain.defaults.waitMined ? await this.aensQuery(name, opt) : {}
180+
return Object.assign(result, nameInter)
181181
}
182182

183183
/**
@@ -190,6 +190,7 @@ async function claim (name, salt, options = {}) {
190190
* @return {Promise<Object>}
191191
*/
192192
async function preclaim (name, options = {}) {
193+
isNameValid(name)
193194
const opt = R.merge(this.Ae.defaults, options)
194195
const _salt = salt()
195196
const height = await this.height()
@@ -234,8 +235,8 @@ const Aens = Ae.compose({
234235
deepProps: {
235236
Ae: {
236237
defaults: {
237-
clientTtl: 1,
238-
nameTtl: 50000 // aec_governance:name_claim_max_expiration() => 50000
238+
clientTtl: CLIENT_TTL,
239+
nameTtl: NAME_TTL // aec_governance:name_claim_max_expiration() => 50000
239240
}
240241
}
241242
}

es/ae/index.js

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import Account from '../account'
2929
import TxBuilder from '../tx/builder'
3030
import * as R from 'ramda'
3131
import { BigNumber } from 'bignumber.js'
32+
import { isAddressValid } from '../utils/crypto'
33+
import { isNameValid } from '../tx/builder/helpers'
3234

3335
/**
3436
* Sign and post a transaction to the chain
@@ -63,16 +65,36 @@ async function signUsingGA (tx, options = {}) {
6365
* @category async
6466
* @rtype (amount: Number|String, recipientId: String, options?: Object) => Promise[String]
6567
* @param {Number|String} amount - Amount to spend
66-
* @param {String} recipientId - Address of recipient account
68+
* @param {String} recipientId - Address or Name of recipient account
6769
* @param {Object} options - Options
6870
* @return {String|String} Transaction or transaction hash
6971
*/
7072
async function spend (amount, recipientId, options = {}) {
7173
const opt = R.merge(this.Ae.defaults, options)
72-
const spendTx = await this.spendTx(R.merge(opt, { senderId: await this.address(opt), recipientId, amount: amount }))
74+
recipientId = await this.resolveRecipientName(recipientId)
75+
const spendTx = await this.spendTx(R.merge(opt, { senderId: await this.address(opt), recipientId, amount }))
7376
return this.send(spendTx, opt)
7477
}
7578

79+
/**
80+
* Resolve AENS name and return name hash
81+
*
82+
* @param {String} nameOrAddress
83+
* @param {String} pointerPrefix
84+
* @return {String} Address or AENS name hash
85+
*/
86+
async function resolveRecipientName (nameOrAddress) {
87+
if (isAddressValid(nameOrAddress)) return nameOrAddress
88+
if (isNameValid(nameOrAddress)) {
89+
const { id } = await this.getName(nameOrAddress)
90+
return id
91+
}
92+
// Validation
93+
// const { id: nameHash, pointers } = await this.getName(nameOrAddress)
94+
// if (pointers.find(({ id }) => id.split('_')[0] === pointerPrefix)) return nameHash
95+
// throw new Error(`Can't find pointers with prefix ${pointerPrefix} for name ${nameOrAddress}`)
96+
}
97+
7698
/**
7799
* Send a percentage of funds to another account
78100
* @instance
@@ -135,7 +157,7 @@ function destroyInstance () {
135157
* @return {Object} Ae instance
136158
*/
137159
const Ae = stampit(Tx, Account, Chain, {
138-
methods: { send, spend, transferFunds, destroyInstance },
160+
methods: { send, spend, transferFunds, destroyInstance, resolveRecipientName },
139161
deepProps: { Ae: { defaults: {} } }
140162
// Todo Enable GA
141163
// deepConfiguration: { Ae: { methods: ['signUsingGA'] } }

es/tx/builder/helpers.js

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import * as R from 'ramda'
12
import {
23
assertedType,
34
decodeBase58Check,
@@ -8,7 +9,7 @@ import {
89
salt
910
} from '../../utils/crypto'
1011
import { toBytes } from '../../utils/bytes'
11-
import { ID_TAG_PREFIX, PREFIX_ID_TAG } from './schema'
12+
import { ID_TAG_PREFIX, PREFIX_ID_TAG, AENS_NAME_DOMAINS } from './schema'
1213
import { BigNumber } from 'bignumber.js'
1314

1415
/**
@@ -205,6 +206,20 @@ export function readPointers (pointers) {
205206
)
206207
}
207208

209+
/**
210+
* Is name valid
211+
* @function
212+
* @alias module:@aeternity/aepp-sdk/es/ae/aens
213+
* @param {string} name
214+
* @return Boolean
215+
* @throws Error
216+
*/
217+
export function isNameValid (name) {
218+
if (typeof name !== 'string') throw new Error('AENS: Name must be a string')
219+
if (!AENS_NAME_DOMAINS.includes(R.last(name.split('.')))) throw new Error(`AENS: Invalid name domain. Possible domains [${AENS_NAME_DOMAINS}]`)
220+
return true
221+
}
222+
208223
export default {
209224
readPointers,
210225
buildPointers,
@@ -219,5 +234,6 @@ export default {
219234
formatSalt,
220235
oracleQueryId,
221236
createSalt,
222-
buildHash
237+
buildHash,
238+
isNameValid
223239
}

es/tx/builder/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ function validateField (value, key, type, prefix) {
141141
return assert((!isNaN(value) || BigNumber.isBigNumber(value)) && BigNumber(value).gte(0), { value, isMinusValue })
142142
}
143143
case FIELD_TYPES.id:
144+
if (Array.isArray(prefix)) {
145+
const p = prefix.find(p => p === value.split('_')[0])
146+
return assert(p && PREFIX_ID_TAG[value.split('_')[0]], { value, prefix })
147+
}
144148
return assert(assertedType(value, prefix) && PREFIX_ID_TAG[value.split('_')[0]] && value.split('_')[0] === prefix, { value, prefix })
145149
case FIELD_TYPES.binary:
146150
return assert(value.split('_')[0] === prefix, { prefix, value })

es/tx/builder/schema.js

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@
1111
import BigNumber from 'bignumber.js'
1212

1313
export const VSN = 1
14+
export const VSN_2 = 2
15+
16+
// # AENS
17+
export const AENS_NAME_DOMAINS = ['aet', 'test']
18+
export const CLIENT_TTL = 1
19+
export const NAME_TTL = 50000
1420

1521
// # Tag constant for ids (type uint8)
1622
// # see https://github.com/aeternity/protocol/blob/master/serializations.md#the-id-type
@@ -374,14 +380,14 @@ const ACCOUNT_TX_2 = [
374380
TX_FIELD('flags', FIELD_TYPES.int),
375381
TX_FIELD('nonce', FIELD_TYPES.int),
376382
TX_FIELD('balance', FIELD_TYPES.int),
377-
TX_FIELD('gaContract', FIELD_TYPES.id, 'ct'),
383+
TX_FIELD('gaContract', FIELD_TYPES.id, ['ct', 'nm']),
378384
TX_FIELD('gaAuthFun', FIELD_TYPES.binary, 'cb')
379385
]
380386

381387
const SPEND_TX = [
382388
...BASE_TX,
383389
TX_FIELD('senderId', FIELD_TYPES.id, 'ak'),
384-
TX_FIELD('recipientId', FIELD_TYPES.id, 'ak'),
390+
TX_FIELD('recipientId', FIELD_TYPES.id, ['ak', 'nm']),
385391
TX_FIELD('amount', FIELD_TYPES.int),
386392
TX_FIELD('fee', FIELD_TYPES.int),
387393
TX_FIELD('ttl', FIELD_TYPES.int),
@@ -431,7 +437,7 @@ const NAME_TRANSFER_TX = [
431437
TX_FIELD('accountId', FIELD_TYPES.id, 'ak'),
432438
TX_FIELD('nonce', FIELD_TYPES.int),
433439
TX_FIELD('nameId', FIELD_TYPES.id, 'nm'),
434-
TX_FIELD('recipientId', FIELD_TYPES.id, 'ak'),
440+
TX_FIELD('recipientId', FIELD_TYPES.id, ['ak', 'nm']),
435441
TX_FIELD('fee', FIELD_TYPES.int),
436442
TX_FIELD('ttl', FIELD_TYPES.int)
437443
]
@@ -501,7 +507,7 @@ const CONTRACT_CALL_TX = [
501507
...BASE_TX,
502508
TX_FIELD('callerId', FIELD_TYPES.id, 'ak'),
503509
TX_FIELD('nonce', FIELD_TYPES.int),
504-
TX_FIELD('contractId', FIELD_TYPES.id, 'ct'),
510+
TX_FIELD('contractId', FIELD_TYPES.id, ['ct', 'nm']),
505511
TX_FIELD('abiVersion', FIELD_TYPES.int),
506512
TX_FIELD('fee', FIELD_TYPES.int),
507513
TX_FIELD('ttl', FIELD_TYPES.int),
@@ -541,7 +547,7 @@ const ORACLE_REGISTER_TX = [
541547

542548
const ORACLE_EXTEND_TX = [
543549
...BASE_TX,
544-
TX_FIELD('oracleId', FIELD_TYPES.id, 'ok'),
550+
TX_FIELD('oracleId', FIELD_TYPES.id, ['ok', 'nm']),
545551
TX_FIELD('nonce', FIELD_TYPES.int),
546552
TX_FIELD('oracleTtlType', FIELD_TYPES.int),
547553
TX_FIELD('oracleTtlValue', FIELD_TYPES.int),
@@ -553,7 +559,7 @@ const ORACLE_QUERY_TX = [
553559
...BASE_TX,
554560
TX_FIELD('senderId', FIELD_TYPES.id, 'ak'),
555561
TX_FIELD('nonce', FIELD_TYPES.int),
556-
TX_FIELD('oracleId', FIELD_TYPES.id, 'ok'),
562+
TX_FIELD('oracleId', FIELD_TYPES.id, ['ok', 'nm']),
557563
TX_FIELD('query', FIELD_TYPES.string),
558564
TX_FIELD('queryFee', FIELD_TYPES.int),
559565
TX_FIELD('queryTtlType', FIELD_TYPES.int),

test/integration/aens.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ describe('Aens', function () {
3030
configure(this)
3131

3232
let aens
33+
let nameHash
3334
const account = generateKeyPair()
3435
const name = randomName()
3536

@@ -68,12 +69,19 @@ describe('Aens', function () {
6869

6970
it('updates names', async () => {
7071
const claim = await aens.aensQuery(name)
72+
nameHash = claim.id
7173
const address = await aens.address()
7274
return claim.update(address).should.eventually.deep.include({
7375
pointers: [R.fromPairs([['key', 'account_pubkey'], ['id', address]])]
7476
})
7577
})
7678

79+
it('Spend by name', async () => {
80+
const current = await aens.address()
81+
const onAccount = aens.addresses().find(acc => acc !== current)
82+
await aens.spend(100, name, { onAccount })
83+
})
84+
7785
it('transfers names', async () => {
7886
const claim = await aens.aensQuery(name)
7987

0 commit comments

Comments
 (0)