diff --git a/src/ae/aepp.js b/src/ae/aepp.ts similarity index 88% rename from src/ae/aepp.js rename to src/ae/aepp.ts index 143b25f6d2..3e5405bc07 100644 --- a/src/ae/aepp.js +++ b/src/ae/aepp.ts @@ -22,11 +22,12 @@ * @example import { RpcAepp } from '@aeternity/aepp-sdk' */ -import Ae from './' +import Ae from '.' import ContractCompilerHttp from '../contract/compiler' import AeppRpc from '../utils/aepp-wallet-communication/rpc/aepp-rpc' -import Oracle from './oracle' import asyncInit from '../utils/async-init' +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import stampit from '@stamp/it' /** * Aepp Stamp @@ -40,4 +41,4 @@ import asyncInit from '../utils/async-init' * @param {Object} [options={}] - Initializer object * @return {Object} Aepp instance */ -export default Ae.compose(asyncInit, Oracle, ContractCompilerHttp, AeppRpc) +export default Ae.compose(asyncInit, ContractCompilerHttp, AeppRpc) diff --git a/src/ae/index.ts b/src/ae/index.ts index 0366c5cd54..c807d4c27e 100644 --- a/src/ae/index.ts +++ b/src/ae/index.ts @@ -26,6 +26,7 @@ import * as chainMethods from '../chain' import * as txMethods from '../tx' import * as aensMethods from './aens' import * as spendMethods from './spend' +import * as oracleMethods from './oracle' import * as contractMethods from './contract' import * as contractGaMethods from '../contract/ga' import NodePool from '../node-pool' @@ -52,6 +53,7 @@ export default stampit(NodePool, AccountResolver, { ...contractMethods, ...contractGaMethods, ...aensMethods, + ...oracleMethods, buildTx: _buildTx }, ([name, handler]) => [ diff --git a/src/ae/oracle.js b/src/ae/oracle.js deleted file mode 100644 index 3a49a4a087..0000000000 --- a/src/ae/oracle.js +++ /dev/null @@ -1,305 +0,0 @@ -/* - * 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. - */ - -/** - * Oracle module - routines to interact with the æternity oracle system - * - * The high-level description of the oracle system is - * https://github.com/aeternity/protocol/blob/master/ORACLE.md in the protocol - * repository. - * @module @aeternity/aepp-sdk/es/ae/oracle - * @export Oracle - * @example import { Oracle } from '@aeternity/aepp-sdk' - */ - -import Ae from './' -import { pause } from '../utils/other' -import { oracleQueryId, decode } from '../tx/builder/helpers' -import { unpackTx } from '../tx/builder' -import { ORACLE_TTL, QUERY_FEE, QUERY_TTL, RESPONSE_TTL, TX_TYPE } from '../tx/builder/schema' -import { RequestTimedOutError } from '../utils/errors' - -/** - * Constructor for Oracle Object (helper object for using Oracle) - * @alias module:@aeternity/aepp-sdk/es/ae/oracle - * @instance - * @function - * @category async - * @param {String} oracleId Oracle public key - * @return {Promise} Oracle object - */ -async function getOracleObject (oracleId) { - const oracle = await this.api.getOracleByPubkey(oracleId) - const { oracleQueries: queries } = await this.api.getOracleQueriesByPubkey(oracleId) - return { - ...oracle, - queries, - pollQueries: this.pollForQueries.bind(this, oracleId), - postQuery: this.postQueryToOracle.bind(this, oracleId), - respondToQuery: this.respondToQuery.bind(this, oracleId), - extendOracle: this.extendOracleTtl.bind(this, oracleId), - getQuery: this.getQueryObject.bind(this, oracleId) - } -} - -/** - * Poll for oracle queries - * @alias module:@aeternity/aepp-sdk/es/ae/oracle - * @instance - * @function - * @param {String} oracleId Oracle public key - * @param {Function} onQuery OnQuery callback - * @param {Object} [options] Options object - * @param {Number} [options.interval] Poll interval(default: 5000) - * @return {Function} stopPolling - Stop polling function - */ -function pollForQueries ( - oracleId, - onQuery, - { interval = this._getPollInterval('microblock') } = {} -) { - const knownQueryIds = new Set() - const checkNewQueries = async () => { - const queries = ((await this.api.getOracleQueriesByPubkey(oracleId)).oracleQueries || []) - .filter(({ id }) => !knownQueryIds.has(id)) - queries.forEach(({ id }) => knownQueryIds.add(id)) - if (queries.length) onQuery(queries) - } - - let stopped - (async () => { - while (!stopped) { // eslint-disable-line no-unmodified-loop-condition - // TODO: allow to handle this error somehow - await checkNewQueries().catch(console.error) - await pause(interval) - } - })() - return () => { stopped = true } -} - -/** - * Constructor for OracleQuery Object (helper object for using OracleQuery) - * @alias module:@aeternity/aepp-sdk/es/ae/oracle - * @instance - * @function - * @category async - * @param {String} oracleId Oracle public key - * @param {String} queryId Oracle Query id - * @return {Promise} OracleQuery object - */ -async function getQueryObject (oracleId, queryId) { - const q = await this.api.getOracleQueryByPubkeyAndQueryId(oracleId, queryId) - return { - ...q, - decodedQuery: decode(q.query).toString(), - decodedResponse: decode(q.response).toString(), - respond: this.respondToQuery.bind(this, oracleId, queryId), - pollForResponse: this.pollForQueryResponse.bind(this, oracleId, queryId) - } -} - -/** - * Poll for oracle query response - * @alias module:@aeternity/aepp-sdk/es/ae/oracle - * @instance - * @function - * @category async - * @param {String} oracleId Oracle public key - * @param {String} queryId Oracle Query id - * @param {Object} [options] Options object - * @param {Object} [options.attempts] Poll attempt's(default: 20) - * @param {Object} [options.interval] Poll interval(default: 5000) - * @return {Promise} OracleQuery object - */ -export async function pollForQueryResponse ( - oracleId, - queryId, - { attempts = 20, interval = this._getPollInterval('microblock') } = {} -) { - for (let i = 0; i < attempts; i++) { - if (i) await pause(interval) - const { response } = await this.api.getOracleQueryByPubkeyAndQueryId(oracleId, queryId) - const responseBuffer = decode(response, 'or') - if (responseBuffer.length) { - return String(responseBuffer) - } - } - throw new RequestTimedOutError((attempts - 1) * interval) -} - -/** - * Register oracle - * @alias module:@aeternity/aepp-sdk/es/ae/oracle - * @instance - * @function - * @category async - * @param {String} queryFormat Format of query - * @param {String} responseFormat Format of query response - * @param {Object} [options={}] Options - * @param {String|Number} [options.queryFee] Oracle query Fee - * @param {Object} [options.oracleTtl] OracleTtl object {type: 'delta|block', value: 'number'} - * @param {Number} [options.abiVersion] Always 0 (do not use virtual machine) - * @param {Number|String} [options.fee] Transaction fee - * @param {Number|String} [options.ttl] Transaction time to leave - * @return {Promise} Oracle object - */ -async function registerOracle (queryFormat, responseFormat, options = {}) { - const opt = { ...this.Ae.defaults, ...options } // Preset VmVersion for oracle - const accountId = await this.address(opt) - - const oracleRegisterTx = await this.buildTx(TX_TYPE.oracleRegister, { - ...opt, - accountId, - queryFormat, - responseFormat - }) - return { - ...await this.send(oracleRegisterTx, opt), - ...await this.getOracleObject(`ok_${accountId.slice(3)}`) - } -} - -/** - * Post query to oracle - * @alias module:@aeternity/aepp-sdk/es/ae/oracle - * @instance - * @function - * @category async - * @param {String} oracleId Oracle public key - * @param {String} query Oracle query object - * @param {Object} [options={}] - * @param {String|Number} [options.queryTtl] queryTtl Oracle query time to leave - * @param {String|Number} [options.responseTtl] queryFee Oracle query response time to leave - * @param {String|Number} [options.queryFee] queryFee Oracle query fee - * @param {Number} [options.fee] fee Transaction fee - * @param {Number} [options.ttl] Transaction time to leave - * @return {Promise} Query object - */ -async function postQueryToOracle (oracleId, query, options = {}) { - const queryFee = options.queryFee || (await this.api.getOracleByPubkey(oracleId)).queryFee - const opt = { ...this.Ae.defaults, queryFee, ...options } - const senderId = await this.address(opt) - - const oracleQueryTx = await this.buildTx(TX_TYPE.oracleQuery, { - ...opt, - oracleId, - senderId, - query - }) - const queryId = oracleQueryId(senderId, unpackTx(oracleQueryTx).tx.nonce, oracleId) - return { - ...await this.send(oracleQueryTx, opt), - ...await this.getQueryObject(oracleId, queryId) - } -} - -/** - * Extend oracle ttl - * @alias module:@aeternity/aepp-sdk/es/ae/oracle - * @instance - * @function - * @category async - * @param {String} oracleId Oracle public key - * @param {String} oracleTtl Oracle time to leave for extend - * @param {Object} [options={}] - * @param {Number} [options.fee] fee Transaction fee - * @param {Number} [options.ttl] Transaction time to leave - * @return {Promise} Oracle object - */ -async function extendOracleTtl (oracleId, oracleTtl, options = {}) { - const opt = { ...this.Ae.defaults, ...options } - const callerId = await this.address(opt) - - const oracleExtendTx = await this.buildTx(TX_TYPE.oracleExtend, { - ...opt, - oracleId, - callerId, - oracleTtl - }) - return { - ...await this.send(oracleExtendTx, opt), - ...await this.getOracleObject(oracleId) - } -} - -/** - * Extend oracle ttl - * @alias module:@aeternity/aepp-sdk/es/ae/oracle - * @instance - * @function - * @category async - * @param {String} oracleId Oracle public key - * @param {String} queryId Oracle query id - * @param {String} response Oracle query response - * @param {Object} [options={}] - * @param {Number} [options.responseTtl] responseTtl Query response time to leave - * @param {Number} [options.fee] Transaction fee - * @param {Number} [options.ttl] Transaction time to leave - * @return {Promise} Oracle object - */ -async function respondToQuery (oracleId, queryId, response, options = {}) { - const opt = { ...this.Ae.defaults, ...options } - const callerId = await this.address(opt) - - const oracleRespondTx = await this.buildTx(TX_TYPE.oracleResponse, { - ...opt, - oracleId, - queryId, - callerId, - response - }) - return { - ...await this.send(oracleRespondTx, opt), - ...await this.getOracleObject(oracleId) - } -} - -/** - * Oracle Stamp - * - * Oracle provides oracle-system related methods atop - * {@link module:@aeternity/aepp-sdk/es/ae--Ae} clients. - * @function - * @alias module:@aeternity/aepp-sdk/es/ae/oracle - * @rtype Stamp - * @param {Object} [options={}] - Initializer object - * @return {Object} Oracle instance - */ -const Oracle = Ae.compose({ - methods: { - registerOracle, - respondToQuery, - extendOracleTtl, - postQueryToOracle, - pollForQueryResponse, - pollForQueries, - getOracleObject, - getQueryObject - }, - deepProps: { - Ae: { - defaults: { - queryFee: QUERY_FEE, - oracleTtl: ORACLE_TTL, - queryTtl: QUERY_TTL, - responseTtl: RESPONSE_TTL - } - } - } -}) - -export default Oracle diff --git a/src/ae/oracle.ts b/src/ae/oracle.ts new file mode 100644 index 0000000000..c31b39704b --- /dev/null +++ b/src/ae/oracle.ts @@ -0,0 +1,349 @@ +/* + * ISC License (ISC) + * Copyright (c) 2022 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. + */ + +/** + * Oracle methods - routines to interact with the æternity oracle system + * + * The high-level description of the oracle system is + * https://github.com/aeternity/protocol/blob/master/ORACLE.md in the protocol + * repository. + * @module @aeternity/aepp-sdk/es/ae/oracle + * @export Oracle + * @example import * as oracleMethods from '@aeternity/aepp-sdk' + */ + +import { send } from './spend' +import { mapObject, pause } from '../utils/other' +import { oracleQueryId, decode, encode } from '../tx/builder/helpers' +import { unpackTx } from '../tx/builder' +import { + ORACLE_TTL, + ORACLE_TTL_TYPES, + QUERY_FEE, + QUERY_TTL, + RESPONSE_TTL, + TX_TYPE +} from '../tx/builder/schema' +import { RequestTimedOutError } from '../utils/errors' +import { EncodedData } from '../utils/encoder' +import { _getPollInterval } from '../chain' +import { _buildTx, BuildTxOptions } from '../tx' +import Node from '../node' +import BigNumber from 'bignumber.js' +import { _AccountBase } from '../account/base' + +type OracleQueries = Awaited>['oracleQueries'] + +/** + * Constructor for Oracle Object (helper object for using Oracle) + * @alias module:@aeternity/aepp-sdk/es/ae/oracle + * @category async + * @param oracleId Oracle public key + * @param options + * @return Oracle object + */ +export async function getOracleObject ( + oracleId: EncodedData<'ok'>, options: { onNode: Node, onAccount: _AccountBase } +): Promise< + Awaited> & { + id: EncodedData<'ok'> + queries: OracleQueries + // TODO: replace getOracleObject with a class + pollQueries: Function + postQuery: Function + respondToQuery: Function + extendOracle: Function + getQuery: Function + } + > { + return { + ...await options.onNode.getOracleByPubkey(oracleId), + queries: (await options.onNode.getOracleQueriesByPubkey(oracleId)).oracleQueries, + ...mapObject( + { + pollQueries: pollForQueries, + postQuery: postQueryToOracle, + respondToQuery, + extendOracle: extendOracleTtl, + getQuery: getQueryObject + }, + ([name, handler]) => [ + name, + function (...args: any) { + const lastArg = args[args.length - 1] + if (lastArg != null && typeof lastArg === 'object' && lastArg.constructor === Object) { + Object.assign(lastArg, { ...options, ...lastArg }) + } else args.push(options) + return handler(oracleId, ...args) + } + ]) + } as any +} + +/** + * Poll for oracle queries + * @alias module:@aeternity/aepp-sdk/es/ae/oracle + * @param oracleId Oracle public key + * @param onQuery OnQuery callback + * @param options Options object + * @param options.interval Poll interval(default: 5000) + * @param options.onNode Node to use + * @return Callback to stop polling function + */ +export function pollForQueries ( + oracleId: EncodedData<'ok'>, + onQuery: (queries: OracleQueries) => void, + { interval, onNode, ...options }: { interval?: number, onNode: Node } + & Parameters[1] +): () => void { + interval ??= _getPollInterval('microblock', options) + const knownQueryIds = new Set() + const checkNewQueries = async (): Promise => { + const queries = ((await onNode.getOracleQueriesByPubkey(oracleId)).oracleQueries ?? []) + .filter(({ id }) => !knownQueryIds.has(id)) + queries.forEach(({ id }) => knownQueryIds.add(id)) + if (queries.length > 0) onQuery(queries) + } + + let stopped = false; + + // eslint-disable-next-line @typescript-eslint/no-floating-promises + (async () => { + while (!stopped) { // eslint-disable-line no-unmodified-loop-condition + // TODO: allow to handle this error somehow + await checkNewQueries().catch(console.error) + await pause(interval) + } + })() + return () => { stopped = true } +} + +/** + * Constructor for OracleQuery Object (helper object for using OracleQuery) + * @alias module:@aeternity/aepp-sdk/es/ae/oracle + * @param oracleId Oracle public key + * @param queryId Oracle Query id + * @param options + * @return OracleQuery object + */ +export async function getQueryObject ( + oracleId: EncodedData<'ok'>, + queryId: EncodedData<'oq'>, + options: Parameters[3] & Parameters[2] +): Promise< + Awaited> & { + decodedQuery: string + decodedResponse: string + respond: (response: string, options: Parameters[3]) => + ReturnType + pollForResponse: (options: Parameters[2]) => + ReturnType + } + > { + const record = await options.onNode.getOracleQueryByPubkeyAndQueryId(oracleId, queryId) + return { + ...record, + decodedQuery: decode(record.query as EncodedData<'oq'>).toString(), + decodedResponse: decode(record.response as EncodedData<'or'>).toString(), + respond: async (response, opt) => + await respondToQuery(oracleId, queryId, response, { ...options, ...opt }), + pollForResponse: async (opt) => + await pollForQueryResponse(oracleId, queryId, { ...options, ...opt }) + } +} + +/** + * Poll for oracle query response + * @alias module:@aeternity/aepp-sdk/es/ae/oracle + * @category async + * @param oracleId Oracle public key + * @param queryId Oracle Query id + * @param options Options object + * @param options.attempts Poll attempts + * @param options.interval Poll interval + * @param options.onNode Node to use + * @return OracleQuery object + */ +export async function pollForQueryResponse ( + oracleId: EncodedData<'ok'>, + queryId: EncodedData<'oq'>, + { attempts = 20, interval, onNode, ...options }: + { attempts?: number, interval?: number, onNode: Node } & Parameters[1] +): Promise { + interval ??= _getPollInterval('microblock', options) + for (let i = 0; i < attempts; i++) { + if (i > 0) await pause(interval) + const { response } = await onNode.getOracleQueryByPubkeyAndQueryId(oracleId, queryId) + const responseBuffer = decode(response as EncodedData<'or'>) + if (responseBuffer.length > 0) return responseBuffer.toString() + } + throw new RequestTimedOutError((attempts - 1) * interval) +} + +/** + * Register oracle + * @alias module:@aeternity/aepp-sdk/es/ae/oracle + * @param queryFormat Format of query + * @param responseFormat Format of query response + * @param options + * @param options.queryFee Oracle query Fee + * @param options Options object + * @param options.abiVersion Always 0 (do not use virtual machine) + * @param options.fee Transaction fee + * @param options.ttl Transaction time to leave + * @return Oracle object + */ +export async function registerOracle ( + queryFormat: string, + responseFormat: string, + options: Parameters[1] & Parameters[1] + & BuildTxOptions< + TX_TYPE.oracleRegister, + 'accountId' | 'queryFormat' | 'responseFormat' | 'queryFee' | 'oracleTtlType' | 'oracleTtlValue' + > + & { + queryFee?: number | string | BigNumber + oracleTtlType?: ORACLE_TTL_TYPES + oracleTtlValue?: number + } +): Promise> & Awaited>> { + const accountId = await options.onAccount.address(options) + const oracleRegisterTx = await _buildTx(TX_TYPE.oracleRegister, { + queryFee: QUERY_FEE, + oracleTtlValue: ORACLE_TTL.value, + oracleTtlType: ORACLE_TTL.type, + ...options, + accountId, + queryFormat, + responseFormat + }) + return { + ...await send(oracleRegisterTx, options), + ...await getOracleObject(encode(decode(accountId), 'ok'), options) + } +} + +/** + * Post query to oracle + * @alias module:@aeternity/aepp-sdk/es/ae/oracle + * @param oracleId Oracle public key + * @param query Oracle query object + * @param options Options object + * @param options.queryTtl queryTtl Oracle query time to leave + * @param options.responseTtl queryFee Oracle query response time to leave + * @param options.queryFee queryFee Oracle query fee + * @param options.fee fee Transaction fee + * @param options.ttl Transaction time to leave + * @return Query object + */ +export async function postQueryToOracle (oracleId: EncodedData<'ok'>, + query: string, + options: Parameters[1] & Parameters[2] + & BuildTxOptions< + TX_TYPE.oracleQuery, + 'oracleId' | 'senderId' | 'query' | 'queryTtlType' | 'queryTtlValue' | 'responseTtlType' | 'responseTtlValue' + > + & { + queryTtlType?: ORACLE_TTL_TYPES + queryTtlValue?: number + responseTtlType?: ORACLE_TTL_TYPES + responseTtlValue?: number + } +): Promise> & Awaited>> { + options.queryFee ??= (await options.onNode.getOracleByPubkey(oracleId)).queryFee.toString() + const senderId = await options.onAccount.address(options) + + const oracleQueryTx = await _buildTx(TX_TYPE.oracleQuery, { + queryTtlType: QUERY_TTL.type, + queryTtlValue: QUERY_TTL.value, + responseTtlType: RESPONSE_TTL.type, + responseTtlValue: RESPONSE_TTL.value, + ...options, + oracleId, + senderId, + query + }) + const nonce = unpackTx(oracleQueryTx, TX_TYPE.oracleQuery).tx.nonce + const queryId = oracleQueryId(senderId, nonce, oracleId) + return { + ...await send(oracleQueryTx, options), + ...await getQueryObject(oracleId, queryId, options) + } +} + +/** + * Extend oracle ttl + * @alias module:@aeternity/aepp-sdk/es/ae/oracle + * @param oracleId Oracle public key + * @param options Options object + * @param options.fee fee Transaction fee + * @param options.ttl Transaction time to leave + * @param options.oracleTtlType Oracle time to leave for extend + * @param options.oracleTtlValue Oracle time to leave for extend + * @return Oracle object + */ +export async function extendOracleTtl ( + oracleId: EncodedData<'ok'>, + options: Parameters[1] & Parameters[1] + & BuildTxOptions + & { oracleTtlType?: ORACLE_TTL_TYPES, oracleTtlValue?: number } +): Promise> & Awaited>> { + const oracleExtendTx = await _buildTx(TX_TYPE.oracleExtend, { + oracleTtlType: ORACLE_TTL.type, + oracleTtlValue: ORACLE_TTL.value, + ...options, + oracleId + }) + return { + ...await send(oracleExtendTx, options), + ...await getOracleObject(oracleId, options) + } +} + +/** + * Extend oracle ttl + * @alias module:@aeternity/aepp-sdk/es/ae/oracle + * @param oracleId Oracle public key + * @param queryId Oracle query id + * @param response Oracle query response + * @param options Options object + * @param options.responseTtl responseTtl Query response time to leave + * @param options.fee Transaction fee + * @param options.ttl Transaction time to leave + * @return Oracle object + */ +export async function respondToQuery ( + oracleId: EncodedData<'ok'>, + queryId: EncodedData<'oq'>, + response: string, + options: Parameters[1] & Parameters[1] + & BuildTxOptions + & { responseTtlType?: ORACLE_TTL_TYPES, responseTtlValue?: number } +): Promise> & Awaited>> { + const oracleRespondTx = await _buildTx(TX_TYPE.oracleResponse, { + responseTtlType: RESPONSE_TTL.type, + responseTtlValue: RESPONSE_TTL.value, + ...options, + oracleId, + queryId, + response + }) + return { + ...await send(oracleRespondTx, options), + ...await getOracleObject(oracleId, options) + } +} diff --git a/src/ae/universal.js b/src/ae/universal.ts similarity index 88% rename from src/ae/universal.js rename to src/ae/universal.ts index e39b20ed24..8030c17622 100644 --- a/src/ae/universal.js +++ b/src/ae/universal.ts @@ -22,10 +22,11 @@ * @example import { Universal } from '@aeternity/aepp-sdk' */ -import Ae from './' -import Oracle from './oracle' +import Ae from '.' import AccountMultiple from '../account/multiple' import ContractCompilerHttp from '../contract/compiler' +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import stampit from '@stamp/it' /** * Universal Stamp @@ -38,4 +39,4 @@ import ContractCompilerHttp from '../contract/compiler' * @param {Object} [options={}] - Initializer object * @return {Object} Universal instance */ -export default Ae.compose(Oracle, AccountMultiple, ContractCompilerHttp) +export default Ae.compose(AccountMultiple, ContractCompilerHttp) diff --git a/src/ae/wallet.js b/src/ae/wallet.ts similarity index 90% rename from src/ae/wallet.js rename to src/ae/wallet.ts index cd28c88da5..d11e7f5f97 100644 --- a/src/ae/wallet.js +++ b/src/ae/wallet.ts @@ -22,10 +22,11 @@ * @example import { RpcWallet } from '@aeternity/aepp-sdk' */ -import Ae from './' +import Ae from '.' import ContractCompilerHttp from '../contract/compiler' import WalletRpc from '../utils/aepp-wallet-communication/rpc/wallet-rpc' -import Oracle from './oracle' +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import stampit from '@stamp/it' /** * Wallet Stamp @@ -43,4 +44,4 @@ import Oracle from './oracle' address: keypair.publicKey, }) */ -export default Ae.compose(Oracle, WalletRpc, ContractCompilerHttp) +export default Ae.compose(WalletRpc, ContractCompilerHttp) diff --git a/src/index.ts b/src/index.ts index 8c93573cdb..1f4114e2ba 100644 --- a/src/index.ts +++ b/src/index.ts @@ -27,6 +27,7 @@ export * from './utils/amount-formatter' export * from './utils/hd-wallet' export * from './ae/aens' export * from './ae/contract' +export * from './ae/oracle' export * from './ae/spend' export * from './contract/ga' @@ -38,14 +39,9 @@ export { default as AccountBase } from './account/base' export { default as AccountMultiple } from './account/multiple' export { default as MemoryAccount } from './account/memory' export { default as ContractCompilerHttp } from './contract/compiler' -// @ts-expect-error export { default as RpcAepp } from './ae/aepp' -// @ts-expect-error export { default as RpcWallet } from './ae/wallet' -// @ts-expect-error -export { default as Oracle } from './ae/oracle' export { default as Channel } from './channel' -// @ts-expect-error export { default as Universal } from './ae/universal' export { default as connectionProxy } from './utils/aepp-wallet-communication/connection-proxy' diff --git a/src/tx/builder/index.ts b/src/tx/builder/index.ts index 2cab95749f..3b269c5175 100644 --- a/src/tx/builder/index.ts +++ b/src/tx/builder/index.ts @@ -42,11 +42,6 @@ import { isKeyOfObject } from '../../utils/other' * @example import { calculateFee } from '@aeternity/aepp-sdk' */ -enum ORACLE_TTL_TYPES { - delta, - block -} - // SERIALIZE AND DESERIALIZE PART function deserializeField ( value: any, @@ -65,6 +60,7 @@ function deserializeField ( case FIELD_TYPES.amount: case FIELD_TYPES.int: case FIELD_TYPES.abiVersion: + case FIELD_TYPES.ttlType: return readInt(value) case FIELD_TYPES.id: return readId(value) @@ -134,6 +130,7 @@ function serializeField ( case FIELD_TYPES.amount: case FIELD_TYPES.int: case FIELD_TYPES.abiVersion: + case FIELD_TYPES.ttlType: return writeInt(value) case FIELD_TYPES.id: return writeId(value) @@ -232,24 +229,7 @@ function transformParams ( }), params ) - const schemaKeys = schema.map(([k]) => k) - return Object - .entries(params) - .reduce( - (acc: any, [key, value]: [key: string, - value: { - type: string - value: number - }]) => { - if (schemaKeys.includes(key)) acc[key] = value - if (['oracleTtl', 'queryTtl', 'responseTtl'].includes(key)) { - acc[`${key}Type`] = Object.values(ORACLE_TTL_TYPES).indexOf(value.type) - acc[`${key}Value`] = value.value - } - return acc - }, - {} - ) + return params } function getOracleRelativeTtl (params: any, txType: TX_TYPE): number { diff --git a/src/tx/builder/schema.ts b/src/tx/builder/schema.ts index 895a117f11..157d5b067a 100644 --- a/src/tx/builder/schema.ts +++ b/src/tx/builder/schema.ts @@ -14,13 +14,18 @@ import MPTree from '../../utils/mptree' export * from './constants' +export enum ORACLE_TTL_TYPES { + delta = 0, + block = 1 +} + // # TRANSACTION DEFAULT TTL export const TX_TTL = 0 // # ORACLE export const QUERY_FEE = 30000 -export const ORACLE_TTL = { type: 'delta', value: 500 } -export const QUERY_TTL = { type: 'delta', value: 10 } -export const RESPONSE_TTL = { type: 'delta', value: 10 } +export const ORACLE_TTL = { type: ORACLE_TTL_TYPES.delta, value: 500 } +export const QUERY_TTL = { type: ORACLE_TTL_TYPES.delta, value: 10 } +export const RESPONSE_TTL = { type: ORACLE_TTL_TYPES.delta, value: 10 } // # CONTRACT export const AMOUNT = 0 export const GAS_MAX = 1600000 - 21000 @@ -165,6 +170,7 @@ export enum FIELD_TYPES { callReturnType, ctVersion, abiVersion, + ttlType, sophiaCodeTypeInfo, payload, stateTree @@ -191,6 +197,7 @@ interface BuildFieldTypes = // TODO: find a better name or rearrange methods export async function _buildTx ( txType: TxType, - _params: TxTypeSchemas[TxType] & { onNode: Node } + { onAccount, ..._params }: TxTypeSchemas[TxType] & { onNode: Node, onAccount: _AccountBase } ): Promise> { // TODO: avoid this assertion const params = _params as unknown as TxParamsCommon & { onNode: Node } - let senderKey: keyof TxParamsCommon + let senderKey: keyof TxParamsCommon | '' switch (txType) { case TX_TYPE.spend: case TX_TYPE.oracleQuery: @@ -70,9 +71,11 @@ export async function _buildTx ( senderKey = 'ownerId' break case TX_TYPE.contractCall: + senderKey = 'callerId' + break case TX_TYPE.oracleExtend: case TX_TYPE.oracleResponse: - senderKey = 'callerId' + senderKey = '' break case TX_TYPE.channelCloseSolo: case TX_TYPE.channelSlash: @@ -101,7 +104,7 @@ export async function _buildTx ( if (txType === TX_TYPE.payingFor) { params.tx = unpackTx(params.tx) } - const senderId = params[senderKey] + const senderId = senderKey === '' ? await onAccount.address() : params[senderKey] // TODO: do this check on TypeScript level if (senderId == null) throw new InvalidTxParamsError(`Transaction field ${senderKey} is missed`) const extraParams = await prepareTxParams(txType, { ...params, senderId }) diff --git a/test/integration/transaction.ts b/test/integration/transaction.ts index 92a9ba929a..ee632af4ed 100644 --- a/test/integration/transaction.ts +++ b/test/integration/transaction.ts @@ -20,7 +20,7 @@ import { expect } from 'chai' // @ts-expect-error import { BaseAe, spendPromise, publicKey } from './index' import { commitmentHash, oracleQueryId, decode, encode } from '../../src/tx/builder/helpers' -import { GAS_MAX, TX_TYPE } from '../../src/tx/builder/schema' +import { GAS_MAX, ORACLE_TTL_TYPES, TX_TYPE } from '../../src/tx/builder/schema' import { AE_AMOUNT_FORMATS } from '../../src/utils/amount-formatter' import { EncodedData } from './../../src/utils/encoder' @@ -39,9 +39,9 @@ const pointers = [{ key: 'account_pubkey', id: senderId }] const queryFormat = '{\'city\': str}' const responseFormat = '{\'tmp\': num}' const queryFee = 30000 -const oracleTtl = { type: 'delta', value: 500 } -const responseTtl = { type: 'delta', value: 100 } -const queryTtl = { type: 'delta', value: 100 } +const oracleTtl = { oracleTtlType: ORACLE_TTL_TYPES.delta, oracleTtlValue: 500 } +const responseTtl = { responseTtlType: ORACLE_TTL_TYPES.delta, responseTtlValue: 100 } +const queryTtl = { queryTtlType: ORACLE_TTL_TYPES.delta, queryTtlValue: 100 } const query = '{\'city\': \'Berlin\'}' const queryResponse = '{\'tmp\': 101}' @@ -133,17 +133,17 @@ describe('Transaction', function () { 'oracle register', 'tx_+FAWAaEB1c8IQA6YgiLybrSwLI+JB3RXRnIRpubZVe23B0nGozsBjXsnY2l0eSc6IHN0cn2Meyd0bXAnOiBudW19gnUwAIIB9IYPN7jqmAAAAGsRIcw=', () => aeSdk.buildTx(TX_TYPE.oracleRegister, { - nonce, accountId: address, queryFormat, responseFormat, queryFee, oracleTtl + nonce, accountId: address, queryFormat, responseFormat, queryFee, ...oracleTtl }) ], [ 'oracle extend', 'tx_8RkBoQTVzwhADpiCIvJutLAsj4kHdFdGchGm5tlV7bcHScajOwEAggH0hg6itfGYAADwE/X7', - () => aeSdk.buildTx(TX_TYPE.oracleExtend, { nonce, oracleId, callerId: address, oracleTtl }) + () => aeSdk.buildTx(TX_TYPE.oracleExtend, { nonce, oracleId, callerId: address, ...oracleTtl }) ], [ 'oracle post query', 'tx_+GkXAaEB1c8IQA6YgiLybrSwLI+JB3RXRnIRpubZVe23B0nGozsBoQTVzwhADpiCIvJutLAsj4kHdFdGchGm5tlV7bcHScajO5J7J2NpdHknOiAnQmVybGluJ32CdTAAZABkhg+bJBmGAAAtn7nr', () => aeSdk.buildTx(TX_TYPE.oracleQuery, { - nonce, oracleId, responseTtl, query, queryTtl, queryFee, senderId: address + nonce, oracleId, ...responseTtl, query, ...queryTtl, queryFee, senderId: address }) ], [ 'oracle respond query', @@ -152,7 +152,7 @@ describe('Transaction', function () { nonce, oracleId, callerId: address, - responseTtl, + ...responseTtl, queryId: oracleQueryId(address, nonce, oracleId), response: queryResponse })