Skip to content

Commit

Permalink
Merge pull request #1023 from aeternity/release/7.4.2
Browse files Browse the repository at this point in the history
Release 7.4.2
  • Loading branch information
nduchak authored Jun 11, 2020
2 parents dd759cb + 9204158 commit 5fed974
Show file tree
Hide file tree
Showing 21 changed files with 3,935 additions and 5,065 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
## [7.4.2](https://github.com/aeternity/aepp-sdk-js/compare/7.2.1...7.4.2) (2020-06-10)


### Bug Fixes

* **AEX-2:** Handler always as Promise ([#1018](https://github.com/aeternity/aepp-sdk-js/issues/1018)) ([a8b0aab](https://github.com/aeternity/aepp-sdk-js/commit/a8b0aab))


### Refactor

* **AEX-2:** Add debug option for `getHandler`. Hide unknown message logs ([#1021](https://github.com/aeternity/aepp-sdk-js/issues/1021)) ([22c452c](https://github.com/aeternity/aepp-sdk-js/commit/22c452c))
* **Contract** Add AENS name resolver for Contract API


## [7.4.1](https://github.com/aeternity/aepp-sdk-js/compare/7.4.0...7.4.1) (2020-05-30)


Expand Down
14 changes: 8 additions & 6 deletions es/ae/contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ function sendAndProcess (tx, options) {

const result = await this.getTxInfo(txData.hash)
return result.returnType === 'ok'
? onSuccess({ hash: txData.hash, tx: TxObject({ tx: txData.rawTx }), result, txData })
: typeof onError === 'function' ? onError(result) : this.handleCallError({ result, tx: TxObject({ tx: txData.rawTx }) })
? onSuccess({ hash: txData.hash, tx: TxObject({ tx: txData.rawTx }), result, txData, rawTx: txData.rawTx })
: typeof onError === 'function' ? onError(result) : this.handleCallError({ result, tx: TxObject({ tx: txData.rawTx }), rawTx: txData.rawTx })
}
}

Expand All @@ -65,13 +65,14 @@ function sendAndProcess (tx, options) {
* @throws Error Decoded error
* @return {Promise<void>}
*/
async function handleCallError ({ result, tx }) {
async function handleCallError ({ result, tx, rawTx }) {
const error = Buffer.from(result.returnValue).toString()
if (isBase64(error.slice(3))) {
const decodedError = Buffer.from(error.slice(3), 'base64').toString()
throw Object.assign(Error(`Invocation failed: ${error}. Decoded: ${decodedError}`), R.merge(result, {
tx,
error,
rawTx,
decodedError
}))
}
Expand All @@ -80,6 +81,7 @@ async function handleCallError ({ result, tx }) {
throw Object.assign(Error(`Invocation failed: ${error}. Decoded: ${decodedError}`), R.merge(result, {
tx,
error,
rawTx,
decodedError
}))
}
Expand Down Expand Up @@ -170,7 +172,7 @@ async function contractCallStatic (source, address, name, args = [], { top, opti
// Prepare `call` transaction
const tx = await this.contractCallTx(R.merge(opt, {
callerId,
contractId: address,
contractId: await this.resolveName(address, 'ct', { resolveByNode: true }),
callData,
nonce
}))
Expand Down Expand Up @@ -208,7 +210,7 @@ async function dryRunContractTx (tx, callerId, source, name, opt = {}) {
* @alias module:@aeternity/aepp-sdk/es/ae/contract
* @category async
* @param {String} source Contract source code
* @param {String} address Contract address
* @param {String} address Contract address or AENS name
* @param {String} name Name of function to call
* @param {Array|String} argsOrCallData Argument's array or callData for call function
* @param {Object} [options={}] Transaction options (fee, ttl, gas, amount, deposit)
Expand All @@ -226,7 +228,7 @@ async function contractCall (source, address, name, argsOrCallData = [], options

const tx = await this.contractCallTx(R.merge(opt, {
callerId: await this.address(opt),
contractId: address,
contractId: await this.resolveName(address, 'ct', { resolveByNode: true }),
callData: Array.isArray(argsOrCallData) ? await this.contractEncodeCall(source, name, argsOrCallData, opt) : argsOrCallData
}))

Expand Down
26 changes: 3 additions & 23 deletions es/ae/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ import Account from '../account'
import TxBuilder from '../tx/builder'
import * as R from 'ramda'
import { BigNumber } from 'bignumber.js'
import { isAddressValid } from '../utils/crypto'
import { isNameValid, produceNameId } from '../tx/builder/helpers'
import { AE_AMOUNT_FORMATS } from '../utils/amount-formatter'

/**
Expand Down Expand Up @@ -69,30 +67,11 @@ async function signUsingGA (tx, options = {}) {
*/
async function spend (amount, recipientId, options = {}) {
const opt = R.merge(this.Ae.defaults, options)
recipientId = await this.resolveRecipientName(recipientId, options)
recipientId = await this.resolveName(recipientId, 'ak', options)
const spendTx = await this.spendTx(R.merge(opt, { senderId: await this.address(opt), recipientId, amount }))
return this.send(spendTx, opt)
}

/**
* Resolve AENS name and return name hash
* @param {String} nameOrAddress
* @param {Boolean} verify
* @return {String} Address or AENS name hash
*/
async function resolveRecipientName (nameOrAddress, { verify = false }) {
if (isAddressValid(nameOrAddress)) return nameOrAddress
if (isNameValid(nameOrAddress)) {
// Validation
if (verify) {
const { pointers } = await this.getName(nameOrAddress)
if (!pointers.find(({ id }) => id.split('_')[0] === 'ak')) throw new Error(`Name ${nameOrAddress} do not have pointers for account`)
}
return produceNameId(nameOrAddress)
}
throw new Error('Invalid recipient name or address: ' + nameOrAddress)
}

/**
* Send a percentage of funds to another account
* @instance
Expand All @@ -106,6 +85,7 @@ async function resolveRecipientName (nameOrAddress, { verify = false }) {
async function transferFunds (percentage, recipientId, options = { excludeFee: false }) {
if (percentage < 0 || percentage > 1) throw new Error(`Percentage should be a number between 0 and 1, got ${percentage}`)
const opt = R.merge(this.Ae.defaults, options)
recipientId = await this.resolveName(recipientId, 'ak', opt)

const requestTransferAmount = BigNumber(await this.balance(await this.address())).times(percentage)
let spendTx = await this.spendTx(R.merge(opt, { senderId: await this.address(), recipientId, amount: requestTransferAmount }))
Expand Down Expand Up @@ -155,7 +135,7 @@ function destroyInstance () {
* @return {Object} Ae instance
*/
const Ae = stampit(Tx, Account, Chain, {
methods: { send, spend, transferFunds, destroyInstance, resolveRecipientName, signUsingGA },
methods: { send, spend, transferFunds, destroyInstance, signUsingGA },
deepProps: { Ae: { defaults: { denomination: AE_AMOUNT_FORMATS.AETTOS } } },
deepConfiguration: { Ae: { methods: ['signUsingGA'] } }
})
Expand Down
2 changes: 1 addition & 1 deletion es/chain/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const Chain = Oracle.compose({
Ae: {
methods: [
'sendTransaction', 'height', 'awaitHeight', 'poll', 'balance', 'getBalance', 'tx',
'mempool', 'topBlock', 'getTxInfo', 'txDryRun', 'getName', 'getNodeInfo', 'getAccount', 'getContractByteCode', 'getContract', 'waitForTxConfirm'
'mempool', 'topBlock', 'getTxInfo', 'txDryRun', 'getName', 'getNodeInfo', 'getAccount', 'getContractByteCode', 'getContract', 'waitForTxConfirm', 'resolveName'
]
}
}
Expand Down
33 changes: 32 additions & 1 deletion es/chain/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import Oracle from '../oracle/node'
import { AE_AMOUNT_FORMATS, formatAmount } from '../utils/amount-formatter'
import TransactionValidator from '../tx/validator'
import NodePool from '../node-pool'
import { assertedType } from '../utils/crypto'
import { isNameValid, produceNameId } from '../tx/builder/helpers'
import { NAME_ID_KEY } from '../tx/builder/schema'

/**
* ChainNode module
Expand Down Expand Up @@ -206,6 +209,33 @@ async function getName (name) {
return this.api.getNameEntryByName(name)
}

/**
* Resolve AENS name and return name hash
* @param {String} nameOrId
* @param {String} prefix
* @param {Boolean} verify
* @param {Boolean} resolveByNode
* @return {String} Address or AENS name hash
*/
async function resolveName (nameOrId, prefix, { verify = false, resolveByNode = false } = {}) {
const prefixes = Object.keys(NAME_ID_KEY)
if (!nameOrId || typeof nameOrId !== 'string') throw new Error('Invalid name or address. Should be a string')
if (!prefixes.includes(prefix)) throw new Error(`Invalid prefix ${prefix}. Should be one of [${prefixes}]`)
if (assertedType(nameOrId, prefix, true)) return nameOrId

if (isNameValid(nameOrId)) {
if (resolveByNode || verify) {
const name = await this.getName(nameOrId).catch(_ => null)
if (!name) throw new Error('Name not found')
const pointer = name.pointers.find(({ id }) => id.split('_')[0] === prefix)
if (!pointer) throw new Error(`Name ${nameOrId} do not have pointers for ${prefix}`)
return pointer.id
}
return produceNameId(nameOrId)
}
throw new Error('Invalid name or address')
}

/**
* ChainNode Stamp
*
Expand Down Expand Up @@ -243,7 +273,8 @@ const ChainNode = Chain.compose(Oracle, TransactionValidator, NodePool, {
getContractByteCode,
getContract,
getName,
waitForTxConfirm
waitForTxConfirm,
resolveName
}
})

Expand Down
1 change: 1 addition & 0 deletions es/contract/aci/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export const prepareArgsForEncode = prepareArgs
*/
async function getContractInstance (source, { aci, contractAddress, filesystem = {}, forceCodeCheck = true, opt } = {}) {
aci = aci || await this.contractGetACI(source, { filesystem })
if (contractAddress) contractAddress = await this.resolveName(contractAddress, 'ct', { resolveByNode: true })
const defaultOptions = {
skipArgsConvert: false,
skipTransformDecoded: false,
Expand Down
13 changes: 3 additions & 10 deletions es/tx/builder/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
NAME_FEE_BID_INCREMENT,
NAME_BID_TIMEOUTS,
FATE_ABI,
VM_TYPE
VM_TYPE, NAME_ID_KEY
} from './schema'
import { ceil } from '../../utils/bignumber'

Expand Down Expand Up @@ -279,20 +279,13 @@ export function isNameValid (name, throwError = true) {
* returns the type, or throws an exception if type not found.
*/
export function classify (s) {
const keys = {
ak: 'account_pubkey',
ok: 'oracle_pubkey',
ct: 'contract_pubkey',
ch: 'channel'
}

if (!s.match(/^[a-z]{2}_.+/)) {
throw Error('Not a valid hash')
}

const klass = s.substr(0, 2)
if (klass in keys) {
return keys[klass]
if (klass in NAME_ID_KEY) {
return NAME_ID_KEY[klass]
} else {
throw Error(`Unknown class ${klass}`)
}
Expand Down
8 changes: 7 additions & 1 deletion es/tx/builder/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const MIN_GAS_PRICE = 1000000000 // min gasPrice 1e9
export const MAX_AUTH_FUN_GAS = 50000
export const DRY_RUN_ACCOUNT = { pub: 'ak_11111111111111111111111111111111273Yts', amount: '100000000000000000000000000000000000' }
// # AENS
export const AENS_NAME_DOMAINS = ['chain', 'test']
export const AENS_NAME_DOMAINS = ['chain']
export const NAME_TTL = 50000
// # max number of block into the future that the name is going to be available
// # https://github.com/aeternity/protocol/blob/epoch-v0.22.0/AENS.md#update
Expand All @@ -42,6 +42,12 @@ export const NAME_FEE_BID_INCREMENT = 0.05 // # the increment is in percentage
// # see https://github.com/aeternity/aeternity/blob/72e440b8731422e335f879a31ecbbee7ac23a1cf/apps/aecore/src/aec_governance.erl#L272
export const NAME_BID_TIMEOUT_BLOCKS = 480 // # ~1 day
export const NAME_BID_MAX_LENGTH = 12 // # this is the max length for a domain to be part of a bid
export const NAME_ID_KEY = {
ak: 'account_pubkey',
ok: 'oracle_pubkey',
ct: 'contract_pubkey',
ch: 'channel'
}
// # ref: https://github.com/aeternity/aeternity/blob/72e440b8731422e335f879a31ecbbee7ac23a1cf/apps/aecore/src/aec_governance.erl#L290
// # bid ranges:
export const NAME_BID_RANGES = {
Expand Down
6 changes: 3 additions & 3 deletions es/utils/aepp-wallet-communication/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@ export const receive = (handler) => (msg, origin) => {
handler(msg, origin)
}

export const getHandler = (schema, msg) => {
export const getHandler = (schema, msg, { debug = false } = {}) => {
const handler = schema[msg.method]
if (!handler || typeof handler !== 'function') {
console.log(`Unknown message method ${msg.method}`)
return () => () => true
debug && console.log(`Unknown message method ${msg.method}`)
return () => async () => true
}
return handler
}
Expand Down
9 changes: 5 additions & 4 deletions es/utils/aepp-wallet-communication/rpc/aepp-rpc.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@ const REQUESTS = {}

const handleMessage = (instance) => async (msg) => {
if (!msg.id) {
return getHandler(NOTIFICATIONS, msg)(instance)(msg)
return getHandler(NOTIFICATIONS, msg, { debug: instance.debug })(instance)(msg)
} else if (Object.prototype.hasOwnProperty.call(instance.rpcClient.callbacks, msg.id)) {
return getHandler(RESPONSES, msg)(instance)(msg)
return getHandler(RESPONSES, msg, { debug: instance.debug })(instance)(msg)
} else {
return getHandler(REQUESTS, msg)(instance)(msg)
return getHandler(REQUESTS, msg, { debug: instance.debug })(instance)(msg)
}
}

Expand All @@ -84,10 +84,11 @@ const handleMessage = (instance) => async (msg) => {
* @return {Object}
*/
export const AeppRpc = Ae.compose({
async init ({ name, onAddressChange = voidFn, onDisconnect = voidFn, onNetworkChange = voidFn, connection, forceValidation = false }) {
async init ({ name, onAddressChange = voidFn, onDisconnect = voidFn, onNetworkChange = voidFn, connection, forceValidation = false, debug = false }) {
const eventsHandlers = ['onDisconnect', 'onAddressChange', 'onNetworkChange']
this.connection = connection
this.name = name
this.debug = debug

if (connection) {
// Init RPCClient
Expand Down
25 changes: 14 additions & 11 deletions es/utils/aepp-wallet-communication/rpc/wallet-rpc.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const rpcClients = RpcClients()

const NOTIFICATIONS = {
[METHODS.closeConnection]: (instance, { client }) =>
(msg) => {
async (msg) => {
client.disconnect(true)
instance.onDisconnect(msg.params, client)
}
Expand All @@ -30,7 +30,7 @@ const RESPONSES = {}
const REQUESTS = {
// Store client info and prepare two fn for each client `connect` and `denyConnection`
// which automatically prepare and send response for that client
async [METHODS.aepp.connect] (callInstance, instance, client, { name, networkId, version, icons }) {
[METHODS.aepp.connect] (callInstance, instance, client, { name, networkId, version, icons }) {
// Check if protocol and network is compatible with wallet
if (version !== VERSION) return { error: ERRORS.unsupportedProtocol() }

Expand All @@ -57,7 +57,7 @@ const REQUESTS = {
}
)
},
async [METHODS.aepp.subscribeAddress] (callInstance, instance, client, { type, value }) {
[METHODS.aepp.subscribeAddress] (callInstance, instance, client, { type, value }) {
// Authorization check
if (!client.isConnected()) return { error: ERRORS.notAuthorize() }

Expand All @@ -84,7 +84,7 @@ const REQUESTS = {
(error) => ({ error: ERRORS.rejectedByUser(error) })
)
},
async [METHODS.aepp.address] (callInstance, instance, client) {
[METHODS.aepp.address] (callInstance, instance, client) {
// Authorization check
if (!client.isConnected()) return { error: ERRORS.notAuthorize() }
if (!client.isSubscribed()) return { error: ERRORS.notAuthorize() }
Expand All @@ -96,7 +96,7 @@ const REQUESTS = {
(error) => ({ error: ERRORS.rejectedByUser(error) })
)
},
async [METHODS.aepp.sign] (callInstance, instance, client, { tx, onAccount, networkId, returnSigned = false }) {
[METHODS.aepp.sign] (callInstance, instance, client, { tx, onAccount, networkId, returnSigned = false }) {
const address = onAccount || client.currentAccount
// Update client with new networkId
networkId && rpcClients.updateClientInfo(client.id, { networkId })
Expand Down Expand Up @@ -140,7 +140,7 @@ const REQUESTS = {
(error) => ({ error: ERRORS.rejectedByUser(error) })
)
},
async [METHODS.aepp.signMessage] (callInstance, instance, client, { message, onAccount }) {
[METHODS.aepp.signMessage] (callInstance, instance, client, { message, onAccount }) {
// Authorization check
if (!client.isConnected()) return { error: ERRORS.notAuthorize() }
const address = onAccount || client.currentAccount
Expand Down Expand Up @@ -173,13 +173,13 @@ const REQUESTS = {
const handleMessage = (instance, id) => async (msg, origin) => {
const client = rpcClients.getClient(id)
if (!msg.id) {
return getHandler(NOTIFICATIONS, msg)(instance, { client })(msg, origin)
return getHandler(NOTIFICATIONS, msg, { debug: instance.debug })(instance, { client })(msg, origin)
}
if (Object.prototype.hasOwnProperty.call(client.callbacks, msg.id)) {
return getHandler(RESPONSES, msg)(instance, { client })(msg, origin)
return getHandler(RESPONSES, msg, { debug: instance.debug })(instance, { client })(msg, origin)
} else {
const { id, method } = msg
const callInstance = (methodName, params, accept, deny) => new Promise(resolve => {
const callInstance = (methodName, params, accept, deny) => () => new Promise(resolve => {
instance[methodName](
client,
client.addAction({ id, method, params }, [
Expand All @@ -188,7 +188,9 @@ const handleMessage = (instance, id) => async (msg, origin) => {
origin
)
})
const response = await getHandler(REQUESTS, msg)(callInstance, instance, client, msg.params)
// TODO make one structure for handler functions
const errorObjectOrHandler = getHandler(REQUESTS, msg, { debug: instance.debug })(callInstance, instance, client, msg.params)
const response = typeof errorObjectOrHandler === 'function' ? await errorObjectOrHandler() : errorObjectOrHandler
sendResponseMessage(client)(id, method, response)
}
}
Expand All @@ -209,7 +211,8 @@ const handleMessage = (instance, id) => async (msg, origin) => {
* @return {Object}
*/
export const WalletRpc = Ae.compose(Accounts, Selector, {
init ({ name, onConnection, onSubscription, onSign, onDisconnect, onAskAccounts, onMessageSign, forceValidation = false }) {
init ({ name, onConnection, onSubscription, onSign, onDisconnect, onAskAccounts, onMessageSign, forceValidation = false, debug = false } = {}) {
this.debug = debug
const eventsHandlers = ['onConnection', 'onSubscription', 'onSign', 'onDisconnect', 'onMessageSign']
// CallBacks for events
this.onConnection = onConnection
Expand Down
Loading

0 comments on commit 5fed974

Please sign in to comment.