From a48444a587162dbd60a99391c7b955b54b899bb1 Mon Sep 17 00:00:00 2001 From: Anthony Law Date: Fri, 11 Nov 2022 10:39:55 +0800 Subject: [PATCH 1/7] [explorer] fix: docker build image issue #1143 --- Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index a5ef6b7fb..74fe97322 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,6 @@ FROM node:lts-alpine AS builder +RUN apk add --no-cache python3 make g++ +ENV NODE_OPTIONS="--dns-result-order=ipv4first --openssl-legacy-provider" WORKDIR /app COPY . . RUN npm install && npm run build From d95d7f0cc35a3ac49401b1ef6f5783cc77664c06 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 4 Dec 2022 06:17:20 +0000 Subject: [PATCH 2/7] build(deps): bump decode-uri-component from 0.2.0 to 0.2.2 Bumps [decode-uri-component](https://github.com/SamVerschueren/decode-uri-component) from 0.2.0 to 0.2.2. - [Release notes](https://github.com/SamVerschueren/decode-uri-component/releases) - [Commits](https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.0...v0.2.2) --- updated-dependencies: - dependency-name: decode-uri-component dependency-type: indirect ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 989786eeb..3f41f6ec3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10934,9 +10934,9 @@ } }, "node_modules/decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", "engines": { "node": ">=0.10" } @@ -38971,9 +38971,9 @@ } }, "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==" + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==" }, "dedent": { "version": "0.7.0", From 133ff8f727b6d663c8a7abca32ed993a795d4ab0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Feb 2023 04:30:33 +0000 Subject: [PATCH 3/7] build(deps): bump @sideway/formula from 3.0.0 to 3.0.1 Bumps [@sideway/formula](https://github.com/sideway/formula) from 3.0.0 to 3.0.1. - [Release notes](https://github.com/sideway/formula/releases) - [Commits](https://github.com/sideway/formula/compare/v3.0.0...v3.0.1) --- updated-dependencies: - dependency-name: "@sideway/formula" dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3f41f6ec3..e09dd3102 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2956,9 +2956,9 @@ } }, "node_modules/@sideway/formula": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", - "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", "dev": true }, "node_modules/@sideway/pinpoint": { @@ -32493,9 +32493,9 @@ } }, "@sideway/formula": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", - "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", "dev": true }, "@sideway/pinpoint": { From 1d2d6fef769c6f540afcc347f3856331e6514326 Mon Sep 17 00:00:00 2001 From: Anthony Law Date: Wed, 15 Feb 2023 03:10:20 +0800 Subject: [PATCH 4/7] [explorer] fix: search namespace without dash #1152 * [explorer] fix: search namespace without dash * [explorer] task: refactor UI store unit test * [explorer] task: patch update on UI store --- __tests__/store/ui.spec.js | 106 +++++++++++++++++++++++++++---------- src/store/ui.js | 2 +- 2 files changed, 79 insertions(+), 29 deletions(-) diff --git a/__tests__/store/ui.spec.js b/__tests__/store/ui.spec.js index 5569e5444..685712616 100644 --- a/__tests__/store/ui.spec.js +++ b/__tests__/store/ui.spec.js @@ -1,10 +1,9 @@ -import Helper from '../../src/helper'; import { AccountService, MosaicService, NamespaceService } from '../../src/infrastructure'; import ui from '../../src/store/ui'; import { stub, restore } from 'sinon'; describe('store/ui', () => { - describe('action search should', () => { + describe('action search', () => { let dispatch; let rootGetters; @@ -15,7 +14,22 @@ describe('store/ui', () => { afterEach(restore); - it('return block page with height', async () => { + const assertSearchNamespace = async namespaceName => { + // Arrange: + stub(NamespaceService, 'getNamespaceInfo').returns(Promise.resolve({})); + + // Act: + await ui.actions.search({ dispatch, rootGetters }, namespaceName); + + // Assert: + expect(dispatch).toHaveBeenCalledTimes(1); + expect(dispatch).toHaveBeenNthCalledWith(1, 'openPage', { + pageName: 'namespace', + param: namespaceName + }); + }; + + it('returns block page with height', async () => { // Arrange: const height = '10'; @@ -30,12 +44,11 @@ describe('store/ui', () => { }); }); - it('return account page with public key', async () => { + it('returns account page with public key', async () => { // Arrange: const publicKey = 'DC20B243B63246C9E75E4FB5ED236513A005454393E93C8A4CE6EDEE323C2DDB'; - const getAccountInfoStub = stub(AccountService, 'getAccountInfo'); - getAccountInfoStub.returns(Promise.resolve({})); + stub(AccountService, 'getAccountInfo').returns(Promise.resolve({})); // Act: await ui.actions.search({ dispatch, rootGetters }, publicKey); @@ -48,7 +61,7 @@ describe('store/ui', () => { }); }); - it('return account page with address', async () => { + it('returns account page with address', async () => { // Arrange: const address = 'TAR5OQBKR4KSVRVZ3ZBVHNLMBZ4N4Q27WFVMJDI'; @@ -63,7 +76,7 @@ describe('store/ui', () => { }); }); - it('return transaction page with hash', async () => { + it('returns transaction page with hash', async () => { // Arrange: const hash = '706BBC8F95AF60B22CB38911A645D3BA24DC480FDBE18C197ACCFE0FDE0DC24D'; @@ -78,12 +91,11 @@ describe('store/ui', () => { }); }); - it('return mosaic page with mosaic Id', async () => { + it('returns mosaic page with mosaic Id', async () => { // Arrange: const mosaicId = '3A8416DB2D53B6C8'; - const getMosaicInfoStub = stub(MosaicService, 'getMosaicInfo'); - getMosaicInfoStub.returns(Promise.resolve({})); + stub(MosaicService, 'getMosaicInfo').returns(Promise.resolve({})); // Act: await ui.actions.search({ dispatch, rootGetters }, mosaicId); @@ -96,23 +108,9 @@ describe('store/ui', () => { }); }); - it('return namespace page with namespace name', async () => { - // Arrange: - const namespaceName = 'symbol.xym'; + it('returns namespace page with namespace name', async () => await assertSearchNamespace('symbol.xym')); - const getNamespaceInfoStub = stub(NamespaceService, 'getNamespaceInfo'); - getNamespaceInfoStub.returns(Promise.resolve({})); - - // Act: - await ui.actions.search({ dispatch, rootGetters }, namespaceName); - - // Assert: - expect(dispatch).toHaveBeenCalledTimes(1); - expect(dispatch).toHaveBeenNthCalledWith(1, 'openPage', { - pageName: 'namespace', - param: namespaceName - }); - }); + it('returns namespace page with namespace name contain dash', async () => await assertSearchNamespace('symbol-')); it('throw error given Nem address', async () => { // Arrange: @@ -131,6 +129,58 @@ describe('store/ui', () => { return expect(ui.actions.search({ dispatch, rootGetters }, randomText)) .rejects.toThrow('errorNothingFound'); }); - }); + describe('input value contain whitespace', () => { + const runBasicWhitespaceTests = (pageName, inputValue, expectedResult) => { + it(`returns ${pageName} page`, async () => { + // Arrange: + if ('mosaic' === pageName) + stub(MosaicService, 'getMosaicInfo').returns(Promise.resolve({})); + else if('account' === pageName) + stub(AccountService, 'getAccountInfo').returns(Promise.resolve({})); + else if ('namespace' === pageName) + stub(NamespaceService, 'getNamespaceInfo').returns(Promise.resolve({})); + + // Act: + await ui.actions.search({ dispatch, rootGetters }, inputValue); + + // Assert: + expect(dispatch).toHaveBeenNthCalledWith(1, 'openPage', expectedResult); + }); + }; + + // Arrange: + const inputValues = [ + { + pageName: 'block', + input: ' 10 ' + }, + { + pageName: 'account', + input: ' DC20B243B63246C9E75E4FB5ED236513A005454393E93C8A4CE6EDEE323C2DDB ' + }, + { + pageName: 'account', + input: ' TAR5OQBKR4KSVRVZ3ZBVHNLMBZ4N4Q27WFVMJDI ' + }, + { + pageName: 'transaction', + input: ' 706BBC8F95AF60B22CB38911A645D3BA24DC480FDBE18C197ACCFE0FDE0DC24D' + }, + { + pageName: 'mosaic', + input: '3A8416DB2D53B6C8 ' + }, + { + pageName: 'namespace', + input: 'symbol . xym ' + } + ]; + + inputValues.forEach(({input, pageName}) => runBasicWhitespaceTests(pageName, input, { + pageName, + param: input.replace(/\s/g, '') + })); + }); + }); }); diff --git a/src/store/ui.js b/src/store/ui.js index be18bb7a0..93baa0f33 100644 --- a/src/store/ui.js +++ b/src/store/ui.js @@ -103,7 +103,7 @@ export default { search: ({ dispatch, rootGetters }, _searchString) => { return new Promise(async (resolve, reject) => { if (null !== _searchString && '' !== _searchString) { - const searchString = _searchString.replace(/\s|-/g, ''); + const searchString = _searchString.replace(/\s/g, ''); if (helper.isBlockHeight(searchString)) { dispatch('openPage', { pageName: 'block', From 5d6da2c428871dba608e4c246213953a9fdf997d Mon Sep 17 00:00:00 2001 From: Anthony Law Date: Tue, 21 Feb 2023 01:41:35 +0800 Subject: [PATCH 5/7] [explorer] fix: lint issue spaces and tabs #1166 * [explorer] fix: add eslint rules * [explorer] fix: lint issue from space to tab --- .editorconfig | 6 +- .eslintrc | 3 + src/helper.js | 282 ++-- src/infrastructure/AccountService.js | 256 ++-- src/infrastructure/AnnouncerService.js | 231 ++-- src/infrastructure/BlockService.js | 688 +++++----- src/infrastructure/ChainService.js | 35 +- src/infrastructure/CreateTransaction.js | 866 ++++++------ src/infrastructure/DataService.js | 80 +- src/infrastructure/FinalizationService.js | 29 +- src/infrastructure/ListenerService.js | 41 +- src/infrastructure/LockService.js | 131 +- src/infrastructure/MetadataService.js | 77 +- src/infrastructure/MosaicService.js | 580 ++++---- src/infrastructure/MultisigService.js | 95 +- src/infrastructure/NamespaceService.js | 896 +++++++------ src/infrastructure/NetworkService.js | 105 +- src/infrastructure/NodeService.js | 428 +++--- src/infrastructure/ReceiptExtractor.js | 176 +-- src/infrastructure/ReceiptService.js | 383 +++--- src/infrastructure/RestrictionService.js | 356 ++--- src/infrastructure/TransactionService.js | 1481 +++++++++++---------- src/infrastructure/http.js | 306 +++-- src/store/account.js | 107 +- src/store/namespace.js | 60 +- 25 files changed, 4153 insertions(+), 3545 deletions(-) diff --git a/.editorconfig b/.editorconfig index 7053c49a0..cfc009c2f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,5 +1,7 @@ [*.{js,jsx,ts,tsx,vue}] -indent_style = space -indent_size = 2 +indent_style = tab +indent_size = 4 trim_trailing_whitespace = true insert_final_newline = true +quote_type = single +max_line_length = 140 diff --git a/.eslintrc b/.eslintrc index 433d82995..1f65c5caf 100644 --- a/.eslintrc +++ b/.eslintrc @@ -18,3 +18,6 @@ rules: - tab - ignoredNodes: - TemplateLiteral + no-mixed-spaces-and-tabs: + - error + diff --git a/src/helper.js b/src/helper.js index 12973498a..5046a84d1 100644 --- a/src/helper.js +++ b/src/helper.js @@ -17,7 +17,11 @@ */ import { Constants } from './config'; -import { NamespaceService, MosaicService, ReceiptService } from './infrastructure'; +import { + NamespaceService, + MosaicService, + ReceiptService +} from './infrastructure'; import http from './infrastructure/http'; import moment from 'moment'; import { @@ -41,7 +45,7 @@ const getNetworkTypeAddressFormat = { }; class helper { - static timeSince (interval) { + static timeSince(interval) { if (1 < interval.years) return interval.years + ' years'; else if (1 === interval.years) @@ -55,13 +59,13 @@ class helper { else if (1 === interval.hours) return interval.hours + ' hour'; else if (1 < interval.minutes) - return interval.minutes + ' min';// ' minutes' + return interval.minutes + ' min'; // ' minutes' else if (1 === interval.minutes) - return interval.minutes + ' min';// ' minute' + return interval.minutes + ' min'; // ' minute' else if (1 !== interval.seconds) - return interval.seconds + ' sec';// ' seconds' + return interval.seconds + ' sec'; // ' seconds' else - return interval.seconds + ' sec';// ' second' + return interval.seconds + ' sec'; // ' second' } static formatSeconds = _second => { @@ -100,37 +104,35 @@ class helper { result = `${d} d ${result}`; return result; - } + }; static isMosaicOrNamespaceId = str => 16 === str.length && /^[0-9a-fA-F]+$/.test(str); static isAccountPublicKey = str => - 64 === str.length && - str.match('^[A-z0-9]+$') + 64 === str.length && str.match('^[A-z0-9]+$'); static isAccountAddress = str => 39 === str.length && str.match(`[${getNetworkTypeAddressFormat[http.networkType]}]` + - '{1,1}[a-zA-Z0-9]{5,5}[a-zA-Z0-9]{6,6}[a-zA-Z0-9]{6,6}[a-zA-Z0-9]{6,6}[a-zA-Z0-9]{6,6}[a-zA-Z0-9]{6,6}[a-zA-Z0-9]{3,3}') + '{1,1}[a-zA-Z0-9]{5,5}[a-zA-Z0-9]{6,6}[a-zA-Z0-9]{6,6}[a-zA-Z0-9]{6,6}[a-zA-Z0-9]{6,6}[a-zA-Z0-9]{6,6}[a-zA-Z0-9]{3,3}'); - static isBlockHeight = str => - str.match(/^-{0,1}\d+$/) + static isBlockHeight = str => str.match(/^-{0,1}\d+$/); - static validURL (url) { + static validURL(url) { // All we expect is there is a valid origin for the url, IE, // the origin is not 'null'. return 'null' !== url.origin; } - static parseUrl (str) { + static parseUrl(str) { let url = new Url(str); if (this.validURL(url)) return url; } - static httpToWssUrl (str) { + static httpToWssUrl(str) { let url = new Url(str); if (this.validURL(url)) { @@ -139,7 +141,7 @@ class helper { } } - static async logError (dispatch, action, ...args) { + static async logError(dispatch, action, ...args) { try { await dispatch(action, ...args); } catch (e) { @@ -147,28 +149,35 @@ class helper { } } - static convertToSecond = durationInBlocks => durationInBlocks * http.networkConfig.TargetBlockTime + static convertToSecond = durationInBlocks => + durationInBlocks * http.networkConfig.TargetBlockTime; static calculateNamespaceExpiration = (currentHeight, endHeight) => { - const expired = currentHeight > endHeight - http.networkConfig.NamespaceGraceDuration; - const expiredInBlock = endHeight - http.networkConfig.NamespaceGraceDuration - currentHeight; + const expired = + currentHeight > endHeight - http.networkConfig.NamespaceGraceDuration; + const expiredInBlock = + endHeight - http.networkConfig.NamespaceGraceDuration - currentHeight; return { isExpired: expired, expiredInBlock: expiredInBlock, expiredInSecond: this.convertToSecond(expiredInBlock) }; - } + }; static fetchData = async (fetchFunction, commit, before, error, success) => { - if ('function' === typeof before) { await before(); } else { + if ('function' === typeof before) { + await before(); + } else { commit('setLoading', true); commit('setError', false); } try { await fetchFunction(); } catch (e) { - if ('function' === typeof error) { await error(e); } else { + if ('function' === typeof error) { + await error(e); + } else { console.error(e); commit('setError', true); } @@ -177,7 +186,7 @@ class helper { await success(); else commit('setLoading', false); - } + }; /** * Convert hex value or namespace name to mosaicId or namespaceId. @@ -190,20 +199,20 @@ class helper { const isHexadecimal = this.isMosaicOrNamespaceId(hexOrNamespace); - if (isHexadecimal){ - Id = 'mosaic' === toId - ? new MosaicId(hexOrNamespace) - : NamespaceId.createFromEncoded(hexOrNamespace); - } - else - { - Id = 'mosaic' === toId - ? await NamespaceService.getLinkedMosaicId(new NamespaceId(hexOrNamespace)) - : new NamespaceId(hexOrNamespace); + if (isHexadecimal) { + Id = + 'mosaic' === toId + ? new MosaicId(hexOrNamespace) + : NamespaceId.createFromEncoded(hexOrNamespace); + } else { + Id = + 'mosaic' === toId + ? await NamespaceService.getLinkedMosaicId(new NamespaceId(hexOrNamespace)) + : new NamespaceId(hexOrNamespace); } return Id; - } + }; /** * Decode Account Public key or Namespace name to plan Address. @@ -225,7 +234,7 @@ class helper { } return address; - } + }; /** * Convert Mosaic amount to relative Amount with divisibility. @@ -239,8 +248,10 @@ class helper { let relativeAmount = amount / Math.pow(10, divisibility); - return relativeAmount.toLocaleString('en-US', { minimumFractionDigits: divisibility }); - } + return relativeAmount.toLocaleString('en-US', { + minimumFractionDigits: divisibility + }); + }; /** * Get network currency balance. @@ -248,14 +259,20 @@ class helper { * @returns {string} network currency balance. */ static getNetworkCurrencyBalance = mosaics => { - const networkCurrencyMosaic = mosaics.filter(mosaic => mosaic.id.toHex() === http.networkCurrency.mosaicId || - (mosaic.id instanceof NamespaceId && - mosaic.id.toHex() === http.networkCurrency.namespaceId)); + const networkCurrencyMosaic = mosaics.filter(mosaic => + mosaic.id.toHex() === http.networkCurrency.mosaicId || + (mosaic.id instanceof NamespaceId && + mosaic.id.toHex() === http.networkCurrency.namespaceId)); - const totalNetworkCurrencyAmount = networkCurrencyMosaic.reduce((acc, cur) => acc + Number(cur.amount.toString()), 0); + const totalNetworkCurrencyAmount = networkCurrencyMosaic.reduce( + (acc, cur) => acc + Number(cur.amount.toString()), + 0 + ); - return 0 < networkCurrencyMosaic.length ? this.toNetworkCurrency(totalNetworkCurrencyAmount) : Constants.Message.UNAVAILABLE; - } + return 0 < networkCurrencyMosaic.length + ? this.toNetworkCurrency(totalNetworkCurrencyAmount) + : Constants.Message.UNAVAILABLE; + }; /** * Convert networkTimestamp to date. @@ -278,14 +295,15 @@ class helper { } return date.format('YYYY-MM-DD HH:mm:ss'); - } + }; /** * convert difficulty raw score to readable * @param {UInt64} difficulty - raw difficulty score * @returns {string} difficulty - readable difficulty score */ - static convertBlockDifficultyToReadable = difficulty => (difficulty.compact() / 1000000000000).toFixed(2).toString() + static convertBlockDifficultyToReadable = difficulty => + (difficulty.compact() / 1000000000000).toFixed(2).toString(); /** * Format Importance score to percentage. @@ -302,7 +320,7 @@ class helper { percent = rawScore / totalChainImportance; return (percent * 100).toFixed(divisibility).toString() + ' %'; - } + }; /** * Format number to Network currency divisibility. @@ -311,22 +329,26 @@ class helper { * @returns {string} amount - (string) with formatted divisibility */ static toNetworkCurrency = amount => - (amount / Math.pow(10, http.networkCurrency.divisibility)) - .toLocaleString('en-US', { minimumFractionDigits: http.networkCurrency.divisibility }) + (amount / Math.pow(10, http.networkCurrency.divisibility)).toLocaleString( + 'en-US', + { minimumFractionDigits: http.networkCurrency.divisibility } + ); /** * Convert public key to Address. * @param {string} publicKey - raw public key * @returns {string} address - address in plain format */ - static publicKeyToAddress = publicKey => Address.createFromPublicKey(publicKey, http.networkType).plain() + static publicKeyToAddress = publicKey => + Address.createFromPublicKey(publicKey, http.networkType).plain(); /** * convert network timestamp to world time * @param {number} timestamp - raw timestamp * @returns {number} timestamp - world timestamp */ - static networkTimestamp = timestamp => Math.round(timestamp / 1000) + http.networkConfig.NemsisTimestamp + static networkTimestamp = timestamp => + Math.round(timestamp / 1000) + http.networkConfig.NemsisTimestamp; /** * Sort Native mosaic to top of list. @@ -342,23 +364,23 @@ class helper { : sortedMosaics.push(mosaic)); return sortedMosaics; - } + }; /** * Convert second to time from now in second. * @param {number} second number of second. * @returns {string} time from now in second. */ - static convertTimeFromNowInSec = second => moment.utc().add(second, 's') - .fromNow() + static convertTimeFromNowInSec = second => + moment.utc().add(second, 's').fromNow(); /** * convert second to Date. * @param {number} second number of second. * @returns {string} YYYY.MM.DD HH:mm UTC. */ - static convertSecondToDate = second => moment.utc().add(second, 's') - .format('YYYY.MM.DD @ HH:mm UTC') + static convertSecondToDate = second => + moment.utc().add(second, 's').format('YYYY.MM.DD @ HH:mm UTC'); /** * Converts an HSL color value to RGB. Conversion formula @@ -371,11 +393,13 @@ class helper { * @param {number} l The lightness * @returns {object} {R: Number, G: Number, B: Number} */ - static hslToRgb (h, s, l) { + static hslToRgb(h, s, l) { let r, g, b; - if (0 === s) { r = g = b = l; } // achromatic - else { + if (0 === s) { + r = g = b = l; + } // achromatic + else { /* eslint-disable no-param-reassign */ const hue2rgb = (_p, _q, _t) => { if (0 > _t) @@ -440,23 +464,56 @@ class helper { totalValue += parseInt(hex, 16); } else { const charset = [ - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i', + 'j', + 'k', + 'l', + 'm', + 'n', + 'o', + 'p', + 'q', + 'r', + 's', + 't', + 'u', + 'v', + 'w', + 'x', + 'y', + 'z' ]; for (const char of hash) totalValue += charset.indexOf(char.toLowerCase()); - }; + } const k = Math.trunc(totalValue / spread); const offsetValue = totalValue - (spread * k); const hue = offsetValue / 100; return this.hslToRgb(hue, saturation, lightness); - } + }; - static truncString (str, strLen = 4) { + static truncString(str, strLen = 4) { if ('string' === typeof str) { if (str.length > (strLen * 2) + 1) return `${str.substring(0, strLen)}...${str.substring(str.length - strLen, str.length)}`; @@ -490,16 +547,17 @@ class helper { const namespaceHex = unResolvedAddress.id.toHex(); - const addressResolutionStatements = await ReceiptService.searchAddressResolutionStatements(searchCriteria); + const addressResolutionStatements = + await ReceiptService.searchAddressResolutionStatements(searchCriteria); - const address = addressResolutionStatements.data.find(item => item.unresolved === namespaceHex - && 'Address' === item.resolutionType)?.addressResolutionEntries[0]; + const address = addressResolutionStatements.data.find(item => + item.unresolved === namespaceHex && 'Address' === item.resolutionType)?.addressResolutionEntries[0]; if (!address) throw new Error('Failed to resolved address'); return address; - } + }; /** * To resolved unresolvedMosaicId. @@ -514,7 +572,7 @@ class helper { return new MosaicId(http.networkCurrency.mosaicId); return await NamespaceService.getLinkedMosaicId(unresolvedMosaicId); - } + }; /** * Build mosaic field object use in MosaicField components. @@ -523,16 +581,25 @@ class helper { * @param {array} mosaicNames - mosaics namespace name. * @returns {object} { mosaicId, amount, mosaicAliasName } */ - static mosaicsFieldObjectBuilder = (resolvedMosaics, mosaicInfos, mosaicNames) => { + static mosaicsFieldObjectBuilder = ( + resolvedMosaics, + mosaicInfos, + mosaicNames + ) => { if (0 === resolvedMosaics.length) return []; - const uniqueMosaicIds = [...new Set(resolvedMosaics.map(mosaic => mosaic.id.toHex()))]; + const uniqueMosaicIds = [ + ...new Set(resolvedMosaics.map(mosaic => mosaic.id.toHex())) + ]; return uniqueMosaicIds.map(idHex => { const mosaics = resolvedMosaics.filter(mosaic => mosaic.id.toHex() === idHex); - const sumAmount = mosaics.reduce((acc, cur) => acc + BigInt(cur.amount.toString()), BigInt(0)); + const sumAmount = mosaics.reduce( + (acc, cur) => acc + BigInt(cur.amount.toString()), + BigInt(0) + ); const mosaicField = { rawAmount: UInt64.fromNumericString(sumAmount.toString()), @@ -542,7 +609,10 @@ class helper { if (idHex === http.networkCurrency.mosaicId) { return { ...mosaicField, - amount: this.formatMosaicAmountWithDivisibility(Number(sumAmount), http.networkCurrency.divisibility), + amount: this.formatMosaicAmountWithDivisibility( + Number(sumAmount), + http.networkCurrency.divisibility + ), mosaicAliasName: http.networkCurrency.namespaceName }; } else { @@ -550,12 +620,18 @@ class helper { return { ...mosaicField, - amount: this.formatMosaicAmountWithDivisibility(Number(sumAmount), divisibility), - mosaicAliasName: MosaicService.extractMosaicNamespace({ mosaicId: mosaics[0].id.toHex() }, mosaicNames)[0] + amount: this.formatMosaicAmountWithDivisibility( + Number(sumAmount), + divisibility + ), + mosaicAliasName: MosaicService.extractMosaicNamespace( + { mosaicId: mosaics[0].id.toHex() }, + mosaicNames + )[0] }; } }); - } + }; /** * Check native namespace. @@ -569,7 +645,7 @@ class helper { const values = http.nativeNamespaces.map(namespace => namespace.namespaceName); return -1 !== values.indexOf(namespaceName); - } + }; /** * Gets single mosaic alias name. @@ -578,10 +654,13 @@ class helper { */ static getMosaicAliasNames = async mosaicId => { const getMosaicNames = await NamespaceService.getMosaicsNames([mosaicId]); - const mosaicAliasNames = MosaicService.extractMosaicNamespace({ mosaicId: mosaicId.toHex() }, getMosaicNames); + const mosaicAliasNames = MosaicService.extractMosaicNamespace( + { mosaicId: mosaicId.toHex() }, + getMosaicNames + ); return mosaicAliasNames; - } + }; static fallbackCopyTextToClipboard = text => { let textArea = document.createElement('textarea'); @@ -607,7 +686,7 @@ class helper { document.body.removeChild(textArea); return success; - } + }; static copyTextToClipboard = text => { return new Promise((resolve, reject) => { @@ -617,14 +696,17 @@ class helper { else reject(Error('Could not copy text. document.execCommand() failed')); } - navigator.clipboard.writeText(text).then(function () { - resolve(); - }, function (err) { - console.error('Async: Could not copy text: ', err); - reject(Error('Async: Could not copy text: ', err)); - }); + navigator.clipboard.writeText(text).then( + function () { + resolve(); + }, + function (err) { + console.error('Async: Could not copy text: ', err); + reject(Error('Async: Could not copy text: ', err)); + } + ); }); - } + }; static formatNodeVersion = rawNodeVersion => { try { @@ -632,19 +714,19 @@ class helper { } catch (e) { return Constants.Message.UNAVAILABLE; } - } + }; - static getMosaicName (mosaic) { + static getMosaicName(mosaic) { let mosaicAliasName; if (Array.isArray(mosaic.mosaicAliasName)) - mosaicAliasName = mosaic.mosaicAliasName.length ? mosaic.mosaicAliasName[0] : 'N/A'; + {mosaicAliasName = mosaic.mosaicAliasName.length + ? mosaic.mosaicAliasName[0] + : 'N/A';} else - mosaicAliasName = mosaic.mosaicAliasName ? mosaic.mosaicAliasName : 'N/A'; + {mosaicAliasName = mosaic.mosaicAliasName ? mosaic.mosaicAliasName : 'N/A';} - return 'N/A' !== mosaicAliasName - ? mosaicAliasName - : mosaic.mosaicId; + return 'N/A' !== mosaicAliasName ? mosaicAliasName : mosaic.mosaicId; } /** @@ -652,7 +734,7 @@ class helper { * @param {array} dataset - list of data. * @returns {string} csv data in string format. */ - static convertArrayToCSV (dataset) { + static convertArrayToCSV(dataset) { if (!Array.isArray(dataset)) throw Error('Convert dataset to CSV fail.'); @@ -687,7 +769,7 @@ class helper { */ static getStartListIndex = (pageNumber, pageSize) => { return 1 === pageNumber ? 0 : (pageNumber - 1) * pageSize; - } + }; static getTransactionMosaicInfoAndNamespace = async transactions => { const unresolvedMosaics = []; @@ -707,7 +789,7 @@ class helper { }); return await helper.getMosaicInfoAndNamespace(unresolvedMosaics); - } + }; static getMosaicInfoAndNamespace = async unresolvedMosaics => { const unresolvedMosaicsMap = {}; @@ -727,7 +809,9 @@ class helper { .filter(mosaicId => mosaicId.toHex() !== http.networkCurrency.mosaicId); // filter duplicated mosaic id - const uniqueMosaicIds = [...new Set(resolvedMosaicIds.map(mosaic => mosaic.toHex()))]; + const uniqueMosaicIds = [ + ...new Set(resolvedMosaicIds.map(mosaic => mosaic.toHex())) + ]; let mosaicInfos = []; let mosaicNames = []; @@ -745,7 +829,7 @@ class helper { mosaicNames, unresolvedMosaicsMap }; - } + }; } export default helper; diff --git a/src/infrastructure/AccountService.js b/src/infrastructure/AccountService.js index 4fa845ea3..860a64041 100644 --- a/src/infrastructure/AccountService.js +++ b/src/infrastructure/AccountService.js @@ -48,14 +48,15 @@ class AccountService { * @returns {object} Formatted AccountInfo */ static getAccount = async address => { - const account = await http.createRepositoryFactory.createAccountRepository() + const account = await http.createRepositoryFactory + .createAccountRepository() .getAccountInfo(Address.createFromRawAddress(address)) .toPromise(); const formattedAccount = this.formatAccountInfo(account); return formattedAccount; - } + }; /** * Gets an AccountInfo for an account. @@ -63,12 +64,13 @@ class AccountService { * @returns {array} Formatted AccountInfos. */ static getAccounts = async addresses => { - const accounts = await http.createRepositoryFactory.createAccountRepository() + const accounts = await http.createRepositoryFactory + .createAccountRepository() .getAccountsInfo(addresses.map(a => Address.createFromRawAddress(a))) .toPromise(); return accounts.map(a => this.formatAccountInfo(a)); - } + }; /** * Gets a accounts list from searchCriteria. @@ -76,7 +78,8 @@ class AccountService { * @returns {object} formatted account data with pagination info. */ static searchAccounts = async accountSearchCriteria => { - const searchAccounts = await http.createRepositoryFactory.createAccountRepository() + const searchAccounts = await http.createRepositoryFactory + .createAccountRepository() .search(accountSearchCriteria) .toPromise(); @@ -84,7 +87,7 @@ class AccountService { ...searchAccounts, data: searchAccounts.data.map(account => this.formatAccountInfo(account)) }; - } + }; /** * Get custom Account list dataset into Vue Component @@ -110,7 +113,8 @@ class AccountService { const accountInfos = await this.searchAccounts(searchCriteria); - const addresses = accountInfos.data.map(accountInfo => Address.createFromRawAddress(accountInfo.address)); + const addresses = accountInfos.data.map(accountInfo => + Address.createFromRawAddress(accountInfo.address)); const accountNames = await NamespaceService.getAccountsNames(addresses); @@ -121,14 +125,16 @@ class AccountService { totalRecords: numAccounts, data: accountInfos.data.map(account => ({ ...account, - balance: helper.getNetworkCurrencyBalance(account.mosaics) !== Constants.Message.UNAVAILABLE - ? helper.getNetworkCurrencyBalance(account.mosaics) - : helper.toNetworkCurrency(0), + balance: + helper.getNetworkCurrencyBalance(account.mosaics) !== + Constants.Message.UNAVAILABLE + ? helper.getNetworkCurrencyBalance(account.mosaics) + : helper.toNetworkCurrency(0), accountAliasNames: this.extractAccountNamespace(account, accountNames), accountLabel: http.accountLabels[account.address] })) }; - } + }; /** * Get custom Account info dataset into Vue Component. @@ -136,21 +142,28 @@ class AccountService { * @returns {object} Custom AccountInfo. */ static getAccountInfo = async address => { - const [ { supplementalPublicKeys, ...accountInfo }, accountNames, { latestFinalizedBlock } ] = await Promise.all([ + const [ + { supplementalPublicKeys, ...accountInfo }, + accountNames, + { latestFinalizedBlock } + ] = await Promise.all([ this.getAccount(address), - NamespaceService.getAccountsNames([Address.createFromRawAddress(address)]), + NamespaceService.getAccountsNames([ + Address.createFromRawAddress(address) + ]), ChainService.getChainInfo() ]); const getVotingEpochStatus = (startEpoch, endEpoch) => { let votingStatus = ''; - if (latestFinalizedBlock.finalizationEpoch >= startEpoch && latestFinalizedBlock.finalizationEpoch <= endEpoch) + if ( + latestFinalizedBlock.finalizationEpoch >= startEpoch && + latestFinalizedBlock.finalizationEpoch <= endEpoch + ) votingStatus = Constants.EpochStatus.CURRENT; - else if (latestFinalizedBlock.finalizationEpoch < startEpoch) votingStatus = Constants.EpochStatus.FUTURE; - else if (latestFinalizedBlock.finalizationEpoch > endEpoch) votingStatus = Constants.EpochStatus.EXPIRED; @@ -167,39 +180,54 @@ class AccountService { })), supplementalPublicKeys: { ...supplementalPublicKeys, - linkedAddress: supplementalPublicKeys.linked === Constants.Message.UNAVAILABLE - ? supplementalPublicKeys.linked - : helper.publicKeyToAddress(supplementalPublicKeys.linked), - nodeAddress: supplementalPublicKeys.node === Constants.Message.UNAVAILABLE - ? supplementalPublicKeys.node - : helper.publicKeyToAddress(supplementalPublicKeys.node), - vrfAddress: supplementalPublicKeys.vrf === Constants.Message.UNAVAILABLE - ? supplementalPublicKeys.vrf - : helper.publicKeyToAddress(supplementalPublicKeys.vrf) + linkedAddress: + supplementalPublicKeys.linked === Constants.Message.UNAVAILABLE + ? supplementalPublicKeys.linked + : helper.publicKeyToAddress(supplementalPublicKeys.linked), + nodeAddress: + supplementalPublicKeys.node === Constants.Message.UNAVAILABLE + ? supplementalPublicKeys.node + : helper.publicKeyToAddress(supplementalPublicKeys.node), + vrfAddress: + supplementalPublicKeys.vrf === Constants.Message.UNAVAILABLE + ? supplementalPublicKeys.vrf + : helper.publicKeyToAddress(supplementalPublicKeys.vrf) }, votingList: - 0 < supplementalPublicKeys.voting.length ? supplementalPublicKeys.voting.map(voting => ({ - ...voting, - epochInfo: { - epochStart: voting.startEpoch, - epochEnd: voting.endEpoch, - epochStatus: getVotingEpochStatus(voting.startEpoch, voting.endEpoch) - }, - publicKey: voting.publicKey - - })).sort((a, b) => { - const orderStatus = { - [Constants.EpochStatus.CURRENT]: 1, - [Constants.EpochStatus.FUTURE]: 2, - [Constants.EpochStatus.EXPIRED]: 3 - }; - - return orderStatus[a.epochInfo.epochStatus] - orderStatus[b.epochInfo.epochStatus]; - }) : [], - accountAliasNames: this.extractAccountNamespace(accountInfo, accountNames), + 0 < supplementalPublicKeys.voting.length + ? supplementalPublicKeys.voting + .map(voting => ({ + ...voting, + epochInfo: { + epochStart: voting.startEpoch, + epochEnd: voting.endEpoch, + epochStatus: getVotingEpochStatus( + voting.startEpoch, + voting.endEpoch + ) + }, + publicKey: voting.publicKey + })) + .sort((a, b) => { + const orderStatus = { + [Constants.EpochStatus.CURRENT]: 1, + [Constants.EpochStatus.FUTURE]: 2, + [Constants.EpochStatus.EXPIRED]: 3 + }; + + return ( + orderStatus[a.epochInfo.epochStatus] - + orderStatus[b.epochInfo.epochStatus] + ); + }) + : [], + accountAliasNames: this.extractAccountNamespace( + accountInfo, + accountNames + ), accountLabel: http.accountLabels[accountInfo.address] }; - } + }; /** * Gets custom array of confirmed transactions dataset into Vue Component. @@ -224,31 +252,39 @@ class AccountService { const accountTransactions = { ...searchTransactions, - data: searchTransactions.data.map(transaction => TransactionService.formatTransaction(transaction)) + data: searchTransactions.data.map(transaction => + TransactionService.formatTransaction(transaction)) }; await Promise.all(accountTransactions.data.map(async transaction => { if (transaction?.recipientAddress) { - const { recipientAddress, transactionBody, transactionInfo } = transaction; + const { recipientAddress, transactionBody, transactionInfo } = + transaction; - return (transactionBody.recipient = await helper.resolvedAddress(recipientAddress, transactionInfo.height)); + return (transactionBody.recipient = await helper.resolvedAddress( + recipientAddress, + transactionInfo.height + )); } })); - if (searchCriteria.group === TransactionGroup.Partial || searchCriteria.group === TransactionGroup.Unconfirmed) { + if ( + searchCriteria.group === TransactionGroup.Partial || + searchCriteria.group === TransactionGroup.Unconfirmed + ) { return { - ...accountTransactions, + ...accountTransactions, data: accountTransactions.data.map(accountTransaction => ({ ...accountTransaction, transactionHash: accountTransaction.transactionInfo.hash, transactionType: accountTransaction.type, recipient: accountTransaction.transactionBody?.recipient, - extendGraphicValue: TransactionService.extendGraphicValue(accountTransaction) + extendGraphicValue: + TransactionService.extendGraphicValue(accountTransaction) })) }; } - return { ...accountTransactions, data: accountTransactions.data.map(({ deadline, ...accountTransaction }) => ({ @@ -256,19 +292,21 @@ class AccountService { timestamp: accountTransaction.transactionInfo.timestamp, blockHeight: accountTransaction.transactionInfo.height, transactionHash: accountTransaction.transactionInfo.hash, - transactionType: accountTransaction.type === TransactionType.TRANSFER - ? (accountTransaction.signer === address - ? 'outgoing_' + accountTransaction.transactionBody.transactionType - : 'incoming_' + accountTransaction.transactionBody.transactionType - ) - : accountTransaction.transactionBody.transactionType, - extendGraphicValue: TransactionService.extendGraphicValue(accountTransaction), - recipient: accountTransaction.signer === address - ? accountTransaction.transactionBody?.recipient - : '' + transactionType: + accountTransaction.type === TransactionType.TRANSFER + ? accountTransaction.signer === address + ? 'outgoing_' + accountTransaction.transactionBody.transactionType + : 'incoming_' + accountTransaction.transactionBody.transactionType + : accountTransaction.transactionBody.transactionType, + extendGraphicValue: + TransactionService.extendGraphicValue(accountTransaction), + recipient: + accountTransaction.signer === address + ? accountTransaction.transactionBody?.recipient + : '' })) }; - } + }; /** * Gets custom array of confirmed transactions dataset into Vue Component. @@ -294,7 +332,10 @@ class AccountService { return { ...accountNamespaces, data: accountNamespaces.data.map(namespaces => { - const { expiredInSecond } = helper.calculateNamespaceExpiration(currentHeight, namespaces.endHeight); + const { expiredInSecond } = helper.calculateNamespaceExpiration( + currentHeight, + namespaces.endHeight + ); return { ...namespaces, @@ -305,7 +346,7 @@ class AccountService { }; }) }; - } + }; /** * Gets account harvested block receipt list dataset into Vue Component. @@ -327,15 +368,16 @@ class AccountService { const harvestedBlockReceipt = await ReceiptService.searchReceipts(searchCriteria); - const formattedReceipt = await ReceiptService.createReceiptTransactionStatement(harvestedBlockReceipt.data.balanceChangeStatement); + const formattedReceipt = + await ReceiptService.createReceiptTransactionStatement(harvestedBlockReceipt.data.balanceChangeStatement); return { ...harvestedBlockReceipt, data: formattedReceipt.filter(receipt => receipt.targetAddress === address && - receipt.type === ReceiptType.Harvest_Fee) + receipt.type === ReceiptType.Harvest_Fee) }; - } + }; /** * Gets account receipt list dataset into Vue Component. @@ -347,7 +389,8 @@ class AccountService { static getAccountReceiptList = async (pageInfo, filterValue, address) => { const { pageNumber, pageSize } = pageInfo; - const { BalanceTransferReceipt, BalanceChangeReceipt } = Constants.ReceiptTransactionStatementType; + const { BalanceTransferReceipt, BalanceChangeReceipt } = + Constants.ReceiptTransactionStatementType; let searchCriteria = { pageNumber, @@ -357,34 +400,43 @@ class AccountService { ...filterValue }; - if (filterValue.receiptTransactionStatementType === BalanceTransferReceipt) - Object.assign(searchCriteria, { senderAddress: Address.createFromRawAddress(address) }); + if ( + filterValue.receiptTransactionStatementType === BalanceTransferReceipt + ) { + Object.assign(searchCriteria, { + senderAddress: Address.createFromRawAddress(address) + }); + } - if (filterValue.receiptTransactionStatementType === BalanceChangeReceipt) - Object.assign(searchCriteria, { targetAddress: Address.createFromRawAddress(address) }); + if (filterValue.receiptTransactionStatementType === BalanceChangeReceipt) { + Object.assign(searchCriteria, { + targetAddress: Address.createFromRawAddress(address) + }); + } const receipt = await ReceiptService.searchReceipts(searchCriteria); let formattedReceipt = []; - if (filterValue.receiptTransactionStatementType === BalanceTransferReceipt) { + if ( + filterValue.receiptTransactionStatementType === BalanceTransferReceipt + ) { formattedReceipt = await ReceiptService.createReceiptTransactionStatement(receipt.data.balanceTransferStatement); - formattedReceipt = formattedReceipt.filter(receipt => - receipt.senderAddress === address); + formattedReceipt = formattedReceipt.filter(receipt => receipt.senderAddress === address); } if (filterValue.receiptTransactionStatementType === BalanceChangeReceipt) { formattedReceipt = await ReceiptService.createReceiptTransactionStatement(receipt.data.balanceChangeStatement); formattedReceipt = formattedReceipt.filter(receipt => receipt.targetAddress === address && - receipt.type !== ReceiptType.Harvest_Fee); + receipt.type !== ReceiptType.Harvest_Fee); } return { ...receipt, data: formattedReceipt }; - } + }; /** * Gets Account Metadata list dataset into Vue component @@ -405,7 +457,7 @@ class AccountService { const accountMetadatas = await MetadataService.searchMetadatas(searchCriteria); return accountMetadatas; - } + }; /** * Gets Account Hash Lock list dataset into Vue component @@ -439,7 +491,7 @@ class AccountService { ...accountHashLocks, data: hashLocks }; - } + }; /** * Gets Account Secret Lock list dataset into Vue component. @@ -461,19 +513,24 @@ class AccountService { const mosaics = accountSecretLocks.data.map(secretlock => new Mosaic(secretlock.mosaicId, secretlock.amount)); - const { mosaicInfos, mosaicNames, unresolvedMosaicsMap } = await helper.getMosaicInfoAndNamespace(mosaics); + const { mosaicInfos, mosaicNames, unresolvedMosaicsMap } = + await helper.getMosaicInfoAndNamespace(mosaics); let secretLocks = []; for (const secretLock of accountSecretLocks.data) { secretLocks.push({ ...secretLock, - mosaics: helper.mosaicsFieldObjectBuilder([ - new Mosaic( - new MosaicId(unresolvedMosaicsMap[secretLock.mosaicId.toHex()]), - secretLock.amount - ) - ], mosaicInfos, mosaicNames) + mosaics: helper.mosaicsFieldObjectBuilder( + [ + new Mosaic( + new MosaicId(unresolvedMosaicsMap[secretLock.mosaicId.toHex()]), + secretLock.amount + ) + ], + mosaicInfos, + mosaicNames + ) }); } @@ -481,7 +538,7 @@ class AccountService { ...accountSecretLocks, data: secretLocks }; - } + }; /** * Format AccountInfo to readable accountInfo object. @@ -492,13 +549,16 @@ class AccountService { ...accountInfo, address: accountInfo.address.address, addressHeight: accountInfo.addressHeight.compact(), - publicKey: 0 < accountInfo.publicKeyHeight.compact() ? accountInfo.publicKey : Constants.Message.UNKNOWN, + publicKey: + 0 < accountInfo.publicKeyHeight.compact() + ? accountInfo.publicKey + : Constants.Message.UNKNOWN, publicKeyHeight: accountInfo.publicKeyHeight.compact(), accountType: Constants.AccountType[accountInfo.accountType], supplementalPublicKeys: this.formatSupplementalPublicKeys(accountInfo.supplementalPublicKeys), importance: helper.ImportanceScoreToPercent(accountInfo.importance.compact()), importanceHeight: accountInfo.importanceHeight.compact() - }) + }); /** * Format SupplementalPublicKeys to readable SupplementalPublicKeys object. @@ -507,11 +567,13 @@ class AccountService { */ static formatSupplementalPublicKeys = supplementalPublicKeys => ({ ...supplementalPublicKeys, - linked: supplementalPublicKeys.linked?.publicKey || Constants.Message.UNAVAILABLE, - node: supplementalPublicKeys.node?.publicKey || Constants.Message.UNAVAILABLE, + linked: + supplementalPublicKeys.linked?.publicKey || Constants.Message.UNAVAILABLE, + node: + supplementalPublicKeys.node?.publicKey || Constants.Message.UNAVAILABLE, vrf: supplementalPublicKeys.vrf?.publicKey || Constants.Message.UNAVAILABLE, voting: supplementalPublicKeys.voting || [] - }) + }); /** * Extract Name for Account. @@ -525,7 +587,7 @@ class AccountService { const aliasNames = accountName.names.map(names => names.name); return 0 < aliasNames.length ? aliasNames : [Constants.Message.UNAVAILABLE]; - } + }; /** * Get customize MosaicAmountView dataset for Vue component. @@ -544,16 +606,16 @@ class AccountService { if (0 === mosaic.duration) nonExpiredMosaics.push(mosaic); - if (chainInfo.height < (mosaic.startHeight + mosaic.duration)) + if (chainInfo.height < mosaic.startHeight + mosaic.duration) nonExpiredMosaics.push(mosaic); } return helper.sortMosaics(nonExpiredMosaics); - } + }; static checkNis1Account = async address => { return nem.model.address.isValid(address); - } + }; } export default AccountService; diff --git a/src/infrastructure/AnnouncerService.js b/src/infrastructure/AnnouncerService.js index 88856026b..0d852a41f 100644 --- a/src/infrastructure/AnnouncerService.js +++ b/src/infrastructure/AnnouncerService.js @@ -35,109 +35,134 @@ import { } from 'symbol-sdk'; class AnnounceService { - static announceHashLock = (signedHashLockTransaction, signedTransaction) => { - return new Promise((resolve, reject) => { - const { nodeUrl } = http; - const repositoryFactory = new RepositoryFactoryHttp(nodeUrl); - // const listener = repositoryFactory.createListener() - const receiptHttp = repositoryFactory.createReceiptRepository(); - const transactionHttp = repositoryFactory.createTransactionRepository(); - const customWsEndpoint = `${localStorage.getItem('currentNode') |> helper.httpToWsUrl}/ws`; - const listener = new Listener(customWsEndpoint, transactionHttp, WebSocket); - const transactionService = new TransactionService(transactionHttp, receiptHttp); - - listener.open() - .then(() => { - transactionService - .announceHashLockAggregateBonded(signedHashLockTransaction, signedTransaction, listener) - .subscribe( - x => { - listener.close(); - resolve(x); - }, - err => { - console.error('AnnounceService', err); - listener.close(); - reject(err); - } - ); - }); - }); - } - - static getSignedHashLosck = (signedTransaction, account) => { - const networkCurrencyMosaicId = new MosaicId(http.networkCurrency.mosaicId); - - const hashLockTransaction = HashLockTransaction.create( - Deadline.create(), - new Mosaic( - networkCurrencyMosaicId, - UInt64.fromUint(10 * Math.pow(10, http.networkCurrency.divisibility)) - ), - UInt64.fromUint(480), - signedTransaction, - http.networkType, - UInt64.fromUint(2000000) - ); - - const signedHashLockTransaction = account.sign(hashLockTransaction, http.generationHash); - - return signedHashLockTransaction; - } - - static multisigAccountModification = async ({ - accountPrivateKey, - minApprovalDelta = 1, - minRemovalDelta = 1, - additions = [], - deletions = [] - }) => { - // const transactionRepository = await http.createRepositoryFactory.createTransactionRepository(); - const { networkType } = http; - const networkGenerationHash = http.generationHash; - const account = Account.createFromPrivateKey(accountPrivateKey, networkType); - - ; - - const addressAdditions = additions.map(addition => { - if ('string' === typeof addition && 64 === addition.length) - return PublicAccount.createFromPublicKey(addition, networkType).address; - if ('string' === typeof addition && 39 === addition.length) - return Address.createFromRawAddress(addition); - return addition; - }); - - const addressDeletions = deletions.map(delition => { - if ('string' === typeof delition && 64 === delition.length) - return PublicAccount.createFromPublicKey(delition, networkType).address; - if ('string' === typeof delition && 39 === delition.length) - return Address.createFromRawAddress(delition); - return delition; - }); - - const multisigAccountModificationTransaction = MultisigAccountModificationTransaction.create( - Deadline.create(), - minApprovalDelta, - minRemovalDelta, - addressAdditions, - addressDeletions, - networkType - ); - - const aggregateTransaction = AggregateTransaction.createBonded( - Deadline.create(), - [multisigAccountModificationTransaction.toAggregate(account.publicAccount)], - networkType, - [], - UInt64.fromUint(2000000) - ); - - const signedTransaction = account.sign(aggregateTransaction, networkGenerationHash); - - const signedHashLockTransaction = this.getSignedHashLosck(signedTransaction, account); - - this.announceHashLock(signedHashLockTransaction, signedTransaction); - } + static announceHashLock = (signedHashLockTransaction, signedTransaction) => { + return new Promise((resolve, reject) => { + const { nodeUrl } = http; + const repositoryFactory = new RepositoryFactoryHttp(nodeUrl); + // const listener = repositoryFactory.createListener() + const receiptHttp = repositoryFactory.createReceiptRepository(); + const transactionHttp = repositoryFactory.createTransactionRepository(); + const customWsEndpoint = `${ + localStorage.getItem('currentNode') |> helper.httpToWsUrl + }/ws`; + const listener = new Listener( + customWsEndpoint, + transactionHttp, + WebSocket + ); + const transactionService = new TransactionService( + transactionHttp, + receiptHttp + ); + + listener.open().then(() => { + transactionService + .announceHashLockAggregateBonded( + signedHashLockTransaction, + signedTransaction, + listener + ) + .subscribe( + x => { + listener.close(); + resolve(x); + }, + err => { + console.error('AnnounceService', err); + listener.close(); + reject(err); + } + ); + }); + }); + }; + + static getSignedHashLosck = (signedTransaction, account) => { + const networkCurrencyMosaicId = new MosaicId(http.networkCurrency.mosaicId); + + const hashLockTransaction = HashLockTransaction.create( + Deadline.create(), + new Mosaic( + networkCurrencyMosaicId, + UInt64.fromUint(10 * Math.pow(10, http.networkCurrency.divisibility)) + ), + UInt64.fromUint(480), + signedTransaction, + http.networkType, + UInt64.fromUint(2000000) + ); + + const signedHashLockTransaction = account.sign( + hashLockTransaction, + http.generationHash + ); + + return signedHashLockTransaction; + }; + + static multisigAccountModification = async ({ + accountPrivateKey, + minApprovalDelta = 1, + minRemovalDelta = 1, + additions = [], + deletions = [] + }) => { + // const transactionRepository = await http.createRepositoryFactory.createTransactionRepository(); + const { networkType } = http; + const networkGenerationHash = http.generationHash; + const account = Account.createFromPrivateKey( + accountPrivateKey, + networkType + ); + + const addressAdditions = additions.map(addition => { + if ('string' === typeof addition && 64 === addition.length) + return PublicAccount.createFromPublicKey(addition, networkType).address; + if ('string' === typeof addition && 39 === addition.length) + return Address.createFromRawAddress(addition); + return addition; + }); + + const addressDeletions = deletions.map(delition => { + if ('string' === typeof delition && 64 === delition.length) + return PublicAccount.createFromPublicKey(delition, networkType).address; + if ('string' === typeof delition && 39 === delition.length) + return Address.createFromRawAddress(delition); + return delition; + }); + + const multisigAccountModificationTransaction = + MultisigAccountModificationTransaction.create( + Deadline.create(), + minApprovalDelta, + minRemovalDelta, + addressAdditions, + addressDeletions, + networkType + ); + + const aggregateTransaction = AggregateTransaction.createBonded( + Deadline.create(), + [ + multisigAccountModificationTransaction.toAggregate(account.publicAccount) + ], + networkType, + [], + UInt64.fromUint(2000000) + ); + + const signedTransaction = account.sign( + aggregateTransaction, + networkGenerationHash + ); + + const signedHashLockTransaction = this.getSignedHashLosck( + signedTransaction, + account + ); + + this.announceHashLock(signedHashLockTransaction, signedTransaction); + }; } export default AnnounceService; diff --git a/src/infrastructure/BlockService.js b/src/infrastructure/BlockService.js index 0c1150d70..61bc5eb30 100644 --- a/src/infrastructure/BlockService.js +++ b/src/infrastructure/BlockService.js @@ -19,338 +19,372 @@ import http from './http'; import { Constants } from '../config'; import helper from '../helper'; -import { TransactionService, ReceiptService, AccountService, NodeService } from '../infrastructure'; +import { + TransactionService, + ReceiptService, + AccountService, + NodeService +} from '../infrastructure'; import { sha3_256 as sha3256 } from 'js-sha3'; import { MerkleTree } from 'merkletreejs'; import { take, toArray } from 'rxjs/operators'; -import { UInt64, TransactionGroup, Order, BlockOrderBy, BlockType, ReceiptType } from 'symbol-sdk'; +import { + UInt64, + TransactionGroup, + Order, + BlockOrderBy, + BlockType, + ReceiptType +} from 'symbol-sdk'; class BlockService { - /** - * Gets a BlockInfo for a given block height. - * @param {number} height block height. - * @returns {object} Formatted BlockDTO. - */ - static getBlockByHeight = async height => { - const blockInfo = await http.createRepositoryFactory.createBlockRepository() - .getBlockByHeight(UInt64.fromUint(height)) - .toPromise(); - - return this.formatBlock(blockInfo); - } - - /** - * Gets a blocks from searchCriteria. - * @param {object} blockSearchCriteria Block Search Criteria. - * @returns {object} formatted block data with pagination info. - */ - static searchBlocks = async blockSearchCriteria => { - const searchBlocks = await http.createRepositoryFactory.createBlockRepository() - .search(blockSearchCriteria) - .toPromise(); - - return { - ...searchBlocks, - data: searchBlocks.data.map(block => this.formatBlock(block)) - }; - } - - /** - * Gets a blocks from streamer. - * @param {object} searchCriteria - Object Search Criteria. - * @param {number} noOfBlock - Number of blocks returned. - * @returns {array} formatted BlockInfos. - */ - static streamerBlocks = async (searchCriteria, noOfBlock) => { - const streamerBlocks = await http.blockPaginationStreamer - .search(searchCriteria).pipe(take(noOfBlock), toArray()) - .toPromise(); - - return streamerBlocks.map(block => this.formatBlock(block)); - } - - /** - * Gets a merkle path for merkle proof. - * @param {number} height - block height. - * @param {string} hash Transaction hash. - * @returns {array} MerkleProofInfos - */ - static getMerkleTransaction = async (height, hash) => { - const merklePath = await http.createRepositoryFactory.createBlockRepository() - .getMerkleTransaction(UInt64.fromUint(height), hash) - .toPromise(); - - return merklePath.merklePath || []; - } - - /** - * Gets transactions a merkle tree. - * @param {number} height - block height. - * @returns {object} merkleTree object. - */ - static getMerkleTransactionTree = async height => { - const searchCriteria = { - group: TransactionGroup.Confirmed, - height: UInt64.fromUint(height), - pageSize: 100, - order: Order.Desc - }; - - const streamerTransactions = await TransactionService.streamerTransactions(searchCriteria); - const transactions = streamerTransactions.map(transaction => TransactionService.formatTransaction(transaction)); - - const leaves = transactions.sort((n1, n2) => n1.transactionInfo.index - n2.transactionInfo.index) - .map(transaction => transaction.transactionInfo.hash); - - const tree = new MerkleTree(leaves, sha3256, { - duplicateOdd: true, - hashLeaves: false, - sort: false, - sortLeaves: false, - sortPairs: false, - isBitcoinTree: false }); - - return tree.getLayersAsObject(); - } - - /** - * Get formatted BlockInfo[] dataset into Vue Component. - * @param {object} pageInfo - pageNumber and pageSize. - * @returns {object} Block info list. - */ - static getBlockList = async pageInfo => { - const { pageNumber, pageSize } = pageInfo; - const blockSearchCriteria = { - pageNumber, - pageSize, - order: Order.Desc, - orderBy: BlockOrderBy.Height - }; - - const blocks = await this.searchBlocks(blockSearchCriteria); - - const signerAddress = blocks.data.map(block => block.signer); - - // Get Inflation rate - const receiptSearchCriteria = { - pageSize: blocks.data.length, - order: Order.Desc, - fromHeight: UInt64.fromUint(blocks.data[blocks.data.length - 1].height), - toHeight: UInt64.fromUint(blocks.data[0].height), - receiptTypes: [ReceiptType.Inflation] - }; - - const [accountInfos, { numBlocks }, balanceTransferReceipt] = await Promise.all([ - AccountService.getAccounts(signerAddress), - NodeService.getStorageInfo(), - ReceiptService.searchReceipts(receiptSearchCriteria) - ]); - - return { - ...blocks, - totalRecords: numBlocks, - data: blocks.data.map(block => { - const { supplementalPublicKeys } = accountInfos.find(account => account.address === block.signer); - const inflationRate = balanceTransferReceipt.data.inflationStatement.data - .find(inflation => Number(inflation.height.toString()) === block.height); - const blockReward = Number(inflationRate?.amount.toString()) || 0; - - return { - ...block, - age: helper.convertTimestampToDate(block.timestamp), - blockReward: helper.toNetworkCurrency(blockReward), - harvester: { - signer: block.signer, - linkedAddress: supplementalPublicKeys.linked === Constants.Message.UNAVAILABLE - ? block.signer - : helper.publicKeyToAddress(supplementalPublicKeys.linked) - } - }; - }) - }; - } - - /** - * Get Custom Transactions dataset into Vue Component - * @param {object} pageInfo - page info such as pageNumber, pageSize - * @param {object} filterValue - search criteria - * @param {number} height - block height - * @returns {object} Custom Transactions dataset - */ - static getBlockTransactionList = async (pageInfo, filterValue, height) => { - const { pageNumber, pageSize } = pageInfo; - const searchCriteria = { - pageNumber, - pageSize, - order: Order.Desc, - type: [], - group: TransactionGroup.Confirmed, - height: UInt64.fromUint(height), - ...filterValue - }; - - const [blockInfo, searchTransactions] = await Promise.all([ - BlockService.getBlockInfo(height), - TransactionService.searchTransactions(searchCriteria) - ]); - - const blockTransactions = { - ...searchTransactions, - data: searchTransactions.data.map(transaction => TransactionService.formatTransaction(transaction)) - }; - - return { - ...blockTransactions, - data: blockTransactions.data.map(blockTransaction => ({ - ...blockTransaction, - timestamp: blockInfo.timestamp, - transactionHash: blockTransaction.transactionInfo.hash, - transactionType: blockTransaction.type - })) - }; - } - - /** - * Gets formatted Receipt and Resolution statements. - * @param {number} height - block height. - * @returns {object} Receipt and Resolution info object. - */ - static getBlockReceiptsInfo = async height => { - const searchCriteria = { - order: Order.Desc, - height: UInt64.fromUint(height) - }; - - const [address, mosaic] = await Promise.all([ - ReceiptService.streamerAddressResolution(searchCriteria), - ReceiptService.streamerMosaicResolution(searchCriteria) - ]); - - return { - resolutionStatements: [...address, ...mosaic] - }; - } - - /** - * Get formatted BlockInfo dataset into Vue Component. - * @param {number} height - block height. - * @returns {object} block info object. - */ - static getBlockInfo = async height => { - const block = await this.getBlockByHeight(height); - - const { supplementalPublicKeys } = await AccountService.getAccount(block.signer); - - // Get merkle info - let { stateHash, stateHashSubCacheMerkleRoots, blockReceiptsHash, blockTransactionsHash } = block; - - // Append merkle root name into hash - stateHashSubCacheMerkleRoots = stateHashSubCacheMerkleRoots.map((root, index) => { - return `${Constants.MerkleRootsOrder[index]} - ${root}`; - }); - - let importanceBlockInfo = {}; - - if (block.type === BlockType.ImportanceBlock) { - Object.assign(importanceBlockInfo, { - totalVotingBalance: helper.toNetworkCurrency(block.totalVotingBalance), - harvestingEligibleAccountsCount: Number(block.harvestingEligibleAccountsCount) - }); - } - - return { - ...block, - ...importanceBlockInfo, - symbolTime: block.timestamp, - payloadSize: block.size, - blockHash: block.hash, - harvester: { - signer: block.signer, - linkedAddress: supplementalPublicKeys.linked === Constants.Message.UNAVAILABLE - ? block.signer - : helper.publicKeyToAddress(supplementalPublicKeys.linked) - }, - merkleInfo: { - stateHash, - stateHashSubCacheMerkleRoots, - blockReceiptsHash, - blockTransactionsHash - } - }; - } - - /** - * Gets block receipt list into Vue Component. - * @param {object} pageInfo - page info such as pageNumber, pageSize. - * @param {object} filterValue - search criteria. - * @param {number} height - block height. - * @returns {object} formatted receipt data list - */ - static getBlockReceiptList = async (pageInfo, filterValue, height) => { - const { pageNumber, pageSize } = pageInfo; - - const { - BalanceTransferReceipt, - BalanceChangeReceipt, - InflationReceipt, - ArtifactExpiryReceipt - } = Constants.ReceiptTransactionStatementType; - - const searchCriteria = { - pageNumber, - pageSize, - order: Order.Desc, - height: UInt64.fromUint(height), - ...filterValue - }; - - const receipt = await ReceiptService.searchReceipts(searchCriteria); - - let formattedReceipt = []; - - switch (filterValue.receiptTransactionStatementType) { - case BalanceTransferReceipt: - formattedReceipt = await ReceiptService.createReceiptTransactionStatement(receipt.data.balanceTransferStatement); - break; - case BalanceChangeReceipt: - formattedReceipt = await ReceiptService.createReceiptTransactionStatement(receipt.data.balanceChangeStatement); - break; - case InflationReceipt: - formattedReceipt = await ReceiptService.createReceiptTransactionStatement(receipt.data.inflationStatement); - break; - case ArtifactExpiryReceipt: - formattedReceipt = await ReceiptService.createReceiptTransactionStatement(receipt.data.artifactExpiryStatement); - break; - default: - break; - } - - return { - ...receipt, - data: formattedReceipt - }; - } - - /** - * Format Block to readable Block object. - * @param {object} block BlockDTO. - * @returns {object} readable BlockDTO object. - */ - static formatBlock = block => ({ - ...block, - blockType: Constants.BlockType[block.type], - height: block.height.compact(), - timestampRaw: Number(block.timestamp.toString()), - timestamp: helper.networkTimestamp(Number(block.timestamp.toString())), - totalFee: helper.toNetworkCurrency(block.totalFee), - difficulty: helper.convertBlockDifficultyToReadable(block.difficulty), - feeMultiplier: block.feeMultiplier.toString(), - totalTransactions: block.totalTransactionsCount, - statements: block.statementsCount, - transactions: block.transactionsCount, - signer: helper.publicKeyToAddress(block.signer.publicKey), - beneficiaryAddress: block?.beneficiaryAddress.plain() || Constants.Message.UNAVAILABLE - }) + /** + * Gets a BlockInfo for a given block height. + * @param {number} height block height. + * @returns {object} Formatted BlockDTO. + */ + static getBlockByHeight = async height => { + const blockInfo = await http.createRepositoryFactory + .createBlockRepository() + .getBlockByHeight(UInt64.fromUint(height)) + .toPromise(); + + return this.formatBlock(blockInfo); + }; + + /** + * Gets a blocks from searchCriteria. + * @param {object} blockSearchCriteria Block Search Criteria. + * @returns {object} formatted block data with pagination info. + */ + static searchBlocks = async blockSearchCriteria => { + const searchBlocks = await http.createRepositoryFactory + .createBlockRepository() + .search(blockSearchCriteria) + .toPromise(); + + return { + ...searchBlocks, + data: searchBlocks.data.map(block => this.formatBlock(block)) + }; + }; + + /** + * Gets a blocks from streamer. + * @param {object} searchCriteria - Object Search Criteria. + * @param {number} noOfBlock - Number of blocks returned. + * @returns {array} formatted BlockInfos. + */ + static streamerBlocks = async (searchCriteria, noOfBlock) => { + const streamerBlocks = await http.blockPaginationStreamer + .search(searchCriteria) + .pipe(take(noOfBlock), toArray()) + .toPromise(); + + return streamerBlocks.map(block => this.formatBlock(block)); + }; + + /** + * Gets a merkle path for merkle proof. + * @param {number} height - block height. + * @param {string} hash Transaction hash. + * @returns {array} MerkleProofInfos + */ + static getMerkleTransaction = async (height, hash) => { + const merklePath = await http.createRepositoryFactory + .createBlockRepository() + .getMerkleTransaction(UInt64.fromUint(height), hash) + .toPromise(); + + return merklePath.merklePath || []; + }; + + /** + * Gets transactions a merkle tree. + * @param {number} height - block height. + * @returns {object} merkleTree object. + */ + static getMerkleTransactionTree = async height => { + const searchCriteria = { + group: TransactionGroup.Confirmed, + height: UInt64.fromUint(height), + pageSize: 100, + order: Order.Desc + }; + + const streamerTransactions = await TransactionService.streamerTransactions(searchCriteria); + const transactions = streamerTransactions.map(transaction => + TransactionService.formatTransaction(transaction)); + + const leaves = transactions + .sort((n1, n2) => n1.transactionInfo.index - n2.transactionInfo.index) + .map(transaction => transaction.transactionInfo.hash); + + const tree = new MerkleTree(leaves, sha3256, { + duplicateOdd: true, + hashLeaves: false, + sort: false, + sortLeaves: false, + sortPairs: false, + isBitcoinTree: false + }); + + return tree.getLayersAsObject(); + }; + + /** + * Get formatted BlockInfo[] dataset into Vue Component. + * @param {object} pageInfo - pageNumber and pageSize. + * @returns {object} Block info list. + */ + static getBlockList = async pageInfo => { + const { pageNumber, pageSize } = pageInfo; + const blockSearchCriteria = { + pageNumber, + pageSize, + order: Order.Desc, + orderBy: BlockOrderBy.Height + }; + + const blocks = await this.searchBlocks(blockSearchCriteria); + + const signerAddress = blocks.data.map(block => block.signer); + + // Get Inflation rate + const receiptSearchCriteria = { + pageSize: blocks.data.length, + order: Order.Desc, + fromHeight: UInt64.fromUint(blocks.data[blocks.data.length - 1].height), + toHeight: UInt64.fromUint(blocks.data[0].height), + receiptTypes: [ReceiptType.Inflation] + }; + + const [accountInfos, { numBlocks }, balanceTransferReceipt] = + await Promise.all([ + AccountService.getAccounts(signerAddress), + NodeService.getStorageInfo(), + ReceiptService.searchReceipts(receiptSearchCriteria) + ]); + + return { + ...blocks, + totalRecords: numBlocks, + data: blocks.data.map(block => { + const { supplementalPublicKeys } = accountInfos.find(account => account.address === block.signer); + const inflationRate = + balanceTransferReceipt.data.inflationStatement.data.find(inflation => + Number(inflation.height.toString()) === block.height); + const blockReward = Number(inflationRate?.amount.toString()) || 0; + + return { + ...block, + age: helper.convertTimestampToDate(block.timestamp), + blockReward: helper.toNetworkCurrency(blockReward), + harvester: { + signer: block.signer, + linkedAddress: + supplementalPublicKeys.linked === Constants.Message.UNAVAILABLE + ? block.signer + : helper.publicKeyToAddress(supplementalPublicKeys.linked) + } + }; + }) + }; + }; + + /** + * Get Custom Transactions dataset into Vue Component + * @param {object} pageInfo - page info such as pageNumber, pageSize + * @param {object} filterValue - search criteria + * @param {number} height - block height + * @returns {object} Custom Transactions dataset + */ + static getBlockTransactionList = async (pageInfo, filterValue, height) => { + const { pageNumber, pageSize } = pageInfo; + const searchCriteria = { + pageNumber, + pageSize, + order: Order.Desc, + type: [], + group: TransactionGroup.Confirmed, + height: UInt64.fromUint(height), + ...filterValue + }; + + const [blockInfo, searchTransactions] = await Promise.all([ + BlockService.getBlockInfo(height), + TransactionService.searchTransactions(searchCriteria) + ]); + + const blockTransactions = { + ...searchTransactions, + data: searchTransactions.data.map(transaction => + TransactionService.formatTransaction(transaction)) + }; + + return { + ...blockTransactions, + data: blockTransactions.data.map(blockTransaction => ({ + ...blockTransaction, + timestamp: blockInfo.timestamp, + transactionHash: blockTransaction.transactionInfo.hash, + transactionType: blockTransaction.type + })) + }; + }; + + /** + * Gets formatted Receipt and Resolution statements. + * @param {number} height - block height. + * @returns {object} Receipt and Resolution info object. + */ + static getBlockReceiptsInfo = async height => { + const searchCriteria = { + order: Order.Desc, + height: UInt64.fromUint(height) + }; + + const [address, mosaic] = await Promise.all([ + ReceiptService.streamerAddressResolution(searchCriteria), + ReceiptService.streamerMosaicResolution(searchCriteria) + ]); + + return { + resolutionStatements: [...address, ...mosaic] + }; + }; + + /** + * Get formatted BlockInfo dataset into Vue Component. + * @param {number} height - block height. + * @returns {object} block info object. + */ + static getBlockInfo = async height => { + const block = await this.getBlockByHeight(height); + + const { supplementalPublicKeys } = await AccountService.getAccount(block.signer); + + // Get merkle info + let { + stateHash, + stateHashSubCacheMerkleRoots, + blockReceiptsHash, + blockTransactionsHash + } = block; + + // Append merkle root name into hash + stateHashSubCacheMerkleRoots = stateHashSubCacheMerkleRoots.map((root, index) => { + return `${Constants.MerkleRootsOrder[index]} - ${root}`; + }); + + let importanceBlockInfo = {}; + + if (block.type === BlockType.ImportanceBlock) { + Object.assign(importanceBlockInfo, { + totalVotingBalance: helper.toNetworkCurrency(block.totalVotingBalance), + harvestingEligibleAccountsCount: Number(block.harvestingEligibleAccountsCount) + }); + } + + return { + ...block, + ...importanceBlockInfo, + symbolTime: block.timestamp, + payloadSize: block.size, + blockHash: block.hash, + harvester: { + signer: block.signer, + linkedAddress: + supplementalPublicKeys.linked === Constants.Message.UNAVAILABLE + ? block.signer + : helper.publicKeyToAddress(supplementalPublicKeys.linked) + }, + merkleInfo: { + stateHash, + stateHashSubCacheMerkleRoots, + blockReceiptsHash, + blockTransactionsHash + } + }; + }; + + /** + * Gets block receipt list into Vue Component. + * @param {object} pageInfo - page info such as pageNumber, pageSize. + * @param {object} filterValue - search criteria. + * @param {number} height - block height. + * @returns {object} formatted receipt data list + */ + static getBlockReceiptList = async (pageInfo, filterValue, height) => { + const { pageNumber, pageSize } = pageInfo; + + const { + BalanceTransferReceipt, + BalanceChangeReceipt, + InflationReceipt, + ArtifactExpiryReceipt + } = Constants.ReceiptTransactionStatementType; + + const searchCriteria = { + pageNumber, + pageSize, + order: Order.Desc, + height: UInt64.fromUint(height), + ...filterValue + }; + + const receipt = await ReceiptService.searchReceipts(searchCriteria); + + let formattedReceipt = []; + + switch (filterValue.receiptTransactionStatementType) { + case BalanceTransferReceipt: + formattedReceipt = + await ReceiptService.createReceiptTransactionStatement(receipt.data.balanceTransferStatement); + break; + case BalanceChangeReceipt: + formattedReceipt = + await ReceiptService.createReceiptTransactionStatement(receipt.data.balanceChangeStatement); + break; + case InflationReceipt: + formattedReceipt = + await ReceiptService.createReceiptTransactionStatement(receipt.data.inflationStatement); + break; + case ArtifactExpiryReceipt: + formattedReceipt = + await ReceiptService.createReceiptTransactionStatement(receipt.data.artifactExpiryStatement); + break; + default: + break; + } + + return { + ...receipt, + data: formattedReceipt + }; + }; + + /** + * Format Block to readable Block object. + * @param {object} block BlockDTO. + * @returns {object} readable BlockDTO object. + */ + static formatBlock = block => ({ + ...block, + blockType: Constants.BlockType[block.type], + height: block.height.compact(), + timestampRaw: Number(block.timestamp.toString()), + timestamp: helper.networkTimestamp(Number(block.timestamp.toString())), + totalFee: helper.toNetworkCurrency(block.totalFee), + difficulty: helper.convertBlockDifficultyToReadable(block.difficulty), + feeMultiplier: block.feeMultiplier.toString(), + totalTransactions: block.totalTransactionsCount, + statements: block.statementsCount, + transactions: block.transactionsCount, + signer: helper.publicKeyToAddress(block.signer.publicKey), + beneficiaryAddress: + block?.beneficiaryAddress.plain() || Constants.Message.UNAVAILABLE + }); } export default BlockService; diff --git a/src/infrastructure/ChainService.js b/src/infrastructure/ChainService.js index feb6d8042..183e80fad 100644 --- a/src/infrastructure/ChainService.js +++ b/src/infrastructure/ChainService.js @@ -19,24 +19,25 @@ import http from './http'; class ChainService { - /** - * Gets chain info such as current block height, finalized block and etc - * @returns {object} formatted chain info - */ - static getChainInfo = async () => { - const chainInfo = await http.createRepositoryFactory.createChainRepository() - .getChainInfo() - .toPromise(); + /** + * Gets chain info such as current block height, finalized block and etc + * @returns {object} formatted chain info + */ + static getChainInfo = async () => { + const chainInfo = await http.createRepositoryFactory + .createChainRepository() + .getChainInfo() + .toPromise(); - return { - ...chainInfo, - height: chainInfo.height.compact(), - latestFinalizedBlock: { - ...chainInfo.latestFinalizedBlock, - height: chainInfo.latestFinalizedBlock.height.compact() - } - }; - } + return { + ...chainInfo, + height: chainInfo.height.compact(), + latestFinalizedBlock: { + ...chainInfo.latestFinalizedBlock, + height: chainInfo.latestFinalizedBlock.height.compact() + } + }; + }; } export default ChainService; diff --git a/src/infrastructure/CreateTransaction.js b/src/infrastructure/CreateTransaction.js index 2245f1441..808c08f83 100644 --- a/src/infrastructure/CreateTransaction.js +++ b/src/infrastructure/CreateTransaction.js @@ -23,110 +23,137 @@ import { NamespaceService } from '../infrastructure'; import { Address, Mosaic, MosaicId, Convert } from 'symbol-sdk'; class CreateTransaction { - static transferTransaction = async (transactionObj, { mosaicInfos, mosaicNames, unresolvedMosaicsMap }) => { - const { transactionInfo } = transactionObj; - - const [resolvedAddress] = await Promise.all([ - helper.resolvedAddress(transactionObj.recipientAddress, transactionInfo.height) - ]); - - const resolvedMosaics = transactionObj.mosaics.map(mosaic => { - return new Mosaic(new MosaicId(unresolvedMosaicsMap[mosaic.id.toHex()]), mosaic.amount); - }); - - return { - ...transactionObj, - transactionBody: { - transactionType: transactionObj.type, - message: transactionObj.message, - recipient: resolvedAddress, - mosaics: helper.mosaicsFieldObjectBuilder(resolvedMosaics, mosaicInfos, mosaicNames) - } - }; - } - - static namespaceRegistration = async transactionObj => { - return { - ...transactionObj, - transactionBody: { - transactionType: transactionObj.type, - recipient: http.networkConfig.NamespaceRentalFeeSinkAddress.address, - registrationType: Constants.NamespaceRegistrationType[transactionObj.registrationType], - namespaceName: transactionObj.namespaceName, - namespaceId: transactionObj.namespaceId.toHex(), - parentId: 'undefined' !== typeof transactionObj.parentId ? transactionObj.parentId?.toHex() : Constants.Message.UNAVAILABLE, - duration: 'undefined' !== typeof transactionObj.duration ? transactionObj.duration?.compact() : Constants.Message.UNLIMITED - } - }; - } - - static addressAlias = async transactionObj => { - const namespaceNames = await NamespaceService.getNamespacesNames([transactionObj.namespaceId]); - const namespaceName = namespaceNames.find(namespace => namespace.namespaceId === transactionObj.namespaceId.toHex()); - - return { - ...transactionObj, - transactionBody: { - transactionType: transactionObj.type, - aliasAction: Constants.AliasAction[transactionObj.aliasAction], - namespaceId: transactionObj.namespaceId.toHex(), - namespaceName: namespaceName.name, - address: transactionObj.address.address - } - }; - } - - static mosaicAlias = async transactionObj => { - const namespaceNames = await NamespaceService.getNamespacesNames([transactionObj.namespaceId]); - const namespaceName = namespaceNames.find(namespace => namespace.namespaceId === transactionObj.namespaceId.toHex()); - - return { - ...transactionObj, - transactionBody: { - transactionType: transactionObj.type, - aliasAction: Constants.AliasAction[transactionObj.aliasAction], - namespaceId: transactionObj.namespaceId.id.toHex(), - namespaceName: namespaceName.name, - mosaicId: transactionObj.mosaicId.id.toHex() - } - }; - }; - - static mosaicDefinition = async transactionObj => { - const resolvedMosaic = await helper.resolveMosaicId(transactionObj.mosaicId); - - return { - ...transactionObj, - transactionBody: { - transactionType: transactionObj.type, - recipient: http.networkConfig.MosaicRentalSinkAddress.address, - mosaicId: resolvedMosaic.toHex(), - divisibility: transactionObj.divisibility, - duration: transactionObj.duration.compact(), - nonce: transactionObj.nonce.toHex(), - supplyMutable: transactionObj.flags.supplyMutable, - transferable: transactionObj.flags.transferable, - restrictable: transactionObj.flags.restrictable, - revokable: transactionObj.flags.revokable - } - }; - }; - - static mosaicSupplyChange = async transactionObj => { - const resolvedMosaic = await helper.resolveMosaicId(transactionObj.mosaicId); - - return { - ...transactionObj, - transactionBody: { - transactionType: transactionObj.type, - mosaicId: resolvedMosaic.toHex(), - action: Constants.MosaicSupplyChangeAction[transactionObj.action], - delta: transactionObj.delta.compact() - } - }; - }; - - static mosaicSupplyRevocation = async (transactionObj, { mosaicInfos, mosaicNames, unresolvedMosaicsMap }) => { + static transferTransaction = async ( + transactionObj, + { mosaicInfos, mosaicNames, unresolvedMosaicsMap } + ) => { + const { transactionInfo } = transactionObj; + + const [resolvedAddress] = await Promise.all([ + helper.resolvedAddress( + transactionObj.recipientAddress, + transactionInfo.height + ) + ]); + + const resolvedMosaics = transactionObj.mosaics.map(mosaic => { + return new Mosaic( + new MosaicId(unresolvedMosaicsMap[mosaic.id.toHex()]), + mosaic.amount + ); + }); + + return { + ...transactionObj, + transactionBody: { + transactionType: transactionObj.type, + message: transactionObj.message, + recipient: resolvedAddress, + mosaics: helper.mosaicsFieldObjectBuilder( + resolvedMosaics, + mosaicInfos, + mosaicNames + ) + } + }; + }; + + static namespaceRegistration = async transactionObj => { + return { + ...transactionObj, + transactionBody: { + transactionType: transactionObj.type, + recipient: http.networkConfig.NamespaceRentalFeeSinkAddress.address, + registrationType: + Constants.NamespaceRegistrationType[transactionObj.registrationType], + namespaceName: transactionObj.namespaceName, + namespaceId: transactionObj.namespaceId.toHex(), + parentId: + 'undefined' !== typeof transactionObj.parentId + ? transactionObj.parentId?.toHex() + : Constants.Message.UNAVAILABLE, + duration: + 'undefined' !== typeof transactionObj.duration + ? transactionObj.duration?.compact() + : Constants.Message.UNLIMITED + } + }; + }; + + static addressAlias = async transactionObj => { + const namespaceNames = await NamespaceService.getNamespacesNames([ + transactionObj.namespaceId + ]); + const namespaceName = namespaceNames.find(namespace => namespace.namespaceId === transactionObj.namespaceId.toHex()); + + return { + ...transactionObj, + transactionBody: { + transactionType: transactionObj.type, + aliasAction: Constants.AliasAction[transactionObj.aliasAction], + namespaceId: transactionObj.namespaceId.toHex(), + namespaceName: namespaceName.name, + address: transactionObj.address.address + } + }; + }; + + static mosaicAlias = async transactionObj => { + const namespaceNames = await NamespaceService.getNamespacesNames([ + transactionObj.namespaceId + ]); + const namespaceName = namespaceNames.find(namespace => namespace.namespaceId === transactionObj.namespaceId.toHex()); + + return { + ...transactionObj, + transactionBody: { + transactionType: transactionObj.type, + aliasAction: Constants.AliasAction[transactionObj.aliasAction], + namespaceId: transactionObj.namespaceId.id.toHex(), + namespaceName: namespaceName.name, + mosaicId: transactionObj.mosaicId.id.toHex() + } + }; + }; + + static mosaicDefinition = async transactionObj => { + const resolvedMosaic = await helper.resolveMosaicId(transactionObj.mosaicId); + + return { + ...transactionObj, + transactionBody: { + transactionType: transactionObj.type, + recipient: http.networkConfig.MosaicRentalSinkAddress.address, + mosaicId: resolvedMosaic.toHex(), + divisibility: transactionObj.divisibility, + duration: transactionObj.duration.compact(), + nonce: transactionObj.nonce.toHex(), + supplyMutable: transactionObj.flags.supplyMutable, + transferable: transactionObj.flags.transferable, + restrictable: transactionObj.flags.restrictable, + revokable: transactionObj.flags.revokable + } + }; + }; + + static mosaicSupplyChange = async transactionObj => { + const resolvedMosaic = await helper.resolveMosaicId(transactionObj.mosaicId); + + return { + ...transactionObj, + transactionBody: { + transactionType: transactionObj.type, + mosaicId: resolvedMosaic.toHex(), + action: Constants.MosaicSupplyChangeAction[transactionObj.action], + delta: transactionObj.delta.compact() + } + }; + }; + + static mosaicSupplyRevocation = async ( + transactionObj, + { mosaicInfos, mosaicNames, unresolvedMosaicsMap } + ) => { const resolvedMosaics = [ new Mosaic( new MosaicId(unresolvedMosaicsMap[transactionObj.mosaic.id.toHex()]), @@ -139,295 +166,354 @@ class CreateTransaction { transactionBody: { transactionType: transactionObj.type, address: transactionObj.sourceAddress.address, - mosaics: helper.mosaicsFieldObjectBuilder(resolvedMosaics, mosaicInfos, mosaicNames) + mosaics: helper.mosaicsFieldObjectBuilder( + resolvedMosaics, + mosaicInfos, + mosaicNames + ) + } + }; + }; + + static multisigAccountModification = async transactionObj => { + const { transactionInfo } = transactionObj; + const [addressAdditions, addressDeletions] = await Promise.all([ + Promise.all(transactionObj.addressAdditions.map(address => { + return helper.resolvedAddress(address, transactionInfo.height); + })), + Promise.all(transactionObj.addressDeletions.map(address => { + return helper.resolvedAddress(address, transactionInfo.height); + })) + ]); + + return { + ...transactionObj, + transactionBody: { + transactionType: transactionObj.type, + minApprovalDelta: transactionObj.minApprovalDelta, + minRemovalDelta: transactionObj.minRemovalDelta, + addressAdditions: addressAdditions, + addressDeletions: addressDeletions + } + }; + }; + + static hashLock = async ( + transactionObj, + { mosaicInfos, mosaicNames, unresolvedMosaicsMap } + ) => { + const resolvedMosaics = [ + new Mosaic( + new MosaicId(unresolvedMosaicsMap[transactionObj.mosaic.id.toHex()]), + transactionObj.mosaic.amount + ) + ]; + + return { + ...transactionObj, + transactionBody: { + transactionType: transactionObj.type, + duration: transactionObj.duration.compact(), + mosaics: helper.mosaicsFieldObjectBuilder( + resolvedMosaics, + mosaicInfos, + mosaicNames + ), + hash: transactionObj.hash + } + }; + }; + + static secretLock = async ( + transactionObj, + { mosaicInfos, mosaicNames, unresolvedMosaicsMap } + ) => { + const { transactionInfo } = transactionObj; + + const [resolvedAddress] = await Promise.all([ + helper.resolvedAddress( + transactionObj.recipientAddress, + transactionInfo.height + ) + ]); + + const resolvedMosaics = [ + new Mosaic( + new MosaicId(unresolvedMosaicsMap[transactionObj.mosaic.id.toHex()]), + transactionObj.mosaic.amount + ) + ]; + + return { + ...transactionObj, + transactionBody: { + transactionType: transactionObj.type, + duration: transactionObj.duration.compact(), + mosaics: helper.mosaicsFieldObjectBuilder( + resolvedMosaics, + mosaicInfos, + mosaicNames + ), + secret: transactionObj.secret, + recipient: resolvedAddress, + hashAlgorithm: Constants.LockHashAlgorithm[transactionObj.hashAlgorithm] + } + }; + }; + + static secretProof = async transactionObj => { + const { transactionInfo } = transactionObj; + const resolvedAddress = await helper.resolvedAddress( + transactionObj.recipientAddress, + transactionInfo.height + ); + + return { + ...transactionObj, + transactionBody: { + transactionType: transactionObj.type, + hashAlgorithm: + Constants.LockHashAlgorithm[transactionObj.hashAlgorithm], + recipient: resolvedAddress, + secret: transactionObj.secret, + proof: transactionObj.proof } }; }; - static multisigAccountModification = async transactionObj => { - const { transactionInfo } = transactionObj; - const [addressAdditions, addressDeletions] = await Promise.all([ - Promise.all(transactionObj.addressAdditions.map(address => { - return helper.resolvedAddress(address, transactionInfo.height); - })), - Promise.all(transactionObj.addressDeletions.map(address => { - return helper.resolvedAddress(address, transactionInfo.height); - })) - ]); - - return { - ...transactionObj, - transactionBody: { - transactionType: transactionObj.type, - minApprovalDelta: transactionObj.minApprovalDelta, - minRemovalDelta: transactionObj.minRemovalDelta, - addressAdditions: addressAdditions, - addressDeletions: addressDeletions - } - }; - } - - static hashLock = async (transactionObj, { mosaicInfos, mosaicNames, unresolvedMosaicsMap }) => { - const resolvedMosaics = [ - new Mosaic( - new MosaicId(unresolvedMosaicsMap[transactionObj.mosaic.id.toHex()]), - transactionObj.mosaic.amount - ) - ]; - - return { - ...transactionObj, - transactionBody: { - transactionType: transactionObj.type, - duration: transactionObj.duration.compact(), - mosaics: helper.mosaicsFieldObjectBuilder(resolvedMosaics, mosaicInfos, mosaicNames), - hash: transactionObj.hash - } - }; - } - - static secretLock = async (transactionObj, { mosaicInfos, mosaicNames, unresolvedMosaicsMap }) => { - const { transactionInfo } = transactionObj; - - const [resolvedAddress] = await Promise.all([ - helper.resolvedAddress(transactionObj.recipientAddress, transactionInfo.height) - ]); - - const resolvedMosaics = [ - new Mosaic( - new MosaicId(unresolvedMosaicsMap[transactionObj.mosaic.id.toHex()]), - transactionObj.mosaic.amount - ) - ]; - - return { - ...transactionObj, - transactionBody: { - transactionType: transactionObj.type, - duration: transactionObj.duration.compact(), - mosaics: helper.mosaicsFieldObjectBuilder(resolvedMosaics, mosaicInfos, mosaicNames), - secret: transactionObj.secret, - recipient: resolvedAddress, - hashAlgorithm: Constants.LockHashAlgorithm[transactionObj.hashAlgorithm] - } - }; - }; - - static secretProof = async transactionObj => { - const { transactionInfo } = transactionObj; - const resolvedAddress = await helper.resolvedAddress(transactionObj.recipientAddress, transactionInfo.height); - - return { - ...transactionObj, - transactionBody: { - transactionType: transactionObj.type, - hashAlgorithm: Constants.LockHashAlgorithm[transactionObj.hashAlgorithm], - recipient: resolvedAddress, - secret: transactionObj.secret, - proof: transactionObj.proof - } - }; - }; - - static accountAddressRestriction = async transactionObj => { - const { transactionInfo } = transactionObj; - const [addressAdditions, addressDeletions] = await Promise.all([ - Promise.all(transactionObj.restrictionAdditions.map(address => { - return helper.resolvedAddress(address, transactionInfo.height); - })), - Promise.all(transactionObj.restrictionDeletions.map(address => { - return helper.resolvedAddress(address, transactionInfo.height); - })) - ]); - - return { - ...transactionObj, - transactionBody: { - transactionType: transactionObj.type, - restrictionType: Constants.AddressRestrictionFlag[transactionObj.restrictionFlags], - restrictionAddressAdditions: addressAdditions, - restrictionAddressDeletions: addressDeletions - } - }; - }; - - static accountMosaicRestriction = async transactionObj => { - // Todo: mosaic restriction field - return { - ...transactionObj, - transactionBody: { - transactionType: transactionObj.type, - restrictionType: Constants.MosaicRestrictionFlag[transactionObj.restrictionFlags], - restrictionMosaicAdditions: transactionObj.restrictionAdditions.map(restriction => restriction.id.toHex()), - restrictionMosaicDeletions: transactionObj.restrictionDeletions.map(restriction => restriction.id.toHex()) - } - }; - } - - static accountOperationRestriction = async transactionObj => { - return { - ...transactionObj, - transactionBody: { - transactionType: transactionObj.type, - restrictionType: Constants.OperationRestrictionFlag[transactionObj.restrictionFlags], - restrictionOperationAdditions: transactionObj.restrictionAdditions.map(operation => operation), - restrictionOperationDeletions: transactionObj.restrictionDeletions.map(operation => operation) - } - }; - }; - - static mosaicAddressRestriction = async transactionObj => { - const { transactionInfo } = transactionObj; - const [resolvedMosaic, targetAddress] = await Promise.all([ - helper.resolveMosaicId(transactionObj.mosaicId), - helper.resolvedAddress(transactionObj.targetAddress, transactionInfo.height) - ]); - - const mosaicAliasNames = await helper.getMosaicAliasNames(resolvedMosaic); - - return { - ...transactionObj, - transactionBody: { - transactionType: transactionObj.type, - mosaicId: resolvedMosaic.toHex(), - mosaicAliasNames, - targetAddress: targetAddress, - restrictionKey: transactionObj.restrictionKey.toHex(), - previousRestrictionValue: transactionObj.previousRestrictionValue.toString(), - newRestrictionValue: transactionObj.newRestrictionValue.toString() - } - }; - }; - - static mosaicGlobalRestriction = async transactionObj => { - const referenceMosaicId = '0000000000000000' === transactionObj.referenceMosaicId.toHex() - ? transactionObj.mosaicId - : transactionObj.referenceMosaicId; - const mosaicAliasNames = await helper.getMosaicAliasNames(referenceMosaicId); - - return { - ...transactionObj, - transactionBody: { - transactionType: transactionObj.type, - referenceMosaicId: referenceMosaicId.toHex(), - mosaicAliasNames, - restrictionKey: transactionObj.restrictionKey.toHex(), - previousRestrictionType: Constants.MosaicRestrictionType[transactionObj.previousRestrictionType], - previousRestrictionValue: transactionObj.previousRestrictionValue.compact(), - newRestrictionType: Constants.MosaicRestrictionType[transactionObj.newRestrictionType], - newRestrictionValue: transactionObj.newRestrictionValue.compact() - } - }; - }; - - static accountMetadata = async transactionObj => { - const { transactionInfo } = transactionObj; - const resolvedAddress = await helper.resolvedAddress(transactionObj.targetAddress, transactionInfo.height); - - return { - ...transactionObj, - transactionBody: { - transactionType: transactionObj.type, - scopedMetadataKey: transactionObj.scopedMetadataKey.toHex(), - targetAddress: resolvedAddress, - metadataValue: `${Convert.uint8ToHex(transactionObj.value)} (Text: ${Convert.uint8ToUtf8(transactionObj.value)})`, - valueSizeDelta: transactionObj.valueSizeDelta - } - }; - }; - - static mosaicMetadata = async transactionObj => { - const { transactionInfo } = transactionObj; - const [resolvedMosaic, resolvedAddress] = await Promise.all([ - helper.resolveMosaicId(transactionObj.targetMosaicId), - helper.resolvedAddress(transactionObj.targetAddress, transactionInfo.height) - ]); - - const mosaicAliasNames = await helper.getMosaicAliasNames(resolvedMosaic); - - return { - ...transactionObj, - transactionBody: { - transactionType: transactionObj.type, - scopedMetadataKey: transactionObj.scopedMetadataKey.toHex(), - targetMosaicId: resolvedMosaic.toHex(), - targetMosaicAliasNames: mosaicAliasNames, - targetAddress: resolvedAddress, - metadataValue: `${Convert.uint8ToHex(transactionObj.value)} (Text: ${Convert.uint8ToUtf8(transactionObj.value)})`, - valueSizeDelta: transactionObj.valueSizeDelta - } - }; - }; - - static namespaceMetadata = async transactionObj => { - const { transactionInfo } = transactionObj; - const [namespaceName, resolvedAddress] = await Promise.all([ - NamespaceService.getNamespacesNames([transactionObj.targetNamespaceId]), - helper.resolvedAddress(transactionObj.targetAddress, transactionInfo.height) - ]); - - return { - ...transactionObj, - transactionBody: { - transactionType: transactionObj.type, - scopedMetadataKey: transactionObj.scopedMetadataKey.toHex(), - targetNamespaceId: transactionObj.targetNamespaceId.toHex(), - namespaceName: namespaceName, - targetAddress: resolvedAddress, - metadataValue: `${Convert.uint8ToHex(transactionObj.value)} (Text: ${Convert.uint8ToUtf8(transactionObj.value)})`, - valueSizeDelta: transactionObj.valueSizeDelta - } - }; - }; - - static votingKeyLink = transactionObj => { - return { - ...transactionObj, - transactionBody: { - linkAction: Constants.LinkAction[transactionObj.linkAction], - linkedPublicKey: transactionObj.linkedPublicKey, - linkedAccountAddress: Address.createFromPublicKey(transactionObj.linkedPublicKey, http.networkType).plain(), - startEpoch: transactionObj.startEpoch, - endEpoch: transactionObj.endEpoch - } - }; - }; - - static vrfKeyLink = transactionObj => { - return { - ...transactionObj, - transactionBody: { - transactionType: transactionObj.type, - linkAction: Constants.LinkAction[transactionObj.linkAction], - linkedPublicKey: transactionObj.linkedPublicKey, - linkedAccountAddress: Address.createFromPublicKey(transactionObj.linkedPublicKey, http.networkType).plain() - } - }; - }; - - static nodeKeyLink = transactionObj => { - return { - ...transactionObj, - transactionBody: { - transactionType: transactionObj.type, - linkAction: Constants.LinkAction[transactionObj.linkAction], - linkedPublicKey: transactionObj.linkedPublicKey, - linkedAccountAddress: Address.createFromPublicKey(transactionObj.linkedPublicKey, http.networkType).plain() - } - }; - }; - - static accountKeyLink = transactionObj => { - return { - ...transactionObj, - transactionBody: { - transactionType: transactionObj.type, - linkAction: Constants.LinkAction[transactionObj.linkAction], - linkedPublicKey: transactionObj.linkedPublicKey, - linkedAccountAddress: Address.createFromPublicKey(transactionObj.linkedPublicKey, http.networkType).plain() - } - }; - }; -}; + static accountAddressRestriction = async transactionObj => { + const { transactionInfo } = transactionObj; + const [addressAdditions, addressDeletions] = await Promise.all([ + Promise.all(transactionObj.restrictionAdditions.map(address => { + return helper.resolvedAddress(address, transactionInfo.height); + })), + Promise.all(transactionObj.restrictionDeletions.map(address => { + return helper.resolvedAddress(address, transactionInfo.height); + })) + ]); + + return { + ...transactionObj, + transactionBody: { + transactionType: transactionObj.type, + restrictionType: + Constants.AddressRestrictionFlag[transactionObj.restrictionFlags], + restrictionAddressAdditions: addressAdditions, + restrictionAddressDeletions: addressDeletions + } + }; + }; + + static accountMosaicRestriction = async transactionObj => { + // Todo: mosaic restriction field + return { + ...transactionObj, + transactionBody: { + transactionType: transactionObj.type, + restrictionType: + Constants.MosaicRestrictionFlag[transactionObj.restrictionFlags], + restrictionMosaicAdditions: transactionObj.restrictionAdditions.map(restriction => restriction.id.toHex()), + restrictionMosaicDeletions: transactionObj.restrictionDeletions.map(restriction => restriction.id.toHex()) + } + }; + }; + + static accountOperationRestriction = async transactionObj => { + return { + ...transactionObj, + transactionBody: { + transactionType: transactionObj.type, + restrictionType: + Constants.OperationRestrictionFlag[transactionObj.restrictionFlags], + restrictionOperationAdditions: transactionObj.restrictionAdditions.map(operation => operation), + restrictionOperationDeletions: transactionObj.restrictionDeletions.map(operation => operation) + } + }; + }; + + static mosaicAddressRestriction = async transactionObj => { + const { transactionInfo } = transactionObj; + const [resolvedMosaic, targetAddress] = await Promise.all([ + helper.resolveMosaicId(transactionObj.mosaicId), + helper.resolvedAddress( + transactionObj.targetAddress, + transactionInfo.height + ) + ]); + + const mosaicAliasNames = await helper.getMosaicAliasNames(resolvedMosaic); + + return { + ...transactionObj, + transactionBody: { + transactionType: transactionObj.type, + mosaicId: resolvedMosaic.toHex(), + mosaicAliasNames, + targetAddress: targetAddress, + restrictionKey: transactionObj.restrictionKey.toHex(), + previousRestrictionValue: + transactionObj.previousRestrictionValue.toString(), + newRestrictionValue: transactionObj.newRestrictionValue.toString() + } + }; + }; + + static mosaicGlobalRestriction = async transactionObj => { + const referenceMosaicId = + '0000000000000000' === transactionObj.referenceMosaicId.toHex() + ? transactionObj.mosaicId + : transactionObj.referenceMosaicId; + const mosaicAliasNames = await helper.getMosaicAliasNames(referenceMosaicId); + + return { + ...transactionObj, + transactionBody: { + transactionType: transactionObj.type, + referenceMosaicId: referenceMosaicId.toHex(), + mosaicAliasNames, + restrictionKey: transactionObj.restrictionKey.toHex(), + previousRestrictionType: + Constants.MosaicRestrictionType[ + transactionObj.previousRestrictionType + ], + previousRestrictionValue: + transactionObj.previousRestrictionValue.compact(), + newRestrictionType: + Constants.MosaicRestrictionType[transactionObj.newRestrictionType], + newRestrictionValue: transactionObj.newRestrictionValue.compact() + } + }; + }; + + static accountMetadata = async transactionObj => { + const { transactionInfo } = transactionObj; + const resolvedAddress = await helper.resolvedAddress( + transactionObj.targetAddress, + transactionInfo.height + ); + + return { + ...transactionObj, + transactionBody: { + transactionType: transactionObj.type, + scopedMetadataKey: transactionObj.scopedMetadataKey.toHex(), + targetAddress: resolvedAddress, + metadataValue: `${Convert.uint8ToHex(transactionObj.value)} (Text: ${Convert.uint8ToUtf8(transactionObj.value)})`, + valueSizeDelta: transactionObj.valueSizeDelta + } + }; + }; + + static mosaicMetadata = async transactionObj => { + const { transactionInfo } = transactionObj; + const [resolvedMosaic, resolvedAddress] = await Promise.all([ + helper.resolveMosaicId(transactionObj.targetMosaicId), + helper.resolvedAddress( + transactionObj.targetAddress, + transactionInfo.height + ) + ]); + + const mosaicAliasNames = await helper.getMosaicAliasNames(resolvedMosaic); + + return { + ...transactionObj, + transactionBody: { + transactionType: transactionObj.type, + scopedMetadataKey: transactionObj.scopedMetadataKey.toHex(), + targetMosaicId: resolvedMosaic.toHex(), + targetMosaicAliasNames: mosaicAliasNames, + targetAddress: resolvedAddress, + metadataValue: `${Convert.uint8ToHex(transactionObj.value)} (Text: ${Convert.uint8ToUtf8(transactionObj.value)})`, + valueSizeDelta: transactionObj.valueSizeDelta + } + }; + }; + + static namespaceMetadata = async transactionObj => { + const { transactionInfo } = transactionObj; + const [namespaceName, resolvedAddress] = await Promise.all([ + NamespaceService.getNamespacesNames([transactionObj.targetNamespaceId]), + helper.resolvedAddress( + transactionObj.targetAddress, + transactionInfo.height + ) + ]); + + return { + ...transactionObj, + transactionBody: { + transactionType: transactionObj.type, + scopedMetadataKey: transactionObj.scopedMetadataKey.toHex(), + targetNamespaceId: transactionObj.targetNamespaceId.toHex(), + namespaceName: namespaceName, + targetAddress: resolvedAddress, + metadataValue: `${Convert.uint8ToHex(transactionObj.value)} (Text: ${Convert.uint8ToUtf8(transactionObj.value)})`, + valueSizeDelta: transactionObj.valueSizeDelta + } + }; + }; + + static votingKeyLink = transactionObj => { + return { + ...transactionObj, + transactionBody: { + linkAction: Constants.LinkAction[transactionObj.linkAction], + linkedPublicKey: transactionObj.linkedPublicKey, + linkedAccountAddress: Address.createFromPublicKey( + transactionObj.linkedPublicKey, + http.networkType + ).plain(), + startEpoch: transactionObj.startEpoch, + endEpoch: transactionObj.endEpoch + } + }; + }; + + static vrfKeyLink = transactionObj => { + return { + ...transactionObj, + transactionBody: { + transactionType: transactionObj.type, + linkAction: Constants.LinkAction[transactionObj.linkAction], + linkedPublicKey: transactionObj.linkedPublicKey, + linkedAccountAddress: Address.createFromPublicKey( + transactionObj.linkedPublicKey, + http.networkType + ).plain() + } + }; + }; + + static nodeKeyLink = transactionObj => { + return { + ...transactionObj, + transactionBody: { + transactionType: transactionObj.type, + linkAction: Constants.LinkAction[transactionObj.linkAction], + linkedPublicKey: transactionObj.linkedPublicKey, + linkedAccountAddress: Address.createFromPublicKey( + transactionObj.linkedPublicKey, + http.networkType + ).plain() + } + }; + }; + + static accountKeyLink = transactionObj => { + return { + ...transactionObj, + transactionBody: { + transactionType: transactionObj.type, + linkAction: Constants.LinkAction[transactionObj.linkAction], + linkedPublicKey: transactionObj.linkedPublicKey, + linkedAccountAddress: Address.createFromPublicKey( + transactionObj.linkedPublicKey, + http.networkType + ).plain() + } + }; + }; +} export default CreateTransaction; diff --git a/src/infrastructure/DataService.js b/src/infrastructure/DataService.js index 1cd5cf031..f38db5be0 100644 --- a/src/infrastructure/DataService.js +++ b/src/infrastructure/DataService.js @@ -20,47 +20,51 @@ import http from './http'; import axios from 'axios'; class DataService { - /** - * Gets cryptocurrency market price from https://min-api.cryptocompare.com/ - * @param {string} cryptocurrency - name such as XEM, BTC - * @returns {object} Object of data - */ - static getMarketPrice = cryptocurrency => { - return new Promise((resolve, reject) => { - let url = http.marketDataUrl + `data/pricemultifull?fsyms=${cryptocurrency}&tsyms=USD`; + /** + * Gets cryptocurrency market price from https://min-api.cryptocompare.com/ + * @param {string} cryptocurrency - name such as XEM, BTC + * @returns {object} Object of data + */ + static getMarketPrice = cryptocurrency => { + return new Promise((resolve, reject) => { + let url = + http.marketDataUrl + + `data/pricemultifull?fsyms=${cryptocurrency}&tsyms=USD`; - axios - .get(url) - .then(res => { - return resolve(res.data.DISPLAY); - }) - .catch(error => { - // reject(new Error('Fail to request XEM price.')) - reject(new Error(error)); - }); - }); - } + axios + .get(url) + .then(res => { + return resolve(res.data.DISPLAY); + }) + .catch(error => { + // reject(new Error('Fail to request XEM price.')) + reject(new Error(error)); + }); + }); + }; - /** - * Gets cryptocurrency historical hourly graph from https://min-api.cryptocompare.com/ - * @param {string} cryptocurrency - name such as XEM, BTC - * @returns {array} Array of Data - */ - static getHistoricalHourlyGraph = cryptocurrency => { - return new Promise((resolve, reject) => { - let url = http.marketDataUrl + `data/histohour?fsym=${cryptocurrency}&tsym=USD&limit=168`; + /** + * Gets cryptocurrency historical hourly graph from https://min-api.cryptocompare.com/ + * @param {string} cryptocurrency - name such as XEM, BTC + * @returns {array} Array of Data + */ + static getHistoricalHourlyGraph = cryptocurrency => { + return new Promise((resolve, reject) => { + let url = + http.marketDataUrl + + `data/histohour?fsym=${cryptocurrency}&tsym=USD&limit=168`; - axios - .get(url) - .then(res => { - return resolve(res.data); - }) - .catch(error => { - // reject(new Error('Fail to request Xem historical hourly graph.')) - reject(new Error(error)); - }); - }); - } + axios + .get(url) + .then(res => { + return resolve(res.data); + }) + .catch(error => { + // reject(new Error('Fail to request Xem historical hourly graph.')) + reject(new Error(error)); + }); + }); + }; } export default DataService; diff --git a/src/infrastructure/FinalizationService.js b/src/infrastructure/FinalizationService.js index 60779f670..5a9d02aef 100644 --- a/src/infrastructure/FinalizationService.js +++ b/src/infrastructure/FinalizationService.js @@ -19,21 +19,22 @@ import http from './http'; class FinalizationService { - /** - * Gets finalization proof for a given epoch. - * @param {number} epoch - epoch number. - * @returns {object} formatted finalization proof. - */ - static getFinalizationProofAtEpoch = async epoch => { - const finalizationProof = await http.createRepositoryFactory.createFinalizationRepository() - .getFinalizationProofAtEpoch(epoch) - .toPromise(); + /** + * Gets finalization proof for a given epoch. + * @param {number} epoch - epoch number. + * @returns {object} formatted finalization proof. + */ + static getFinalizationProofAtEpoch = async epoch => { + const finalizationProof = await http.createRepositoryFactory + .createFinalizationRepository() + .getFinalizationProofAtEpoch(epoch) + .toPromise(); - return { - ...finalizationProof, - height: finalizationProof.height.compact() - }; - } + return { + ...finalizationProof, + height: finalizationProof.height.compact() + }; + }; } export default FinalizationService; diff --git a/src/infrastructure/ListenerService.js b/src/infrastructure/ListenerService.js index 242e4bf2e..64572a701 100644 --- a/src/infrastructure/ListenerService.js +++ b/src/infrastructure/ListenerService.js @@ -19,28 +19,31 @@ import http from './http'; import { Listener } from 'symbol-sdk'; class ListenerService { - /** - * Subscribe to new blocks announced to the chain. - * @param {function} onAdd - Getters function - * @param {string} wssEndpoint - WSS endpoint in string format. - * @returns {array} Array object [Listener, Subscription] - */ - static subscribeNewBlock = async (onAdd, wssEndpoint) => { - const namespaceRepository = http.createRepositoryFactory.createNamespaceRepository(); - const customWssEndpoint = `${wssEndpoint}/ws`; + /** + * Subscribe to new blocks announced to the chain. + * @param {function} onAdd - Getters function + * @param {string} wssEndpoint - WSS endpoint in string format. + * @returns {array} Array object [Listener, Subscription] + */ + static subscribeNewBlock = async (onAdd, wssEndpoint) => { + const namespaceRepository = + http.createRepositoryFactory.createNamespaceRepository(); + const customWssEndpoint = `${wssEndpoint}/ws`; - const listener = new Listener(customWssEndpoint, namespaceRepository, WebSocket); + const listener = new Listener( + customWssEndpoint, + namespaceRepository, + WebSocket + ); - await listener.open(); - let subscription = listener - .newBlock() - .subscribe( - block => onAdd(block), - err => console.error(err) - ); + await listener.open(); + let subscription = listener.newBlock().subscribe( + block => onAdd(block), + err => console.error(err) + ); - return [listener, subscription]; - } + return [listener, subscription]; + }; } export default ListenerService; diff --git a/src/infrastructure/LockService.js b/src/infrastructure/LockService.js index 20c708372..5e12ad6e3 100644 --- a/src/infrastructure/LockService.js +++ b/src/infrastructure/LockService.js @@ -20,76 +20,81 @@ import http from './http'; import { Constants } from '../config'; class LockService { - /** - * Gets a hash lock from searchCriteria. - * @param {object} hashLockSearchCriteria Search Criteria. - * @returns {object} formatted hash lock data with pagination info. - */ - static searchHashLocks = async hashLockSearchCriteria => { - const searchHashLocks = await http.createRepositoryFactory.createHashLockRepository() - .search(hashLockSearchCriteria) - .toPromise(); + /** + * Gets a hash lock from searchCriteria. + * @param {object} hashLockSearchCriteria Search Criteria. + * @returns {object} formatted hash lock data with pagination info. + */ + static searchHashLocks = async hashLockSearchCriteria => { + const searchHashLocks = await http.createRepositoryFactory + .createHashLockRepository() + .search(hashLockSearchCriteria) + .toPromise(); - return { - ...searchHashLocks, - data: searchHashLocks.data.map(hashLock => this.formatHashLockInfo(hashLock)) - }; - } + return { + ...searchHashLocks, + data: searchHashLocks.data.map(hashLock => + this.formatHashLockInfo(hashLock)) + }; + }; - /** - * Gets hash lock from hash. - * @param {string} hash Transaction hash. - * @returns {object}formatted Hash lock info. - */ - static getHashLock = async hash => { - const hashLock = await http.createRepositoryFactory.createHashLockRepository() - .getHashLock(hash) - .toPromise(); + /** + * Gets hash lock from hash. + * @param {string} hash Transaction hash. + * @returns {object}formatted Hash lock info. + */ + static getHashLock = async hash => { + const hashLock = await http.createRepositoryFactory + .createHashLockRepository() + .getHashLock(hash) + .toPromise(); - return this.formatHashLockInfo(hashLock); - } + return this.formatHashLockInfo(hashLock); + }; - /** - * Gets a secret lock from searchCriteria. - * @param {object} secretLockSearchCriteria Search Criteria. - * @returns {object} formatted secret lock data with pagination info. - */ - static searchSecretLocks = async secretLockSearchCriteria => { - const searchSecretLocks = await http.createRepositoryFactory.createSecretLockRepository() - .search(secretLockSearchCriteria) - .toPromise(); + /** + * Gets a secret lock from searchCriteria. + * @param {object} secretLockSearchCriteria Search Criteria. + * @returns {object} formatted secret lock data with pagination info. + */ + static searchSecretLocks = async secretLockSearchCriteria => { + const searchSecretLocks = await http.createRepositoryFactory + .createSecretLockRepository() + .search(secretLockSearchCriteria) + .toPromise(); - return { - ...searchSecretLocks, - data: searchSecretLocks.data.map(hashLock => this.formatSecretLockInfo(hashLock)) - }; - } + return { + ...searchSecretLocks, + data: searchSecretLocks.data.map(hashLock => + this.formatSecretLockInfo(hashLock)) + }; + }; - /** - * Format secretLockInfoDTO. - * @param {object} secretLockInfo secretLockInfoDTO. - * @returns {object} readable secretLockInfoDTO object. - */ - static formatSecretLockInfo = secretLockInfo => ({ - ...secretLockInfo, - status: Constants.LockStatusType[secretLockInfo.status], - endHeight: Number(secretLockInfo.endHeight.toString()), - ownerAddress: secretLockInfo.ownerAddress.plain(), - recipient: secretLockInfo.recipientAddress.plain(), - hashAlgorithm: Constants.LockHashAlgorithm[secretLockInfo.hashAlgorithm] - }) + /** + * Format secretLockInfoDTO. + * @param {object} secretLockInfo secretLockInfoDTO. + * @returns {object} readable secretLockInfoDTO object. + */ + static formatSecretLockInfo = secretLockInfo => ({ + ...secretLockInfo, + status: Constants.LockStatusType[secretLockInfo.status], + endHeight: Number(secretLockInfo.endHeight.toString()), + ownerAddress: secretLockInfo.ownerAddress.plain(), + recipient: secretLockInfo.recipientAddress.plain(), + hashAlgorithm: Constants.LockHashAlgorithm[secretLockInfo.hashAlgorithm] + }); - /** - * Format HashLockInfoDTO - * @param {object} hashLockInfo hashLockInfoDTO. - * @returns {object} readable HashLockInfoDTO object - */ - static formatHashLockInfo = hashLockInfo => ({ - ...hashLockInfo, - status: Constants.LockStatusType[hashLockInfo.status], - endHeight: Number(hashLockInfo.endHeight.toString()), - ownerAddress: hashLockInfo.ownerAddress.plain() - }) + /** + * Format HashLockInfoDTO + * @param {object} hashLockInfo hashLockInfoDTO. + * @returns {object} readable HashLockInfoDTO object + */ + static formatHashLockInfo = hashLockInfo => ({ + ...hashLockInfo, + status: Constants.LockStatusType[hashLockInfo.status], + endHeight: Number(hashLockInfo.endHeight.toString()), + ownerAddress: hashLockInfo.ownerAddress.plain() + }); } export default LockService; diff --git a/src/infrastructure/MetadataService.js b/src/infrastructure/MetadataService.js index 17efc3451..08dc28638 100644 --- a/src/infrastructure/MetadataService.js +++ b/src/infrastructure/MetadataService.js @@ -20,46 +20,49 @@ import http from './http'; import Constants from '../config/constants'; class MetadataService { - /** - * Gets a metadata from searchCriteria. - * @param {object} metadataSearchCriteria Search Criteria. - * @returns {object} formatted metadatas with pagination info. - */ - static searchMetadatas = async metadataSearchCriteria => { - const searchMetadatas = await http.createRepositoryFactory.createMetadataRepository() - .search(metadataSearchCriteria) - .toPromise(); + /** + * Gets a metadata from searchCriteria. + * @param {object} metadataSearchCriteria Search Criteria. + * @returns {object} formatted metadatas with pagination info. + */ + static searchMetadatas = async metadataSearchCriteria => { + const searchMetadatas = await http.createRepositoryFactory + .createMetadataRepository() + .search(metadataSearchCriteria) + .toPromise(); - return { - ...searchMetadatas, - data: searchMetadatas.data.map(metadata => this.formatMetadata(metadata)) - }; - } + return { + ...searchMetadatas, + data: searchMetadatas.data.map(metadata => this.formatMetadata(metadata)) + }; + }; - /** - * Format Metadata to readable object. - * @param {object} metadata - metadata DTO. - * @returns {object} readable Metadata object. - */ - static formatMetadata = metadata => ({ - metadataId: metadata.id, - ...this.formatMetadataEntry(metadata.metadataEntry) - }) + /** + * Format Metadata to readable object. + * @param {object} metadata - metadata DTO. + * @returns {object} readable Metadata object. + */ + static formatMetadata = metadata => ({ + metadataId: metadata.id, + ...this.formatMetadataEntry(metadata.metadataEntry) + }); - /** - * Format MetadataEntry to readable object. - * @param {object} metadataEntry - metadataEntry DTO. - * @returns {object} readable metadataEntry object. - */ - static formatMetadataEntry = metadataEntry => ({ - ...metadataEntry, - scopedMetadataKey: metadataEntry.scopedMetadataKey.toHex(), - sourceAddress: metadataEntry.sourceAddress.plain(), - targetAddress: metadataEntry.targetAddress.plain(), - metadataType: Constants.MetadataType[metadataEntry.metadataType], - targetId: metadataEntry.targetId ? metadataEntry.targetId.toHex() : Constants.Message.UNAVAILABLE, - value: metadataEntry.value - }) + /** + * Format MetadataEntry to readable object. + * @param {object} metadataEntry - metadataEntry DTO. + * @returns {object} readable metadataEntry object. + */ + static formatMetadataEntry = metadataEntry => ({ + ...metadataEntry, + scopedMetadataKey: metadataEntry.scopedMetadataKey.toHex(), + sourceAddress: metadataEntry.sourceAddress.plain(), + targetAddress: metadataEntry.targetAddress.plain(), + metadataType: Constants.MetadataType[metadataEntry.metadataType], + targetId: metadataEntry.targetId + ? metadataEntry.targetId.toHex() + : Constants.Message.UNAVAILABLE, + value: metadataEntry.value + }); } export default MetadataService; diff --git a/src/infrastructure/MosaicService.js b/src/infrastructure/MosaicService.js index 77e15a026..ba330c388 100644 --- a/src/infrastructure/MosaicService.js +++ b/src/infrastructure/MosaicService.js @@ -19,285 +19,315 @@ import http from './http'; import { Constants } from '../config'; import helper from '../helper'; -import { NamespaceService, MetadataService, ReceiptService } from '../infrastructure'; +import { + NamespaceService, + MetadataService, + ReceiptService +} from '../infrastructure'; import { Address, MosaicId, Order, ReceiptType, UInt64 } from 'symbol-sdk'; class MosaicService { /** - * Gets MosaicInfo for different mosaicIds. - * @param {array} mosaicIds - Array of mosaic ids. - * @returns {array} Formatted MosaicInfos. - */ - static getMosaics = async mosaicIds => { - const mosaics = await http.createRepositoryFactory.createMosaicRepository() - .getMosaics(mosaicIds) - .toPromise(); - const formattedMosaics = mosaics.map(mosaic => this.formatMosaicInfo(mosaic)); - - return formattedMosaics; - } - - /** - * Gets the MosaicInfo for a given mosaicId. - * @param {object} mosaicId - Mosaic id - * @returns {object} Formatted MosaicInfo - */ - static getMosaic = async mosaicId => { - const mosaic = await http.createRepositoryFactory.createMosaicRepository() - .getMosaic(mosaicId) - .toPromise(); - - const formattedMosaic = this.formatMosaicInfo(mosaic); - - return formattedMosaic; - } - - /** - * Get balance mosaics in form of MosaicAmountViews for a given account address. - * @param {string} address - Account address - * @returns {array} formatted MosaicAmountViews - */ - static getMosaicAmountView = async address => { - const mosaicAmountViews = await http.mosaicService.mosaicsAmountViewFromAddress(Address.createFromRawAddress(address)).toPromise(); - - return mosaicAmountViews.map(mosaicAmountView => this.formatMosaicAmountView(mosaicAmountView)); - } - - /** - * Gets a mosaics list from searchCriteria. - * @param {object} mosaicSearchCriteria Search Criteria. - * @returns {object} formatted mosaic data with pagination info. - */ - static searchMosaics = async mosaicSearchCriteria => { - const searchMosaics = await http.createRepositoryFactory.createMosaicRepository() - .search(mosaicSearchCriteria) - .toPromise(); - - return { - ...searchMosaics, - data: searchMosaics.data.map(mosaic => this.formatMosaicInfo(mosaic)) - }; - } - - /** - * Get formatted MosaicInfo dataset into Vue Component. - * @param {string} hexOrNamespace - hex value or namespace name. - * @returns {object} MosaicInfo info object. - */ - static getMosaicInfo = async hexOrNamespace => { - const mosaicId = await helper.hexOrNamespaceToId(hexOrNamespace, 'mosaic'); - const mosaicInfo = await this.getMosaic(mosaicId); - - const mosaicNames = await NamespaceService.getMosaicsNames([mosaicId]); - - const expiredInBlock = mosaicInfo.duration + mosaicInfo.startHeight; - - return { - ...mosaicInfo, - mosaicAliasNames: this.extractMosaicNamespace(mosaicInfo, mosaicNames), - expiredInBlock: expiredInBlock === mosaicInfo.startHeight ? Constants.Message.INFINITY : expiredInBlock - }; - } - - /** - * Get custom MosaicInfo dataset into Vue Component. - * @param {object} pageInfo - pagination info. - * @returns {object} Custom MosaicInfos - */ - static getMosaicList = async pageInfo => { - const { pageNumber, pageSize } = pageInfo; - const searchCriteria = { - pageNumber, - pageSize, - order: Order.Desc - }; - - const mosaicInfos = await this.searchMosaics(searchCriteria); - - const mosaicIdsList = mosaicInfos.data.map(mosaicInfo => new MosaicId(mosaicInfo.mosaicId)); - - const mosaicNames = await NamespaceService.getMosaicsNames(mosaicIdsList); - - return { - ...mosaicInfos, - data: mosaicInfos.data.map(mosaic => ({ - ...mosaic, - ownerAddress: mosaic.address, - mosaicAliasNames: this.extractMosaicNamespace(mosaic, mosaicNames), - mosaicFlags: { - supplyMutable: mosaic.supplyMutable, - transferable: mosaic.transferable, - restrictable: mosaic.restrictable, - revokable: mosaic.revokable - } - })) - }; - } - - /** - * Get customize MosaicAmountView dataset for Vue component. - * @param {string} address - Account address. - * @returns {object} customize MosaicAmountViews. - */ - static getMosaicAmountViewList = async address => { - const mosaicAmountViewInfos = await this.getMosaicAmountView(address); - - const mosaicIdsList = mosaicAmountViewInfos.map(mosaicAmountViewInfo => new MosaicId(mosaicAmountViewInfo.mosaicId)); - const mosaicNames = await NamespaceService.getMosaicsNames(mosaicIdsList); - - return mosaicAmountViewInfos.map(mosaicAmountViewInfo => ({ - ...mosaicAmountViewInfo, - mosaicAliasNames: this.extractMosaicNamespace(mosaicAmountViewInfo, mosaicNames) - })); - } - - /** - * Gets mosaic Metadata list dataset into Vue component. - * @param {object} pageInfo - page info such as pageNumber, pageSize. - * @param {object} filterValue - search criteria. - * @param {string} hexOrNamespace - hex value or namespace name. - * @returns {object} formatted mosaic Metadata list. - */ - static getMosaicMetadataList = async (pageInfo, filterValue, hexOrNamespace) => { - const mosaicId = await helper.hexOrNamespaceToId(hexOrNamespace, 'mosaic'); - - const { pageNumber, pageSize } = pageInfo; - - const searchCriteria = { - pageNumber, - pageSize, - order: Order.Desc, - targetId: mosaicId, - ...filterValue - }; - const mosaicMetadatas = await MetadataService.searchMetadatas(searchCriteria); - - return mosaicMetadatas; - } - - /** - * Gets mosaic balance transfer receipt list dataset into Vue component. - * @param {object} pageInfo - page info such as pageNumber, pageSize. - * @param {string} hexOrNamespace - hex value or namespace name. - * @returns {object} formatted balance transfer receipt list. - */ - static getMosaicBalanceTransferReceipt = async (pageInfo, hexOrNamespace) => { - const mosaicId = await helper.hexOrNamespaceToId(hexOrNamespace, 'mosaic'); - - const { startHeight, address } = await this.getMosaic(mosaicId); - - const { pageNumber, pageSize } = pageInfo; - - const searchCriteria = { - pageNumber, - pageSize, - order: Order.Desc, - height: UInt64.fromUint(startHeight), - receiptTypes: [ReceiptType.Mosaic_Rental_Fee], - senderAddress: Address.createFromRawAddress(address) - }; - - const balanceTransferReceipt = await ReceiptService.searchReceipts(searchCriteria); - - const formattedReceipt = await ReceiptService.createReceiptTransactionStatement(balanceTransferReceipt.data.balanceTransferStatement); - - return { - ...balanceTransferReceipt, - data: formattedReceipt.filter(receipt => - receipt.senderAddress === address && - receipt.type === ReceiptType.Mosaic_Rental_Fee) - }; - } - - /** - * Gets mosaic artifact expiry receipt list dataset into Vue component. - * @param {object} pageInfo - page info such as pageNumber, pageSize. - * @param {string} hexOrNamespace - hex value or namespace name. - * @returns {object} formatted artifact expiry receipt list. - */ - static getMosaicArtifactExpiryReceipt = async (pageInfo, hexOrNamespace) => { - const mosaicId = await helper.hexOrNamespaceToId(hexOrNamespace, 'mosaic'); - - const { startHeight, duration } = await this.getMosaic(mosaicId); - - const { pageNumber, pageSize } = pageInfo; - - // Returns empty, artifact expiry receipt does not exist when duration is infinity - if (0 === duration) { - return { - data: [], - pageNumber, - pageSize, - isLastPage: true - }; - } - - const endHeight = startHeight + duration; - - // Todo: Should filter with with ArtifactId rather than height. - // Bug: https://github.com/nemtech/catapult-rest/issues/517 - const searchCriteria = { - pageNumber, - pageSize, - order: Order.Desc, - height: UInt64.fromUint(endHeight), - receiptTypes: [ReceiptType.Mosaic_Expired] - }; - - const artifactExpiryReceipt = await ReceiptService.searchReceipts(searchCriteria); - const formattedReceipt = await ReceiptService.createReceiptTransactionStatement(artifactExpiryReceipt.data.artifactExpiryStatement); - - return { - ...artifactExpiryReceipt, - data: formattedReceipt.filter(receipt => receipt.type === ReceiptType.Mosaic_Expired) - }; - } - - /** - * Format MosaicInfo to readable mosaicInfo object. - * @param {object} mosaicInfo MosaicInfoDTO. - * @returns {object} readable MosaicInfoDTO object. - */ - static formatMosaicInfo = mosaicInfo => ({ - mosaicId: mosaicInfo.id.toHex(), - divisibility: mosaicInfo.divisibility, - address: mosaicInfo.ownerAddress.plain(), - supply: mosaicInfo.supply.compact().toLocaleString('en-US'), - relativeAmount: helper.formatMosaicAmountWithDivisibility(mosaicInfo.supply.compact(), mosaicInfo.divisibility), - revision: mosaicInfo.revision, - startHeight: Number(mosaicInfo.startHeight.toString()), - duration: Number(mosaicInfo.duration.toString()), - supplyMutable: mosaicInfo.flags.supplyMutable, - transferable: mosaicInfo.flags.transferable, - restrictable: mosaicInfo.flags.restrictable, - revokable: mosaicInfo.flags.revokable - }) - - /** - * format MosaicAmountView to readable object. - * @param {object} mosaicAmountView - mosaicAmountView DTO. - * @returns {object} formatted mosaicAmountView. - */ - static formatMosaicAmountView = mosaicAmountView => ({ - ...this.formatMosaicInfo(mosaicAmountView.mosaicInfo), - amount: helper.formatMosaicAmountWithDivisibility(mosaicAmountView.amount.compact(), mosaicAmountView.mosaicInfo.divisibility) - }) - - /** - * Extract Name for Mosaic. - * @param {object} mosaicInfo - mosaicInfo DTO. - * @param {array} mosaicNames - MosaicNames[]. - * @returns {array} mosaicNames. - */ - static extractMosaicNamespace = (mosaicInfo, mosaicNames) => { - const mosaicName = mosaicNames.find(name => name.mosaicId === mosaicInfo.mosaicId); - - const aliasNames = mosaicName.names.map(names => names.name); - - const names = 0 < aliasNames.length ? aliasNames : [Constants.Message.UNAVAILABLE]; - - return names; - } + * Gets MosaicInfo for different mosaicIds. + * @param {array} mosaicIds - Array of mosaic ids. + * @returns {array} Formatted MosaicInfos. + */ + static getMosaics = async mosaicIds => { + const mosaics = await http.createRepositoryFactory + .createMosaicRepository() + .getMosaics(mosaicIds) + .toPromise(); + const formattedMosaics = mosaics.map(mosaic => + this.formatMosaicInfo(mosaic)); + + return formattedMosaics; + }; + + /** + * Gets the MosaicInfo for a given mosaicId. + * @param {object} mosaicId - Mosaic id + * @returns {object} Formatted MosaicInfo + */ + static getMosaic = async mosaicId => { + const mosaic = await http.createRepositoryFactory + .createMosaicRepository() + .getMosaic(mosaicId) + .toPromise(); + + const formattedMosaic = this.formatMosaicInfo(mosaic); + + return formattedMosaic; + }; + + /** + * Get balance mosaics in form of MosaicAmountViews for a given account address. + * @param {string} address - Account address + * @returns {array} formatted MosaicAmountViews + */ + static getMosaicAmountView = async address => { + const mosaicAmountViews = await http.mosaicService + .mosaicsAmountViewFromAddress(Address.createFromRawAddress(address)) + .toPromise(); + + return mosaicAmountViews.map(mosaicAmountView => + this.formatMosaicAmountView(mosaicAmountView)); + }; + + /** + * Gets a mosaics list from searchCriteria. + * @param {object} mosaicSearchCriteria Search Criteria. + * @returns {object} formatted mosaic data with pagination info. + */ + static searchMosaics = async mosaicSearchCriteria => { + const searchMosaics = await http.createRepositoryFactory + .createMosaicRepository() + .search(mosaicSearchCriteria) + .toPromise(); + + return { + ...searchMosaics, + data: searchMosaics.data.map(mosaic => this.formatMosaicInfo(mosaic)) + }; + }; + + /** + * Get formatted MosaicInfo dataset into Vue Component. + * @param {string} hexOrNamespace - hex value or namespace name. + * @returns {object} MosaicInfo info object. + */ + static getMosaicInfo = async hexOrNamespace => { + const mosaicId = await helper.hexOrNamespaceToId(hexOrNamespace, 'mosaic'); + const mosaicInfo = await this.getMosaic(mosaicId); + + const mosaicNames = await NamespaceService.getMosaicsNames([mosaicId]); + + const expiredInBlock = mosaicInfo.duration + mosaicInfo.startHeight; + + return { + ...mosaicInfo, + mosaicAliasNames: this.extractMosaicNamespace(mosaicInfo, mosaicNames), + expiredInBlock: + expiredInBlock === mosaicInfo.startHeight + ? Constants.Message.INFINITY + : expiredInBlock + }; + }; + + /** + * Get custom MosaicInfo dataset into Vue Component. + * @param {object} pageInfo - pagination info. + * @returns {object} Custom MosaicInfos + */ + static getMosaicList = async pageInfo => { + const { pageNumber, pageSize } = pageInfo; + const searchCriteria = { + pageNumber, + pageSize, + order: Order.Desc + }; + + const mosaicInfos = await this.searchMosaics(searchCriteria); + + const mosaicIdsList = mosaicInfos.data.map(mosaicInfo => new MosaicId(mosaicInfo.mosaicId)); + + const mosaicNames = await NamespaceService.getMosaicsNames(mosaicIdsList); + + return { + ...mosaicInfos, + data: mosaicInfos.data.map(mosaic => ({ + ...mosaic, + ownerAddress: mosaic.address, + mosaicAliasNames: this.extractMosaicNamespace(mosaic, mosaicNames), + mosaicFlags: { + supplyMutable: mosaic.supplyMutable, + transferable: mosaic.transferable, + restrictable: mosaic.restrictable, + revokable: mosaic.revokable + } + })) + }; + }; + + /** + * Get customize MosaicAmountView dataset for Vue component. + * @param {string} address - Account address. + * @returns {object} customize MosaicAmountViews. + */ + static getMosaicAmountViewList = async address => { + const mosaicAmountViewInfos = await this.getMosaicAmountView(address); + + const mosaicIdsList = mosaicAmountViewInfos.map(mosaicAmountViewInfo => new MosaicId(mosaicAmountViewInfo.mosaicId)); + const mosaicNames = await NamespaceService.getMosaicsNames(mosaicIdsList); + + return mosaicAmountViewInfos.map(mosaicAmountViewInfo => ({ + ...mosaicAmountViewInfo, + mosaicAliasNames: this.extractMosaicNamespace( + mosaicAmountViewInfo, + mosaicNames + ) + })); + }; + + /** + * Gets mosaic Metadata list dataset into Vue component. + * @param {object} pageInfo - page info such as pageNumber, pageSize. + * @param {object} filterValue - search criteria. + * @param {string} hexOrNamespace - hex value or namespace name. + * @returns {object} formatted mosaic Metadata list. + */ + static getMosaicMetadataList = async ( + pageInfo, + filterValue, + hexOrNamespace + ) => { + const mosaicId = await helper.hexOrNamespaceToId(hexOrNamespace, 'mosaic'); + + const { pageNumber, pageSize } = pageInfo; + + const searchCriteria = { + pageNumber, + pageSize, + order: Order.Desc, + targetId: mosaicId, + ...filterValue + }; + const mosaicMetadatas = await MetadataService.searchMetadatas(searchCriteria); + + return mosaicMetadatas; + }; + + /** + * Gets mosaic balance transfer receipt list dataset into Vue component. + * @param {object} pageInfo - page info such as pageNumber, pageSize. + * @param {string} hexOrNamespace - hex value or namespace name. + * @returns {object} formatted balance transfer receipt list. + */ + static getMosaicBalanceTransferReceipt = async (pageInfo, hexOrNamespace) => { + const mosaicId = await helper.hexOrNamespaceToId(hexOrNamespace, 'mosaic'); + + const { startHeight, address } = await this.getMosaic(mosaicId); + + const { pageNumber, pageSize } = pageInfo; + + const searchCriteria = { + pageNumber, + pageSize, + order: Order.Desc, + height: UInt64.fromUint(startHeight), + receiptTypes: [ReceiptType.Mosaic_Rental_Fee], + senderAddress: Address.createFromRawAddress(address) + }; + + const balanceTransferReceipt = await ReceiptService.searchReceipts(searchCriteria); + + const formattedReceipt = + await ReceiptService.createReceiptTransactionStatement(balanceTransferReceipt.data.balanceTransferStatement); + + return { + ...balanceTransferReceipt, + data: formattedReceipt.filter(receipt => + receipt.senderAddress === address && + receipt.type === ReceiptType.Mosaic_Rental_Fee) + }; + }; + + /** + * Gets mosaic artifact expiry receipt list dataset into Vue component. + * @param {object} pageInfo - page info such as pageNumber, pageSize. + * @param {string} hexOrNamespace - hex value or namespace name. + * @returns {object} formatted artifact expiry receipt list. + */ + static getMosaicArtifactExpiryReceipt = async (pageInfo, hexOrNamespace) => { + const mosaicId = await helper.hexOrNamespaceToId(hexOrNamespace, 'mosaic'); + + const { startHeight, duration } = await this.getMosaic(mosaicId); + + const { pageNumber, pageSize } = pageInfo; + + // Returns empty, artifact expiry receipt does not exist when duration is infinity + if (0 === duration) { + return { + data: [], + pageNumber, + pageSize, + isLastPage: true + }; + } + + const endHeight = startHeight + duration; + + // Todo: Should filter with with ArtifactId rather than height. + // Bug: https://github.com/nemtech/catapult-rest/issues/517 + const searchCriteria = { + pageNumber, + pageSize, + order: Order.Desc, + height: UInt64.fromUint(endHeight), + receiptTypes: [ReceiptType.Mosaic_Expired] + }; + + const artifactExpiryReceipt = await ReceiptService.searchReceipts(searchCriteria); + const formattedReceipt = + await ReceiptService.createReceiptTransactionStatement(artifactExpiryReceipt.data.artifactExpiryStatement); + + return { + ...artifactExpiryReceipt, + data: formattedReceipt.filter(receipt => receipt.type === ReceiptType.Mosaic_Expired) + }; + }; + + /** + * Format MosaicInfo to readable mosaicInfo object. + * @param {object} mosaicInfo MosaicInfoDTO. + * @returns {object} readable MosaicInfoDTO object. + */ + static formatMosaicInfo = mosaicInfo => ({ + mosaicId: mosaicInfo.id.toHex(), + divisibility: mosaicInfo.divisibility, + address: mosaicInfo.ownerAddress.plain(), + supply: mosaicInfo.supply.compact().toLocaleString('en-US'), + relativeAmount: helper.formatMosaicAmountWithDivisibility( + mosaicInfo.supply.compact(), + mosaicInfo.divisibility + ), + revision: mosaicInfo.revision, + startHeight: Number(mosaicInfo.startHeight.toString()), + duration: Number(mosaicInfo.duration.toString()), + supplyMutable: mosaicInfo.flags.supplyMutable, + transferable: mosaicInfo.flags.transferable, + restrictable: mosaicInfo.flags.restrictable, + revokable: mosaicInfo.flags.revokable + }); + + /** + * format MosaicAmountView to readable object. + * @param {object} mosaicAmountView - mosaicAmountView DTO. + * @returns {object} formatted mosaicAmountView. + */ + static formatMosaicAmountView = mosaicAmountView => ({ + ...this.formatMosaicInfo(mosaicAmountView.mosaicInfo), + amount: helper.formatMosaicAmountWithDivisibility( + mosaicAmountView.amount.compact(), + mosaicAmountView.mosaicInfo.divisibility + ) + }); + + /** + * Extract Name for Mosaic. + * @param {object} mosaicInfo - mosaicInfo DTO. + * @param {array} mosaicNames - MosaicNames[]. + * @returns {array} mosaicNames. + */ + static extractMosaicNamespace = (mosaicInfo, mosaicNames) => { + const mosaicName = mosaicNames.find(name => name.mosaicId === mosaicInfo.mosaicId); + + const aliasNames = mosaicName.names.map(names => names.name); + + const names = + 0 < aliasNames.length ? aliasNames : [Constants.Message.UNAVAILABLE]; + + return names; + }; } export default MosaicService; diff --git a/src/infrastructure/MultisigService.js b/src/infrastructure/MultisigService.js index 3055aef48..a63913295 100644 --- a/src/infrastructure/MultisigService.js +++ b/src/infrastructure/MultisigService.js @@ -20,55 +20,64 @@ import http from './http'; import { Address } from 'symbol-sdk'; class MultisigService { - /** - * Gets a MultisigAccountInfo for an account. - * @param {string} address - Account Address. - * @returns {object} MultisigAccountInfo. - */ - static getMultisigAccount = async address => { - let multisigAccountInfo; + /** + * Gets a MultisigAccountInfo for an account. + * @param {string} address - Account Address. + * @returns {object} MultisigAccountInfo. + */ + static getMultisigAccount = async address => { + let multisigAccountInfo; - try { - multisigAccountInfo = await http.createRepositoryFactory.createMultisigRepository() - .getMultisigAccountInfo(Address.createFromRawAddress(address)) - .toPromise(); - } catch (e) { - // To Catch statusCode 404 if Address is not a multisig account. - throw Error('Address is not a multisig account.'); - } + try { + multisigAccountInfo = await http.createRepositoryFactory + .createMultisigRepository() + .getMultisigAccountInfo(Address.createFromRawAddress(address)) + .toPromise(); + } catch (e) { + // To Catch statusCode 404 if Address is not a multisig account. + throw Error('Address is not a multisig account.'); + } - const formattedMultisigAccount = this.formatMultisigAccountInfo(multisigAccountInfo); + const formattedMultisigAccount = + this.formatMultisigAccountInfo(multisigAccountInfo); - return formattedMultisigAccount; - } + return formattedMultisigAccount; + }; - /** - * Get Customize MultisigAccountInfo for Vue component. - * @param {string} address - Account Address. - * @returns {object} customize MultisigAccountInfo. - */ - static getMultisigAccountInfo = async address => { - const multisigAccountInfo = await this.getMultisigAccount(address); + /** + * Get Customize MultisigAccountInfo for Vue component. + * @param {string} address - Account Address. + * @returns {object} customize MultisigAccountInfo. + */ + static getMultisigAccountInfo = async address => { + const multisigAccountInfo = await this.getMultisigAccount(address); - return { - ...multisigAccountInfo, - minApproval: 0 < multisigAccountInfo?.cosignatoryAddresses.length ? multisigAccountInfo.minApproval : null, - minRemoval: 0 < multisigAccountInfo?.cosignatoryAddresses.length ? multisigAccountInfo.minRemoval : null, - cosignatoryAddresses: multisigAccountInfo?.cosignatoryAddresses, - multisigAddresses: multisigAccountInfo?.multisigAddresses - }; - } + return { + ...multisigAccountInfo, + minApproval: + 0 < multisigAccountInfo?.cosignatoryAddresses.length + ? multisigAccountInfo.minApproval + : null, + minRemoval: + 0 < multisigAccountInfo?.cosignatoryAddresses.length + ? multisigAccountInfo.minRemoval + : null, + cosignatoryAddresses: multisigAccountInfo?.cosignatoryAddresses, + multisigAddresses: multisigAccountInfo?.multisigAddresses + }; + }; - /** - * Format multisigAccountInfo DTO to readable object. - * @param {object} multisigAccountInfo - multisigAccountInfo DTO. - * @returns {object} formatted multisigAccountInfo DTO. - */ - static formatMultisigAccountInfo = multisigAccountInfo => ({ - ...multisigAccountInfo, - cosignatoryAddresses: multisigAccountInfo.cosignatoryAddresses.map(address => address.plain()), - multisigAddresses: multisigAccountInfo.multisigAddresses.map(address => address.plain()) - }) + /** + * Format multisigAccountInfo DTO to readable object. + * @param {object} multisigAccountInfo - multisigAccountInfo DTO. + * @returns {object} formatted multisigAccountInfo DTO. + */ + static formatMultisigAccountInfo = multisigAccountInfo => ({ + ...multisigAccountInfo, + cosignatoryAddresses: multisigAccountInfo.cosignatoryAddresses.map(address => address.plain()), + multisigAddresses: multisigAccountInfo.multisigAddresses.map(address => + address.plain()) + }); } export default MultisigService; diff --git a/src/infrastructure/NamespaceService.js b/src/infrastructure/NamespaceService.js index 1b56b22eb..3683f7b89 100644 --- a/src/infrastructure/NamespaceService.js +++ b/src/infrastructure/NamespaceService.js @@ -20,429 +20,485 @@ import http from './http'; import Constants from '../config/constants'; import globalConfig from '../config/globalConfig'; import helper from '../helper'; -import { ChainService, MetadataService, ReceiptService } from '../infrastructure'; +import { + ChainService, + MetadataService, + ReceiptService +} from '../infrastructure'; import { Order, NamespaceId, UInt64, ReceiptType, Address } from 'symbol-sdk'; class NamespaceService { - /** - * Gets array of NamespaceName for different namespaceIds. - * @param {array} namespaceIds - Array of namespace ids. - * @returns {array} Formatted NamespaceName. - */ - static getNamespacesNames = async namespaceIds => { - let namespaceNames = await http.createRepositoryFactory.createNamespaceRepository() - .getNamespacesNames(namespaceIds) - .toPromise(); - - let formattedNamespacesName = namespaceNames.map(namespaceName => this.formatNamespaceName(namespaceName)); - - return formattedNamespacesName; - } - - /** - * Get readable names for a set of mosaics Returns friendly names for mosaics. - * @param {array} mosaicIds - Array of mosaic ids. - * @returns {array} MosaicNames. - */ - static getMosaicsNames = async mosaicIds => { - const mosaicNames = await http.createRepositoryFactory.createNamespaceRepository() - .getMosaicsNames(mosaicIds) - .toPromise(); - const formattedMosaicNames = mosaicNames.map(mosaicName => this.formatMosaicName(mosaicName)); - - return formattedMosaicNames; - } - - /** - * Returns friendly names for array of addresses. - * @param {array} addresses - Array of addresses. - * @returns {array} AccountNames. - */ - static getAccountsNames = async addresses => { - const accountNames = await http.createRepositoryFactory.createNamespaceRepository() - .getAccountsNames(addresses) - .toPromise(); - - const formattedAccountNames = accountNames.map(accountName => this.formatAccountName(accountName)); - - return formattedAccountNames; - } - - /** - * Get namespace info and name from namespace Id. - * @param {number} namespaceId - Namespace id. - * @returns {object} formatted namespace info and name. - */ - static getNamespace = async namespaceId => { - let namespace = await http.namespaceService.namespace(namespaceId).toPromise(); - - let formattedNamespace = this.formatNamespace(namespace); - - return formattedNamespace; - } - - /** - * Get linked address from namespace Id - * @param {object} namespaceId - Namespace id - * @returns {object} Address - */ - static getLinkedAddress = async namespaceId => { - const address = await http.createRepositoryFactory.createNamespaceRepository() - .getLinkedAddress(namespaceId) - .toPromise(); - - return address; - } - - /** - * Get linked mosaicId from namespace Id. - * @param {object} namespaceId - Namespace id - * @returns {object} mosaicId - */ - static getLinkedMosaicId = async namespaceId => { - const mosaicId = await http.createRepositoryFactory.createNamespaceRepository() - .getLinkedMosaicId(namespaceId) - .toPromise(); - - return mosaicId; - } - - /** - * Gets a namespace list from searchCriteria. - * @param {object} namespaceSearchCriteria Search Criteria - * @returns {object} formatted namespace data with pagination info - */ - static searchNamespaces = async namespaceSearchCriteria => { - const searchNamespaces = await http.createRepositoryFactory.createNamespaceRepository() - .search(namespaceSearchCriteria) - .toPromise(); - - // Convert NamespaceInfo[] to Namespace[] - const namespaces = await this.toNamespaces(searchNamespaces.data); - - return { - ...searchNamespaces, - data: namespaces.map(namespace => this.formatNamespace(namespace)) - }; - } - - /** - * Get namespace info for Vue Component. - * @param {string} hexOrNamespace - hex value or namespace name. - * @returns {object} customize namespace info Object. - */ - static getNamespaceInfo = async hexOrNamespace => { - const namespaceId = await helper.hexOrNamespaceToId(hexOrNamespace, 'namespace'); - - const namespace = await this.getNamespace(namespaceId); - const { height: currentHeight } = await ChainService.getChainInfo(); - - const { - isExpired, - expiredInBlock, - expiredInSecond - } = helper.calculateNamespaceExpiration(currentHeight, namespace.endHeight); - - let formattedNamespaceInfo = { - ...namespace, - duration: helper.convertTimeFromNowInSec(expiredInSecond) || Constants.Message.UNLIMITED, - status: isExpired ? Constants.Message.EXPIRED : Constants.Message.ACTIVE - }; - - // create alias props by alias type. - if (namespace.aliasType === Constants.Message.ADDRESS) - formattedNamespaceInfo.aliasAddress = namespace.alias; - - if (namespace.aliasType === Constants.Message.MOSAIC) - formattedNamespaceInfo.aliasMosaic = namespace.alias; - - // End height disable click before expired. - formattedNamespaceInfo.expiredInBlock = helper.isNativeNamespace(namespace.namespaceName) - ? Constants.Message.INFINITY - : expiredInBlock + ' ≈ ' + formattedNamespaceInfo.duration; - - if (isExpired) - delete formattedNamespaceInfo.expiredInBlock; - - if (currentHeight < formattedNamespaceInfo.endHeight) { - formattedNamespaceInfo.beforeEndHeight = helper.isNativeNamespace(namespace.namespaceName) - ? Constants.Message.INFINITY - : formattedNamespaceInfo.endHeight + ` ( ${http.networkConfig.NamespaceGraceDuration} blocks of grace period )`; - delete formattedNamespaceInfo.endHeight; - } - - return formattedNamespaceInfo; - } - - /** - * Gets NamespaceName list from Vue Component. - * @param {string} hexOrNamespace - hex value or namespace name. - * @returns {array} Namespace name list. - */ - static getNamespaceLevelList = async hexOrNamespace => { - let namespaceId = await helper.hexOrNamespaceToId(hexOrNamespace, 'namespace'); - - let namespacesName = await this.getNamespacesNames([namespaceId]); - - return namespacesName; - } - - /** - * Get customize NamespaceInfo dataset into Vue Component. - * @param {object} pageInfo - pagination info. - * @param {object} filterValue - search criteria. - * @returns {array} customize NamespaceInfo. - */ - static getNamespaceList = async (pageInfo, filterValue) => { - const { pageNumber, pageSize } = pageInfo; - const searchCriteria = { - pageNumber, - pageSize, - order: Order.Desc, - ...filterValue - }; - - const namespaceInfos = await this.searchNamespaces(searchCriteria); - const { height: currentHeight } = await ChainService.getChainInfo(); - - return { - ...namespaceInfos, - data: namespaceInfos.data.map(namespace => { - const { isExpired, expiredInSecond, expiredInBlock } - = helper.calculateNamespaceExpiration(currentHeight, namespace.endHeight); - - return { - ...namespace, - isExpired: isExpired, - approximateExpired: helper.isNativeNamespace(namespace.namespaceName) - ? Constants.Message.INFINITY - : helper.convertSecondToDate(expiredInSecond), - expiredInBlock: expiredInBlock - }; - }) - }; - } - - /** - * Gets namespace metadata list dataset into Vue component. - * @param {object} pageInfo - page info such as pageNumber, pageSize. - * @param {object} filterValue - search criteria. - * @param {string} hexOrNamespace - hex value or namespace name. - * @returns {array} formatted namespace Metadata list - */ - static getNamespaceMetadataList = async (pageInfo, filterValue, hexOrNamespace) => { - const namespaceId = await helper.hexOrNamespaceToId(hexOrNamespace, 'namespace'); - - const { pageNumber, pageSize } = pageInfo; - - const searchCriteria = { - pageNumber, - pageSize, - order: Order.Desc, - targetId: namespaceId, - ...filterValue - }; - const namespaceMetadatas = await MetadataService.searchMetadatas(searchCriteria); - - return namespaceMetadatas; - } - - /** - * Gets namespace balance transfer receipt list dataset into Vue component. - * @param {object} pageInfo - page info such as pageNumber, pageSize. - * @param {string} hexOrNamespace - hex value or namespace name. - * @returns {array} formatted balance transfer receipt list. - */ - static getNamespaceBalanceTransferReceipt = async (pageInfo, hexOrNamespace) => { - const namespaceId = await helper.hexOrNamespaceToId(hexOrNamespace, 'namespace'); - - const { ownerAddress, startHeight } = await this.getNamespace(namespaceId); - - const { pageNumber, pageSize } = pageInfo; - - const searchCriteria = { - pageNumber, - pageSize, - order: Order.Desc, - height: UInt64.fromUint(startHeight), - receiptTypes: [ReceiptType.Namespace_Rental_Fee], - senderAddress: Address.createFromRawAddress(ownerAddress) - }; - - const balanceTransferReceipt = await ReceiptService.searchReceipts(searchCriteria); - - const formattedReceipt = await ReceiptService.createReceiptTransactionStatement(balanceTransferReceipt.data.balanceTransferStatement); - - return { - ...balanceTransferReceipt, - data: formattedReceipt.filter(receipt => - receipt.senderAddress === ownerAddress && - receipt.type === ReceiptType.Namespace_Rental_Fee) - }; - } - - /** - * Gets namespace artifact expiry receipt list dataset into Vue component. - * @param {object} pageInfo - page info such as pageNumber, pageSize. - * @param {string} hexOrNamespace - hex value or namespace name. - * @returns {array} formatted artifact expiry receipt list. - */ - static getNamespaceArtifactExpiryReceipt = async (pageInfo, hexOrNamespace) => { - const namespaceId = await helper.hexOrNamespaceToId(hexOrNamespace, 'namespace'); - - const { endHeight } = await this.getNamespace(namespaceId); - - const { pageNumber, pageSize } = pageInfo; - - // Todo: Should filter with with ArtifactId rather than height. - // Bug: https://github.com/nemtech/catapult-rest/issues/517 - const searchCriteria = { - pageNumber, - pageSize, - order: Order.Desc, - height: UInt64.fromUint(endHeight), - receiptTypes: [ReceiptType.Namespace_Expired, ReceiptType.Namespace_Deleted] - }; - - const artifactExpiryReceipt = await ReceiptService.searchReceipts(searchCriteria); - const formattedReceipt = await ReceiptService.createReceiptTransactionStatement(artifactExpiryReceipt.data.artifactExpiryStatement); - - return { - ...artifactExpiryReceipt, - data: formattedReceipt.filter(receipt => receipt.type === ReceiptType.Namespace_Expired - || receipt.type === ReceiptType.Namespace_Deleted) - }; - } - - /** - * Convert NamespaceInfo[] to Namespace[]. - * @param {array} namespaceInfos - array of NamespaceInfo. - * @returns {array} Namespaces. - */ - static toNamespaces = async namespaceInfos => { - const namespaceIdsList = namespaceInfos.map(namespaceInfo => namespaceInfo.id); - const namespaceNames = await this.getNamespacesNames(namespaceIdsList); - - return namespaceInfos.map(namespaceInfo => ({ - ...namespaceInfo, - id: namespaceInfo.id, - name: this.extractFullNamespace(namespaceInfo, namespaceNames) - })); - } - - /** - * Gets native namespaces name in from nemesis transactions. - * @returns {array} Namespaces - */ - static getNativeNamespaces = async () => { - const namespace = globalConfig.networkConfig.namespaceName; - const rootNamespace = namespace.split('.')[0]; - const namespaceids = [rootNamespace, namespace].map(namespace => new NamespaceId(namespace)); - - const namespaceInfos = await Promise.all(namespaceids.map(async namespaceid => { - return (NamespaceService.getNamespace(namespaceid)); - })); - - return namespaceInfos; - } - - /** - * Format namespaceName to readable object. - * @param {object} namespaceName namespaceNameDTO - * @returns {object} readable namespaceNameDTO. - */ - static formatNamespaceName = namespaceName => ({ - ...namespaceName, - namespaceId: namespaceName.namespaceId.toHex(), - parentId: namespaceName?.parentId ? namespaceName.parentId.toHex() : Constants.Message.UNAVAILABLE - }) - - /** - * Format alias to readable object. - * @param {string} alias alias type. - * @returns {object} readable Alias object. - */ - static formatAlias = alias => { - switch (alias.type) { - case 0: - return { - ...alias, - aliasType: Constants.Message.UNAVAILABLE - }; - case 1: // Mosaic id alias - return { - ...alias, - aliasType: Constants.Message.MOSAIC, - alias: alias?.mosaicId.toHex() - }; - case 2: // Address alias - return { - ...alias, - aliasType: Constants.Message.ADDRESS, - alias: alias?.address.plain() - }; - } - } - - /** - * Format namespace to readable object. - * @param {object} namespace - namespace DTO. - * @returns {object} readable namespaceDTO. - */ - static formatNamespace = namespace => ({ - ...namespace, - ownerAddress: namespace.ownerAddress.plain(), - namespaceName: namespace.name, - namespaceId: namespace.id.toHex(), - registrationType: Constants.NamespaceRegistrationType[namespace.registrationType], - startHeight: Number(namespace.startHeight.toString()), - endHeight: helper.isNativeNamespace(namespace.name) - ? Constants.Message.INFINITY - : Number(namespace.endHeight.toString()), - active: namespace.active ? Constants.Message.ACTIVE : Constants.Message.INACTIVE, - ...this.formatAlias(namespace.alias), - parentName: 0 !== namespace.registrationType ? namespace.name.split('.')[0] : '', - levels: namespace.levels - }) - - /** - * Format mosaic name to readable object. - * @param {object} mosaicName - mosaicName DTO. - * @returns {object} readable mosaicName DTO. - */ - static formatMosaicName = mosaicName => ({ - ...mosaicName, - mosaicId: mosaicName.mosaicId.toHex() - }) - - /** - * Format account name to readable object. - * @param {object} accountName - accountName DTO. - * @returns {object} readable accountName DTO. - */ - static formatAccountName = accountName => ({ - ...accountName, - address: accountName.address.plain(), - names: accountName.names.map(name => this.formatNamespaceName(name)) - }) - - /** - * Extract full name for Namespace. - * @param {object} namespaceInfo - namespaceInfo DTO. - * @param {array} namespaceNames - NamespaceNames. - * @returns {string} full name. - */ - static extractFullNamespace = (namespaceInfo, namespaceNames) => { - return namespaceInfo.levels.map(level => { - const namespaceName = namespaceNames.find(name => name.namespaceId === level.toHex()); - - if (namespaceName === undefined) - throw new Error('Not found'); - return namespaceName; - }) - .map(namespaceName => namespaceName.name) - .join('.'); - } + /** + * Gets array of NamespaceName for different namespaceIds. + * @param {array} namespaceIds - Array of namespace ids. + * @returns {array} Formatted NamespaceName. + */ + static getNamespacesNames = async namespaceIds => { + let namespaceNames = await http.createRepositoryFactory + .createNamespaceRepository() + .getNamespacesNames(namespaceIds) + .toPromise(); + + let formattedNamespacesName = namespaceNames.map(namespaceName => + this.formatNamespaceName(namespaceName)); + + return formattedNamespacesName; + }; + + /** + * Get readable names for a set of mosaics Returns friendly names for mosaics. + * @param {array} mosaicIds - Array of mosaic ids. + * @returns {array} MosaicNames. + */ + static getMosaicsNames = async mosaicIds => { + const mosaicNames = await http.createRepositoryFactory + .createNamespaceRepository() + .getMosaicsNames(mosaicIds) + .toPromise(); + const formattedMosaicNames = mosaicNames.map(mosaicName => + this.formatMosaicName(mosaicName)); + + return formattedMosaicNames; + }; + + /** + * Returns friendly names for array of addresses. + * @param {array} addresses - Array of addresses. + * @returns {array} AccountNames. + */ + static getAccountsNames = async addresses => { + const accountNames = await http.createRepositoryFactory + .createNamespaceRepository() + .getAccountsNames(addresses) + .toPromise(); + + const formattedAccountNames = accountNames.map(accountName => + this.formatAccountName(accountName)); + + return formattedAccountNames; + }; + + /** + * Get namespace info and name from namespace Id. + * @param {number} namespaceId - Namespace id. + * @returns {object} formatted namespace info and name. + */ + static getNamespace = async namespaceId => { + let namespace = await http.namespaceService + .namespace(namespaceId) + .toPromise(); + + let formattedNamespace = this.formatNamespace(namespace); + + return formattedNamespace; + }; + + /** + * Get linked address from namespace Id + * @param {object} namespaceId - Namespace id + * @returns {object} Address + */ + static getLinkedAddress = async namespaceId => { + const address = await http.createRepositoryFactory + .createNamespaceRepository() + .getLinkedAddress(namespaceId) + .toPromise(); + + return address; + }; + + /** + * Get linked mosaicId from namespace Id. + * @param {object} namespaceId - Namespace id + * @returns {object} mosaicId + */ + static getLinkedMosaicId = async namespaceId => { + const mosaicId = await http.createRepositoryFactory + .createNamespaceRepository() + .getLinkedMosaicId(namespaceId) + .toPromise(); + + return mosaicId; + }; + + /** + * Gets a namespace list from searchCriteria. + * @param {object} namespaceSearchCriteria Search Criteria + * @returns {object} formatted namespace data with pagination info + */ + static searchNamespaces = async namespaceSearchCriteria => { + const searchNamespaces = await http.createRepositoryFactory + .createNamespaceRepository() + .search(namespaceSearchCriteria) + .toPromise(); + + // Convert NamespaceInfo[] to Namespace[] + const namespaces = await this.toNamespaces(searchNamespaces.data); + + return { + ...searchNamespaces, + data: namespaces.map(namespace => this.formatNamespace(namespace)) + }; + }; + + /** + * Get namespace info for Vue Component. + * @param {string} hexOrNamespace - hex value or namespace name. + * @returns {object} customize namespace info Object. + */ + static getNamespaceInfo = async hexOrNamespace => { + const namespaceId = await helper.hexOrNamespaceToId( + hexOrNamespace, + 'namespace' + ); + + const namespace = await this.getNamespace(namespaceId); + const { height: currentHeight } = await ChainService.getChainInfo(); + + const { isExpired, expiredInBlock, expiredInSecond } = + helper.calculateNamespaceExpiration(currentHeight, namespace.endHeight); + + let formattedNamespaceInfo = { + ...namespace, + duration: + helper.convertTimeFromNowInSec(expiredInSecond) || + Constants.Message.UNLIMITED, + status: isExpired ? Constants.Message.EXPIRED : Constants.Message.ACTIVE + }; + + // create alias props by alias type. + if (namespace.aliasType === Constants.Message.ADDRESS) + formattedNamespaceInfo.aliasAddress = namespace.alias; + + if (namespace.aliasType === Constants.Message.MOSAIC) + formattedNamespaceInfo.aliasMosaic = namespace.alias; + + // End height disable click before expired. + formattedNamespaceInfo.expiredInBlock = helper.isNativeNamespace(namespace.namespaceName) + ? Constants.Message.INFINITY + : expiredInBlock + ' ≈ ' + formattedNamespaceInfo.duration; + + if (isExpired) + delete formattedNamespaceInfo.expiredInBlock; + + if (currentHeight < formattedNamespaceInfo.endHeight) { + formattedNamespaceInfo.beforeEndHeight = helper.isNativeNamespace(namespace.namespaceName) + ? Constants.Message.INFINITY + : formattedNamespaceInfo.endHeight + + ` ( ${http.networkConfig.NamespaceGraceDuration} blocks of grace period )`; + delete formattedNamespaceInfo.endHeight; + } + + return formattedNamespaceInfo; + }; + + /** + * Gets NamespaceName list from Vue Component. + * @param {string} hexOrNamespace - hex value or namespace name. + * @returns {array} Namespace name list. + */ + static getNamespaceLevelList = async hexOrNamespace => { + let namespaceId = await helper.hexOrNamespaceToId( + hexOrNamespace, + 'namespace' + ); + + let namespacesName = await this.getNamespacesNames([namespaceId]); + + return namespacesName; + }; + + /** + * Get customize NamespaceInfo dataset into Vue Component. + * @param {object} pageInfo - pagination info. + * @param {object} filterValue - search criteria. + * @returns {array} customize NamespaceInfo. + */ + static getNamespaceList = async (pageInfo, filterValue) => { + const { pageNumber, pageSize } = pageInfo; + const searchCriteria = { + pageNumber, + pageSize, + order: Order.Desc, + ...filterValue + }; + + const namespaceInfos = await this.searchNamespaces(searchCriteria); + const { height: currentHeight } = await ChainService.getChainInfo(); + + return { + ...namespaceInfos, + data: namespaceInfos.data.map(namespace => { + const { isExpired, expiredInSecond, expiredInBlock } = + helper.calculateNamespaceExpiration( + currentHeight, + namespace.endHeight + ); + + return { + ...namespace, + isExpired: isExpired, + approximateExpired: helper.isNativeNamespace(namespace.namespaceName) + ? Constants.Message.INFINITY + : helper.convertSecondToDate(expiredInSecond), + expiredInBlock: expiredInBlock + }; + }) + }; + }; + + /** + * Gets namespace metadata list dataset into Vue component. + * @param {object} pageInfo - page info such as pageNumber, pageSize. + * @param {object} filterValue - search criteria. + * @param {string} hexOrNamespace - hex value or namespace name. + * @returns {array} formatted namespace Metadata list + */ + static getNamespaceMetadataList = async ( + pageInfo, + filterValue, + hexOrNamespace + ) => { + const namespaceId = await helper.hexOrNamespaceToId( + hexOrNamespace, + 'namespace' + ); + + const { pageNumber, pageSize } = pageInfo; + + const searchCriteria = { + pageNumber, + pageSize, + order: Order.Desc, + targetId: namespaceId, + ...filterValue + }; + const namespaceMetadatas = await MetadataService.searchMetadatas(searchCriteria); + + return namespaceMetadatas; + }; + + /** + * Gets namespace balance transfer receipt list dataset into Vue component. + * @param {object} pageInfo - page info such as pageNumber, pageSize. + * @param {string} hexOrNamespace - hex value or namespace name. + * @returns {array} formatted balance transfer receipt list. + */ + static getNamespaceBalanceTransferReceipt = async ( + pageInfo, + hexOrNamespace + ) => { + const namespaceId = await helper.hexOrNamespaceToId( + hexOrNamespace, + 'namespace' + ); + + const { ownerAddress, startHeight } = await this.getNamespace(namespaceId); + + const { pageNumber, pageSize } = pageInfo; + + const searchCriteria = { + pageNumber, + pageSize, + order: Order.Desc, + height: UInt64.fromUint(startHeight), + receiptTypes: [ReceiptType.Namespace_Rental_Fee], + senderAddress: Address.createFromRawAddress(ownerAddress) + }; + + const balanceTransferReceipt = await ReceiptService.searchReceipts(searchCriteria); + + const formattedReceipt = + await ReceiptService.createReceiptTransactionStatement(balanceTransferReceipt.data.balanceTransferStatement); + + return { + ...balanceTransferReceipt, + data: formattedReceipt.filter(receipt => + receipt.senderAddress === ownerAddress && + receipt.type === ReceiptType.Namespace_Rental_Fee) + }; + }; + + /** + * Gets namespace artifact expiry receipt list dataset into Vue component. + * @param {object} pageInfo - page info such as pageNumber, pageSize. + * @param {string} hexOrNamespace - hex value or namespace name. + * @returns {array} formatted artifact expiry receipt list. + */ + static getNamespaceArtifactExpiryReceipt = async ( + pageInfo, + hexOrNamespace + ) => { + const namespaceId = await helper.hexOrNamespaceToId( + hexOrNamespace, + 'namespace' + ); + + const { endHeight } = await this.getNamespace(namespaceId); + + const { pageNumber, pageSize } = pageInfo; + + // Todo: Should filter with with ArtifactId rather than height. + // Bug: https://github.com/nemtech/catapult-rest/issues/517 + const searchCriteria = { + pageNumber, + pageSize, + order: Order.Desc, + height: UInt64.fromUint(endHeight), + receiptTypes: [ + ReceiptType.Namespace_Expired, + ReceiptType.Namespace_Deleted + ] + }; + + const artifactExpiryReceipt = await ReceiptService.searchReceipts(searchCriteria); + const formattedReceipt = + await ReceiptService.createReceiptTransactionStatement(artifactExpiryReceipt.data.artifactExpiryStatement); + + return { + ...artifactExpiryReceipt, + data: formattedReceipt.filter(receipt => + receipt.type === ReceiptType.Namespace_Expired || + receipt.type === ReceiptType.Namespace_Deleted) + }; + }; + + /** + * Convert NamespaceInfo[] to Namespace[]. + * @param {array} namespaceInfos - array of NamespaceInfo. + * @returns {array} Namespaces. + */ + static toNamespaces = async namespaceInfos => { + const namespaceIdsList = namespaceInfos.map(namespaceInfo => namespaceInfo.id); + const namespaceNames = await this.getNamespacesNames(namespaceIdsList); + + return namespaceInfos.map(namespaceInfo => ({ + ...namespaceInfo, + id: namespaceInfo.id, + name: this.extractFullNamespace(namespaceInfo, namespaceNames) + })); + }; + + /** + * Gets native namespaces name in from nemesis transactions. + * @returns {array} Namespaces + */ + static getNativeNamespaces = async () => { + const namespace = globalConfig.networkConfig.namespaceName; + const rootNamespace = namespace.split('.')[0]; + const namespaceids = [rootNamespace, namespace].map(namespace => new NamespaceId(namespace)); + + const namespaceInfos = await Promise.all(namespaceids.map(async namespaceid => { + return NamespaceService.getNamespace(namespaceid); + })); + + return namespaceInfos; + }; + + /** + * Format namespaceName to readable object. + * @param {object} namespaceName namespaceNameDTO + * @returns {object} readable namespaceNameDTO. + */ + static formatNamespaceName = namespaceName => ({ + ...namespaceName, + namespaceId: namespaceName.namespaceId.toHex(), + parentId: namespaceName?.parentId + ? namespaceName.parentId.toHex() + : Constants.Message.UNAVAILABLE + }); + + /** + * Format alias to readable object. + * @param {string} alias alias type. + * @returns {object} readable Alias object. + */ + static formatAlias = alias => { + switch (alias.type) { + case 0: + return { + ...alias, + aliasType: Constants.Message.UNAVAILABLE + }; + case 1: // Mosaic id alias + return { + ...alias, + aliasType: Constants.Message.MOSAIC, + alias: alias?.mosaicId.toHex() + }; + case 2: // Address alias + return { + ...alias, + aliasType: Constants.Message.ADDRESS, + alias: alias?.address.plain() + }; + } + }; + + /** + * Format namespace to readable object. + * @param {object} namespace - namespace DTO. + * @returns {object} readable namespaceDTO. + */ + static formatNamespace = namespace => ({ + ...namespace, + ownerAddress: namespace.ownerAddress.plain(), + namespaceName: namespace.name, + namespaceId: namespace.id.toHex(), + registrationType: + Constants.NamespaceRegistrationType[namespace.registrationType], + startHeight: Number(namespace.startHeight.toString()), + endHeight: helper.isNativeNamespace(namespace.name) + ? Constants.Message.INFINITY + : Number(namespace.endHeight.toString()), + active: namespace.active + ? Constants.Message.ACTIVE + : Constants.Message.INACTIVE, + ...this.formatAlias(namespace.alias), + parentName: + 0 !== namespace.registrationType ? namespace.name.split('.')[0] : '', + levels: namespace.levels + }); + + /** + * Format mosaic name to readable object. + * @param {object} mosaicName - mosaicName DTO. + * @returns {object} readable mosaicName DTO. + */ + static formatMosaicName = mosaicName => ({ + ...mosaicName, + mosaicId: mosaicName.mosaicId.toHex() + }); + + /** + * Format account name to readable object. + * @param {object} accountName - accountName DTO. + * @returns {object} readable accountName DTO. + */ + static formatAccountName = accountName => ({ + ...accountName, + address: accountName.address.plain(), + names: accountName.names.map(name => this.formatNamespaceName(name)) + }); + + /** + * Extract full name for Namespace. + * @param {object} namespaceInfo - namespaceInfo DTO. + * @param {array} namespaceNames - NamespaceNames. + * @returns {string} full name. + */ + static extractFullNamespace = (namespaceInfo, namespaceNames) => { + return namespaceInfo.levels + .map(level => { + const namespaceName = namespaceNames.find(name => name.namespaceId === level.toHex()); + + if (namespaceName === undefined) + throw new Error('Not found'); + return namespaceName; + }) + .map(namespaceName => namespaceName.name) + .join('.'); + }; } export default NamespaceService; diff --git a/src/infrastructure/NetworkService.js b/src/infrastructure/NetworkService.js index 746f96e76..95fab45ae 100644 --- a/src/infrastructure/NetworkService.js +++ b/src/infrastructure/NetworkService.js @@ -19,64 +19,71 @@ import http from './http'; class NetworkService { - /** - * Get estimated effective rental fees for namespaces and mosaics. - * @returns {object} rental fees information - */ - static getRentalFees = async () => { - const rentalFees = await http.createRepositoryFactory.createNetworkRepository() - .getRentalFees() - .toPromise(); + /** + * Get estimated effective rental fees for namespaces and mosaics. + * @returns {object} rental fees information + */ + static getRentalFees = async () => { + const rentalFees = await http.createRepositoryFactory + .createNetworkRepository() + .getRentalFees() + .toPromise(); - return this.formatRentalFees(rentalFees); - } + return this.formatRentalFees(rentalFees); + }; - /** - * Get average, median, highest and lower fee multiplier over the last "numBlocksTransactionFeeStats". - * @returns {object} transaction fees information - */ - static getTransactionFees = async () => { - const transactionFees = await http.createRepositoryFactory.createNetworkRepository() - .getTransactionFees() - .toPromise(); + /** + * Get average, median, highest and lower fee multiplier over the last "numBlocksTransactionFeeStats". + * @returns {object} transaction fees information + */ + static getTransactionFees = async () => { + const transactionFees = await http.createRepositoryFactory + .createNetworkRepository() + .getTransactionFees() + .toPromise(); - return transactionFees; - } + return transactionFees; + }; - /** - * Formatted Transaction Fees multiplier into Vue Component. - * @returns {object} Transaction Fees Info for Vue Component. - */ - static getTransactionFeesInfo = async () => { - const transactionFees = await this.getTransactionFees(); + /** + * Formatted Transaction Fees multiplier into Vue Component. + * @returns {object} Transaction Fees Info for Vue Component. + */ + static getTransactionFeesInfo = async () => { + const transactionFees = await this.getTransactionFees(); - return transactionFees; - } + return transactionFees; + }; - /** - * Formatted Rental Fees into Vue Component. - * @returns {object} Rental Fees Info for Vue Component. - */ - static getRentalFeesInfo = async () => { - const rentalFees = await this.getRentalFees(); + /** + * Formatted Rental Fees into Vue Component. + * @returns {object} Rental Fees Info for Vue Component. + */ + static getRentalFeesInfo = async () => { + const rentalFees = await this.getRentalFees(); - return rentalFees; - } + return rentalFees; + }; - /** - * Format RentalfeesDTO to readable object. - * @param {object} rentalFees rental fees. - * @returns {object} readable RentalfeesDTO object. - */ - static formatRentalFees = rentalFees => { - const { divisibility } = http.networkCurrency; + /** + * Format RentalfeesDTO to readable object. + * @param {object} rentalFees rental fees. + * @returns {object} readable RentalfeesDTO object. + */ + static formatRentalFees = rentalFees => { + const { divisibility } = http.networkCurrency; - return { - effectiveRootNamespaceRentalFeePerBlock: rentalFees.effectiveRootNamespaceRentalFeePerBlock / Math.pow(10, divisibility), - effectiveChildNamespaceRentalFee: rentalFees.effectiveChildNamespaceRentalFee / Math.pow(10, divisibility), - effectiveMosaicRentalFee: rentalFees.effectiveMosaicRentalFee / Math.pow(10, divisibility) - }; - } + return { + effectiveRootNamespaceRentalFeePerBlock: + rentalFees.effectiveRootNamespaceRentalFeePerBlock / + Math.pow(10, divisibility), + effectiveChildNamespaceRentalFee: + rentalFees.effectiveChildNamespaceRentalFee / + Math.pow(10, divisibility), + effectiveMosaicRentalFee: + rentalFees.effectiveMosaicRentalFee / Math.pow(10, divisibility) + }; + }; } export default NetworkService; diff --git a/src/infrastructure/NodeService.js b/src/infrastructure/NodeService.js index e8a8c3eea..98a7c4966 100644 --- a/src/infrastructure/NodeService.js +++ b/src/infrastructure/NodeService.js @@ -23,180 +23,200 @@ import moment from 'moment'; import * as symbol from 'symbol-sdk'; class NodeService { - /** - * Get Storage Info from symbol SDK. - * @returns {object} StorageInfo - */ - static getStorageInfo = () => { - return http.createRepositoryFactory.createNodeRepository() - .getStorageInfo() - .toPromise(); - } - - /** - * Get Node Info from symbol SDK. - * @returns {object} NodeInfo - */ - static getCurrentNodeInfo = () => { - return http.createRepositoryFactory.createNodeRepository() - .getNodeInfo() - .toPromise(); - } - - /** - * Get Node Peers from symbol SDK. - * @returns {array} NodeInfo[] - */ - static getNodePeers = async () => { - let nodePeers = []; - - try { - nodePeers = await http.statisticServiceRestClient().getNodes(); - } catch (e) { - console.error('Statistics service getNodes error: ', e); - } - - return nodePeers - .map(nodeInfo => this.formatNodeInfo(nodeInfo)) - .sort((a, b) => a.friendlyName.localeCompare(b.friendlyName)); - } - - /** - * Get node health status by endpoint. - * @param {string} currentUrl api-node endpoint such as http:localhost:3000 - * @returns {boolean} boolean - */ - static isNodeActive = async currentUrl => { - let status = true; - - try { - await new symbol.NodeHttp(currentUrl).getNodeHealth() - .toPromise(); - } catch (e) { - status = false; - } - - return status; - } - - /** - * Format NodeInfoDTO to readable NodeInfo object. - * @param {object} nodeInfo NodeInfoDTO. - * @returns {object} readable NodeInfo. - */ - static formatNodeInfo = nodeInfo => ({ - ...nodeInfo, - nodePublicKey: nodeInfo.publicKey, - address: symbol.Address.createFromPublicKey(nodeInfo.publicKey, nodeInfo.networkIdentifier).plain(), - rolesRaw: nodeInfo.roles, - roles: Constants.RoleType[nodeInfo.roles], - network: Constants.NetworkType[nodeInfo.networkIdentifier], - version: helper.formatNodeVersion(nodeInfo.version), - apiEndpoint: - 2 === nodeInfo.roles || - 3 === nodeInfo.roles || - 6 === nodeInfo.roles || - 7 === nodeInfo.roles - ? nodeInfo.apiStatus.restGatewayUrl - : Constants.Message.UNAVAILABLE - }) - - /** - * Format Node Peers dataset into Vue Component. + /** + * Get Storage Info from symbol SDK. + * @returns {object} StorageInfo + */ + static getStorageInfo = () => { + return http.createRepositoryFactory + .createNodeRepository() + .getStorageInfo() + .toPromise(); + }; + + /** + * Get Node Info from symbol SDK. + * @returns {object} NodeInfo + */ + static getCurrentNodeInfo = () => { + return http.createRepositoryFactory + .createNodeRepository() + .getNodeInfo() + .toPromise(); + }; + + /** + * Get Node Peers from symbol SDK. + * @returns {array} NodeInfo[] + */ + static getNodePeers = async () => { + let nodePeers = []; + + try { + nodePeers = await http.statisticServiceRestClient().getNodes(); + } catch (e) { + console.error('Statistics service getNodes error: ', e); + } + + return nodePeers + .map(nodeInfo => this.formatNodeInfo(nodeInfo)) + .sort((a, b) => a.friendlyName.localeCompare(b.friendlyName)); + }; + + /** + * Get node health status by endpoint. + * @param {string} currentUrl api-node endpoint such as http:localhost:3000 + * @returns {boolean} boolean + */ + static isNodeActive = async currentUrl => { + let status = true; + + try { + await new symbol.NodeHttp(currentUrl).getNodeHealth().toPromise(); + } catch (e) { + status = false; + } + + return status; + }; + + /** + * Format NodeInfoDTO to readable NodeInfo object. + * @param {object} nodeInfo NodeInfoDTO. + * @returns {object} readable NodeInfo. + */ + static formatNodeInfo = nodeInfo => ({ + ...nodeInfo, + nodePublicKey: nodeInfo.publicKey, + address: symbol.Address.createFromPublicKey( + nodeInfo.publicKey, + nodeInfo.networkIdentifier + ).plain(), + rolesRaw: nodeInfo.roles, + roles: Constants.RoleType[nodeInfo.roles], + network: Constants.NetworkType[nodeInfo.networkIdentifier], + version: helper.formatNodeVersion(nodeInfo.version), + apiEndpoint: + 2 === nodeInfo.roles || + 3 === nodeInfo.roles || + 6 === nodeInfo.roles || + 7 === nodeInfo.roles + ? nodeInfo.apiStatus.restGatewayUrl + : Constants.Message.UNAVAILABLE + }); + + /** + * Format Node Peers dataset into Vue Component. * @param {string} filter role filter. - * @returns {object} Node peers object for Vue component. - */ - static getNodePeerList = async filter => { - let nodePeers = await this.getNodePeers(); - - return { - data: - nodePeers - .filter(el => !filter.rolesRaw || el.rolesRaw === filter.rolesRaw) - .map(el => { - let node = { - ...el - }; - - node['softwareVersion'] = { version: el.version }; - - if (el.apiStatus) { - const { chainHeight, finalization, lastStatusCheck, restVersion, isHttpsEnabled } = el.apiStatus; - - node['chainInfo'] = { - chainHeight, - finalizationHeight: finalization?.height, - lastStatusCheck - }; - - node['softwareVersion'] = { - ...node.softwareVersion, - restVersion, - isHttpsEnabled - }; - } else { node['chainInfo'] = {}; } - - if (node?.hostDetail) { - node = { ...node, ...node.hostDetail }; - delete node.hostDetail; - } - - return node; - }) - }; - } - - static getNodeInfo = async publicKey => { - let node = {}; - - try { - node = await http.statisticServiceRestClient().getNode(publicKey); - } catch (e) { - throw Error('Statistics service getNode error: ', e); - } - const formattedNode = this.formatNodeInfo(node); - - if (2 === formattedNode.rolesRaw || - 3 === formattedNode.rolesRaw || - 6 === formattedNode.rolesRaw || - 7 === formattedNode.rolesRaw - ) { - const { - finalization, - chainHeight, - lastStatusCheck, - nodeStatus, - isAvailable, - isHttpsEnabled, - restVersion - } = formattedNode.apiStatus; - - // // Api status - formattedNode.apiStatus = { - connectionStatus: isAvailable, - databaseStatus: 'up' === nodeStatus?.db || Constants.Message.UNAVAILABLE, - apiNodeStatus: 'up' === nodeStatus?.apiNode || Constants.Message.UNAVAILABLE, - isHttpsEnabled, - restVersion, - lastStatusCheck: moment.utc(lastStatusCheck).format('YYYY-MM-DD HH:mm:ss') - }; - - if (finalization && chainHeight) { - // Chain info - formattedNode.chainInfo = { - height: chainHeight, - finalizedHeight: finalization?.height, - finalizationEpoch: finalization?.epoch, - finalizationPoint: finalization?.point, - finalizedHash: finalization?.hash, - lastStatusCheck: moment.utc(lastStatusCheck).format('YYYY-MM-DD HH:mm:ss') - }; - } else { formattedNode.chainInfo = {}; } - } - if (formattedNode?.peerStatus) - formattedNode.peerStatus.lastStatusCheck = moment(formattedNode.peerStatus.lastStatusCheck).format('YYYY-MM-DD HH:mm:ss'); - return formattedNode; - } + * @returns {object} Node peers object for Vue component. + */ + static getNodePeerList = async filter => { + let nodePeers = await this.getNodePeers(); + + return { + data: nodePeers + .filter(el => !filter.rolesRaw || el.rolesRaw === filter.rolesRaw) + .map(el => { + let node = { + ...el + }; + + node['softwareVersion'] = { version: el.version }; + + if (el.apiStatus) { + const { + chainHeight, + finalization, + lastStatusCheck, + restVersion, + isHttpsEnabled + } = el.apiStatus; + + node['chainInfo'] = { + chainHeight, + finalizationHeight: finalization?.height, + lastStatusCheck + }; + + node['softwareVersion'] = { + ...node.softwareVersion, + restVersion, + isHttpsEnabled + }; + } else { + node['chainInfo'] = {}; + } + + if (node?.hostDetail) { + node = { ...node, ...node.hostDetail }; + delete node.hostDetail; + } + + return node; + }) + }; + }; + + static getNodeInfo = async publicKey => { + let node = {}; + + try { + node = await http.statisticServiceRestClient().getNode(publicKey); + } catch (e) { + throw Error('Statistics service getNode error: ', e); + } + const formattedNode = this.formatNodeInfo(node); + + if ( + 2 === formattedNode.rolesRaw || + 3 === formattedNode.rolesRaw || + 6 === formattedNode.rolesRaw || + 7 === formattedNode.rolesRaw + ) { + const { + finalization, + chainHeight, + lastStatusCheck, + nodeStatus, + isAvailable, + isHttpsEnabled, + restVersion + } = formattedNode.apiStatus; + + // // Api status + formattedNode.apiStatus = { + connectionStatus: isAvailable, + databaseStatus: + 'up' === nodeStatus?.db || Constants.Message.UNAVAILABLE, + apiNodeStatus: + 'up' === nodeStatus?.apiNode || Constants.Message.UNAVAILABLE, + isHttpsEnabled, + restVersion, + lastStatusCheck: moment + .utc(lastStatusCheck) + .format('YYYY-MM-DD HH:mm:ss') + }; + + if (finalization && chainHeight) { + // Chain info + formattedNode.chainInfo = { + height: chainHeight, + finalizedHeight: finalization?.height, + finalizationEpoch: finalization?.epoch, + finalizationPoint: finalization?.point, + finalizedHash: finalization?.hash, + lastStatusCheck: moment + .utc(lastStatusCheck) + .format('YYYY-MM-DD HH:mm:ss') + }; + } else { + formattedNode.chainInfo = {}; + } + } + if (formattedNode?.peerStatus) + formattedNode.peerStatus.lastStatusCheck = moment(formattedNode.peerStatus.lastStatusCheck).format('YYYY-MM-DD HH:mm:ss'); + return formattedNode; + }; static getNodeStats = async () => { try { @@ -204,7 +224,7 @@ class NodeService { } catch (e) { throw Error('Statistics service getNodeStats error: ', e); } - } + }; static getNodeHeightStats = async () => { try { @@ -213,17 +233,23 @@ class NodeService { return [ { name: 'Height', - data: data.height.map(el => ({ x: '' + parseInt(el.value), y: parseInt(el.count) })) + data: data.height.map(el => ({ + x: '' + parseInt(el.value), + y: parseInt(el.count) + })) }, { name: 'Finalized Height', - data: data.finalizedHeight.map(el => ({ x: '' + parseInt(el.value), y: parseInt(el.count) })) + data: data.finalizedHeight.map(el => ({ + x: '' + parseInt(el.value), + y: parseInt(el.count) + })) } ]; } catch (e) { throw Error('Statistics service getNodeHeightStats error: ', e); } - } + }; static getNodeListCSV = async filter => { const nodes = await this.getNodePeerList(filter); @@ -243,33 +269,37 @@ class NodeService { })); return helper.convertArrayToCSV(formattedData); - } + }; /** - * Gets node list from statistics service. + * Gets node list from statistics service. * @param {string} filter (optional) 'preferred | suggested'. * @param {number} limit (optional) number of records. * @param {boolean} ssl (optional) return ssl ready node. - * @returns {array} nodes - */ - static getNodeList = async (filter, limit, ssl) => { - try { - return await http.statisticServiceRestClient().getNodes(filter, limit, ssl); - } catch (e) { - throw Error('Statistics service getNodeHeightStats error: ', e); - } - } - - /** - * Get API node list dataset into Vue Component. - * @returns {array} API Node list object for Vue component. - */ - static getAPINodeList = async () => { - // get 30 ssl ready nodes from statistics service the list - const nodes = await this.getNodeList('suggested', 30, true); - - return nodes.map(nodeInfo => this.formatNodeInfo(nodeInfo)).sort((a, b) => a.friendlyName.localeCompare(b.friendlyName)); - } + * @returns {array} nodes + */ + static getNodeList = async (filter, limit, ssl) => { + try { + return await http + .statisticServiceRestClient() + .getNodes(filter, limit, ssl); + } catch (e) { + throw Error('Statistics service getNodeHeightStats error: ', e); + } + }; + + /** + * Get API node list dataset into Vue Component. + * @returns {array} API Node list object for Vue component. + */ + static getAPINodeList = async () => { + // get 30 ssl ready nodes from statistics service the list + const nodes = await this.getNodeList('suggested', 30, true); + + return nodes + .map(nodeInfo => this.formatNodeInfo(nodeInfo)) + .sort((a, b) => a.friendlyName.localeCompare(b.friendlyName)); + }; } export default NodeService; diff --git a/src/infrastructure/ReceiptExtractor.js b/src/infrastructure/ReceiptExtractor.js index 8c06765d0..39fe19933 100644 --- a/src/infrastructure/ReceiptExtractor.js +++ b/src/infrastructure/ReceiptExtractor.js @@ -3,88 +3,100 @@ import helper from '../helper'; import { Mosaic, ReceiptType, MosaicId } from 'symbol-sdk'; class ReceiptExtractor { - static balanceChangeReceipt = async transactionStatement => { - let balanceChangeReceipt = []; - - const mosaics = transactionStatement.map(statement => new Mosaic(statement.mosaicId, statement.amount)); - - const { mosaicInfos, mosaicNames, unresolvedMosaicsMap } = await helper.getMosaicInfoAndNamespace(mosaics); - - for (const statement of transactionStatement) { - balanceChangeReceipt.push({ - ...statement, - height: statement.height.compact(), - receiptType: Constants.ReceiptType[statement.type], - targetAddress: statement.targetAddress.plain(), - mosaics: helper.mosaicsFieldObjectBuilder([ - new Mosaic( - new MosaicId(unresolvedMosaicsMap[statement.mosaicId.toHex()]), - statement.amount - ) - ], mosaicInfos, mosaicNames) - }); - } - - return balanceChangeReceipt; - } - - static balanceTransferReceipt = async transactionStatement => { - let balanceTransferReceipt = []; - - for (const statement of transactionStatement) { - const mosaic = new Mosaic(statement.mosaicId, statement.amount); - - balanceTransferReceipt.push({ - ...statement, - height: statement.height.compact(), - receiptType: Constants.ReceiptType[statement.type], - senderAddress: statement.senderAddress.address, - recipient: statement.recipientAddress.address, - mosaics: helper.mosaicsFieldObjectBuilder([mosaic]) - }); - } - - return balanceTransferReceipt; - } - - static inflationReceipt = async transactionStatement => { - let inflationReceipt = []; - - for (const statement of transactionStatement) { - const mosaic = new Mosaic(statement.mosaicId, statement.amount); - - inflationReceipt.push({ - ...statement, - height: statement.height.compact(), - receiptType: Constants.ReceiptType[statement.type], - mosaics: helper.mosaicsFieldObjectBuilder([mosaic]) - }); - } - - return inflationReceipt; - } - - static artifactExpiryReceipt = async transactionStatement => { - let artifactExpiryReceipt = []; - - for (const statement of transactionStatement) { - let artifactObj = { - ...statement, - height: statement.height.compact(), - receiptType: Constants.ReceiptType[statement.type], - artifactId: statement.artifactId.toHex() - }; - - if (ReceiptType.Mosaic_Expired === statement.type) - Object.assign(artifactObj, { mosaicArtifactId: statement.artifactId.toHex() }); - else if (ReceiptType.Namespace_Expired === statement.type || ReceiptType.Namespace_Deleted === statement.type) - Object.assign(artifactObj, { namespaceArtifactId: statement.artifactId.toHex() }); - - artifactExpiryReceipt.push(artifactObj); - } - - return artifactExpiryReceipt; - } + static balanceChangeReceipt = async transactionStatement => { + let balanceChangeReceipt = []; + + const mosaics = transactionStatement.map(statement => new Mosaic(statement.mosaicId, statement.amount)); + + const { mosaicInfos, mosaicNames, unresolvedMosaicsMap } = + await helper.getMosaicInfoAndNamespace(mosaics); + + for (const statement of transactionStatement) { + balanceChangeReceipt.push({ + ...statement, + height: statement.height.compact(), + receiptType: Constants.ReceiptType[statement.type], + targetAddress: statement.targetAddress.plain(), + mosaics: helper.mosaicsFieldObjectBuilder( + [ + new Mosaic( + new MosaicId(unresolvedMosaicsMap[statement.mosaicId.toHex()]), + statement.amount + ) + ], + mosaicInfos, + mosaicNames + ) + }); + } + + return balanceChangeReceipt; + }; + + static balanceTransferReceipt = async transactionStatement => { + let balanceTransferReceipt = []; + + for (const statement of transactionStatement) { + const mosaic = new Mosaic(statement.mosaicId, statement.amount); + + balanceTransferReceipt.push({ + ...statement, + height: statement.height.compact(), + receiptType: Constants.ReceiptType[statement.type], + senderAddress: statement.senderAddress.address, + recipient: statement.recipientAddress.address, + mosaics: helper.mosaicsFieldObjectBuilder([mosaic]) + }); + } + + return balanceTransferReceipt; + }; + + static inflationReceipt = async transactionStatement => { + let inflationReceipt = []; + + for (const statement of transactionStatement) { + const mosaic = new Mosaic(statement.mosaicId, statement.amount); + + inflationReceipt.push({ + ...statement, + height: statement.height.compact(), + receiptType: Constants.ReceiptType[statement.type], + mosaics: helper.mosaicsFieldObjectBuilder([mosaic]) + }); + } + + return inflationReceipt; + }; + + static artifactExpiryReceipt = async transactionStatement => { + let artifactExpiryReceipt = []; + + for (const statement of transactionStatement) { + let artifactObj = { + ...statement, + height: statement.height.compact(), + receiptType: Constants.ReceiptType[statement.type], + artifactId: statement.artifactId.toHex() + }; + + if (ReceiptType.Mosaic_Expired === statement.type) + {Object.assign(artifactObj, { + mosaicArtifactId: statement.artifactId.toHex() + });} + else if ( + ReceiptType.Namespace_Expired === statement.type || + ReceiptType.Namespace_Deleted === statement.type + ) + {Object.assign(artifactObj, { + namespaceArtifactId: statement.artifactId.toHex() + });} + + artifactExpiryReceipt.push(artifactObj); + } + + return artifactExpiryReceipt; + }; } export default ReceiptExtractor; diff --git a/src/infrastructure/ReceiptService.js b/src/infrastructure/ReceiptService.js index 21a032d2a..49c0c4e27 100644 --- a/src/infrastructure/ReceiptService.js +++ b/src/infrastructure/ReceiptService.js @@ -18,9 +18,7 @@ import http from './http'; import Constants from '../config/constants'; -import { - ReceiptExtractor -} from '../infrastructure'; +import { ReceiptExtractor } from '../infrastructure'; import { take, toArray } from 'rxjs/operators'; import { ReceiptType, ResolutionType } from 'symbol-sdk'; @@ -30,90 +28,100 @@ class ReceiptService { * @param {object} transactionStatementSearchCriteria Block Search Criteria. * @returns {object} formatted Receipts data with pagination info. */ - static searchReceipts = async transactionStatementSearchCriteria => { - const searchReceipts = await http.createRepositoryFactory.createReceiptRepository() - .searchReceipts(transactionStatementSearchCriteria) - .toPromise(); - - return { - ...searchReceipts, - data: this.transactionStatementBuilder(searchReceipts.data) - }; - } - - /** + static searchReceipts = async transactionStatementSearchCriteria => { + const searchReceipts = await http.createRepositoryFactory + .createReceiptRepository() + .searchReceipts(transactionStatementSearchCriteria) + .toPromise(); + + return { + ...searchReceipts, + data: this.transactionStatementBuilder(searchReceipts.data) + }; + }; + + /** * Gets a Address Resolution from searchCriteria. * @param {object} resolutionStatementSearchCriteria Block Search Criteria. * @returns {object} formatted address resolution data with pagination info. */ - static searchAddressResolutionStatements = async resolutionStatementSearchCriteria => { - const searchAddressResolutionStatements = await http.createRepositoryFactory.createReceiptRepository() - .searchAddressResolutionStatements(resolutionStatementSearchCriteria) - .toPromise(); - - return { - ...searchAddressResolutionStatements, - data: this.formatResolutionStatement(searchAddressResolutionStatements.data) - }; - } - - /** + static searchAddressResolutionStatements = + async resolutionStatementSearchCriteria => { + const searchAddressResolutionStatements = + await http.createRepositoryFactory + .createReceiptRepository() + .searchAddressResolutionStatements(resolutionStatementSearchCriteria) + .toPromise(); + + return { + ...searchAddressResolutionStatements, + data: this.formatResolutionStatement(searchAddressResolutionStatements.data) + }; + }; + + /** * Gets a Mosaic Resolution from searchCriteria. * @param {object} resolutionStatementSearchCriteria Block Search Criteria. * @returns {object} formatted mosaic resolution data with pagination info. */ - static searchMosaicResolutionStatements = async resolutionStatementSearchCriteria => { - const searchMosaicResolutionStatements = await http.createRepositoryFactory.createReceiptRepository() - .searchMosaicResolutionStatements(resolutionStatementSearchCriteria) - .toPromise(); - - return { - ...searchMosaicResolutionStatements, - data: this.formatResolutionStatement(searchMosaicResolutionStatements.data) - }; - } - - /** - * Gets a receipts from streamer. - * @param {object} searchCriteria - Search Criteria. - * @returns {array} formatted statementReceipt[] - */ - static streamerReceipts = async searchCriteria => { - const streamerReceipts = await http.transactionStatementPaginationStreamer() - .search(searchCriteria) - .pipe(take(10), toArray()) - .toPromise(); - - return this.transactionStatementBuilder(streamerReceipts); - } - - /** - * Gets a Address Resolution statement from streamer. - * @param {object} searchCriteria - Search Criteria. - * @returns {array} formatted statementAddressResolution[] - */ + static searchMosaicResolutionStatements = + async resolutionStatementSearchCriteria => { + const searchMosaicResolutionStatements = + await http.createRepositoryFactory + .createReceiptRepository() + .searchMosaicResolutionStatements(resolutionStatementSearchCriteria) + .toPromise(); + + return { + ...searchMosaicResolutionStatements, + data: this.formatResolutionStatement(searchMosaicResolutionStatements.data) + }; + }; + + /** + * Gets a receipts from streamer. + * @param {object} searchCriteria - Search Criteria. + * @returns {array} formatted statementReceipt[] + */ + static streamerReceipts = async searchCriteria => { + const streamerReceipts = await http + .transactionStatementPaginationStreamer() + .search(searchCriteria) + .pipe(take(10), toArray()) + .toPromise(); + + return this.transactionStatementBuilder(streamerReceipts); + }; + + /** + * Gets a Address Resolution statement from streamer. + * @param {object} searchCriteria - Search Criteria. + * @returns {array} formatted statementAddressResolution[] + */ static streamerAddressResolution = async searchCriteria => { - const streamerAddressResolution = await http.addressResolutionStatementPaginationStreamer() + const streamerAddressResolution = await http + .addressResolutionStatementPaginationStreamer() .search(searchCriteria) .pipe(take(10), toArray()) .toPromise(); return this.formatResolutionStatement(streamerAddressResolution); - } + }; - /** - * Gets a Mosaic Resolution statement from streamer. - * @param {object} searchCriteria - Search Criteria. - * @returns {array} formatted statementMosaicResolution[] - */ + /** + * Gets a Mosaic Resolution statement from streamer. + * @param {object} searchCriteria - Search Criteria. + * @returns {array} formatted statementMosaicResolution[] + */ static streamerMosaicResolution = async searchCriteria => { - const streamerMosaicResolution = await http.mosaicResolutionStatementPaginationStreamer() + const streamerMosaicResolution = await http + .mosaicResolutionStatementPaginationStreamer() .search(searchCriteria) .pipe(take(10), toArray()) .toPromise(); return this.formatResolutionStatement(streamerMosaicResolution); - } + }; static createReceiptTransactionStatement = async transactionStatement => { const { receiptTransactionStatementType } = transactionStatement; @@ -127,128 +135,133 @@ class ReceiptService { case Constants.ReceiptTransactionStatementType.InflationReceipt: return ReceiptExtractor.inflationReceipt(transactionStatement.data); default: - throw new Error('Unimplemented receipt transaction statement with type ' + receiptTransactionStatementType); + throw new Error('Unimplemented receipt transaction statement with type ' + + receiptTransactionStatementType); } + }; + + /** + * Format Receipt Statements. + * @param {array} transactionStatement - List of transaction statement. + * @returns {object} collection of receipts + * + */ + static groupTransactionStatement = transactionStatement => { + let balanceChangeReceipt = []; + + let balanceTransferReceipt = []; + + let inflationReceipt = []; + + let artifactExpiryReceipt = []; + + transactionStatement.forEach(statement => { + statement.receipts.forEach(receipt => { + switch (receipt.type) { + case ReceiptType.Harvest_Fee: + case ReceiptType.LockHash_Created: + case ReceiptType.LockHash_Completed: + case ReceiptType.LockHash_Expired: + case ReceiptType.LockSecret_Created: + case ReceiptType.LockSecret_Completed: + case ReceiptType.LockSecret_Expired: + balanceChangeReceipt.push({ + ...receipt, + height: statement.height + }); + break; + case ReceiptType.Mosaic_Rental_Fee: + case ReceiptType.Namespace_Rental_Fee: + balanceTransferReceipt.push({ + ...receipt, + height: statement.height + }); + break; + case ReceiptType.Mosaic_Expired: + case ReceiptType.Namespace_Expired: + case ReceiptType.Namespace_Deleted: + artifactExpiryReceipt.push({ + ...receipt, + height: statement.height + }); + break; + case ReceiptType.Inflation: + inflationReceipt.push({ + ...receipt, + height: statement.height + }); + break; + } + }); + }); + + return { + balanceChangeReceipt, + balanceTransferReceipt, + inflationReceipt, + artifactExpiryReceipt + }; + }; + + static transactionStatementBuilder(transactionStatement) { + const { + balanceChangeReceipt, + balanceTransferReceipt, + inflationReceipt, + artifactExpiryReceipt + } = this.groupTransactionStatement(transactionStatement); + + return { + balanceChangeStatement: { + receiptTransactionStatementType: + Constants.ReceiptTransactionStatementType.BalanceChangeReceipt, + data: balanceChangeReceipt + }, + balanceTransferStatement: { + receiptTransactionStatementType: + Constants.ReceiptTransactionStatementType.BalanceTransferReceipt, + data: balanceTransferReceipt + }, + inflationStatement: { + receiptTransactionStatementType: + Constants.ReceiptTransactionStatementType.InflationReceipt, + data: inflationReceipt + }, + artifactExpiryStatement: { + receiptTransactionStatementType: + Constants.ReceiptTransactionStatementType.ArtifactExpiryReceipt, + data: artifactExpiryReceipt + } + }; } - /** - * Format Receipt Statements. - * @param {array} transactionStatement - List of transaction statement. - * @returns {object} collection of receipts - * - */ - static groupTransactionStatement = transactionStatement => { - let balanceChangeReceipt = []; - - let balanceTransferReceipt = []; - - let inflationReceipt = []; - - let artifactExpiryReceipt = []; - - transactionStatement.forEach(statement => { - statement.receipts.forEach(receipt => { - switch (receipt.type) { - case ReceiptType.Harvest_Fee: - case ReceiptType.LockHash_Created: - case ReceiptType.LockHash_Completed: - case ReceiptType.LockHash_Expired: - case ReceiptType.LockSecret_Created: - case ReceiptType.LockSecret_Completed: - case ReceiptType.LockSecret_Expired: - balanceChangeReceipt.push({ - ...receipt, - height: statement.height - }); - break; - case ReceiptType.Mosaic_Rental_Fee: - case ReceiptType.Namespace_Rental_Fee: - balanceTransferReceipt.push({ - ...receipt, - height: statement.height - }); - break; - case ReceiptType.Mosaic_Expired: - case ReceiptType.Namespace_Expired: - case ReceiptType.Namespace_Deleted: - artifactExpiryReceipt.push({ - ...receipt, - height: statement.height - }); - break; - case ReceiptType.Inflation: - inflationReceipt.push({ - ...receipt, - height: statement.height - }); - break; - } - }); - }); - - return { - balanceChangeReceipt, - balanceTransferReceipt, - inflationReceipt, - artifactExpiryReceipt - }; - } - - static transactionStatementBuilder (transactionStatement) { - const { - balanceChangeReceipt, - balanceTransferReceipt, - inflationReceipt, - artifactExpiryReceipt - } = this.groupTransactionStatement(transactionStatement); - - return { - balanceChangeStatement: { - receiptTransactionStatementType: Constants.ReceiptTransactionStatementType.BalanceChangeReceipt, - data: balanceChangeReceipt - }, - balanceTransferStatement: { - receiptTransactionStatementType: Constants.ReceiptTransactionStatementType.BalanceTransferReceipt, - data: balanceTransferReceipt - }, - inflationStatement: { - receiptTransactionStatementType: Constants.ReceiptTransactionStatementType.InflationReceipt, - data: inflationReceipt - }, - artifactExpiryStatement: { - receiptTransactionStatementType: Constants.ReceiptTransactionStatementType.ArtifactExpiryReceipt, - data: artifactExpiryReceipt - } - }; - } - - /** - * Format Resolution Statements - * @param {array} resolutionStatement - list of resolution statement. - * @returns {object} Address Resolution | Mosaic Resolution - */ - static formatResolutionStatement = resolutionStatement => { - return resolutionStatement.map(statement => { - if (statement.resolutionType === ResolutionType.Address) { - return { - ...statement, - height: statement.height.compact(), - resolutionType: Constants.ResolutionType[statement.resolutionType], - unresolved: statement.unresolved.toHex(), - addressResolutionEntries: statement.resolutionEntries.map(resolutionEntry => resolutionEntry.resolved.address) - }; - } - - if (statement.resolutionType === ResolutionType.Mosaic) { - return { - ...statement, - resolutionType: Constants.ResolutionType[statement.resolutionType], - unresolved: statement.unresolved.toHex(), - mosaicResolutionEntries: statement.resolutionEntries.map(resolutionEntry => resolutionEntry.resolved.toHex()) - }; - } - }); - } + /** + * Format Resolution Statements + * @param {array} resolutionStatement - list of resolution statement. + * @returns {object} Address Resolution | Mosaic Resolution + */ + static formatResolutionStatement = resolutionStatement => { + return resolutionStatement.map(statement => { + if (statement.resolutionType === ResolutionType.Address) { + return { + ...statement, + height: statement.height.compact(), + resolutionType: Constants.ResolutionType[statement.resolutionType], + unresolved: statement.unresolved.toHex(), + addressResolutionEntries: statement.resolutionEntries.map(resolutionEntry => resolutionEntry.resolved.address) + }; + } + + if (statement.resolutionType === ResolutionType.Mosaic) { + return { + ...statement, + resolutionType: Constants.ResolutionType[statement.resolutionType], + unresolved: statement.unresolved.toHex(), + mosaicResolutionEntries: statement.resolutionEntries.map(resolutionEntry => resolutionEntry.resolved.toHex()) + }; + } + }); + }; } export default ReceiptService; diff --git a/src/infrastructure/RestrictionService.js b/src/infrastructure/RestrictionService.js index 7576bd45e..52aaadf79 100644 --- a/src/infrastructure/RestrictionService.js +++ b/src/infrastructure/RestrictionService.js @@ -13,174 +13,194 @@ import { } from 'symbol-sdk'; class RestrictionService { - /** - * Get Account Restriction from symbol SDK - * @param {string} address - Account address to be created from PublicKey or RawAddress - * @returns {array} AccountRestrictions[] - */ - static getAccountRestrictions = async address => { - let accountRestrictions; - - try { - accountRestrictions = await http.createRepositoryFactory.createRestrictionAccountRepository() - .getAccountRestrictions(Address.createFromRawAddress(address)) - .toPromise(); - } catch (e) { - // To catch statusCode 404 - throw Error('Account restrictions are not available.'); - } - - return accountRestrictions.restrictions.map(restriction => this.formatAccountRestriction(restriction)); - } - - /** - * Gets a mosaic restrictions list from searchCriteria - * @param {object} restrictionMosaicSearchCriteria Object of Search Criteria - * @returns {object} formatted namespace data with pagination info - */ - static searchMosaicRestrictions = async restrictionMosaicSearchCriteria => { - const searchMosaicRestrictions = await http.createRepositoryFactory.createRestrictionMosaicRepository() - .search(restrictionMosaicSearchCriteria) - .toPromise(); - - return { - ...searchMosaicRestrictions, - data: searchMosaicRestrictions.data.map(mosaicRestriction => { - if (mosaicRestriction instanceof MosaicAddressRestriction) - return this.formatMosaicAddressRestriction(mosaicRestriction); - - if (mosaicRestriction instanceof MosaicGlobalRestriction) - return this.formatMosaicGlobalRestriction(mosaicRestriction); - }) - }; - } - - /** - * Format AccountRestriction to readable object. - * @param {object} accountRestriction account restriction dto. - * @returns {object} readable AccountRestriction object. - */ - static formatAccountRestriction = accountRestriction => { - switch (accountRestriction.restrictionFlags) { - case AddressRestrictionFlag.AllowIncomingAddress: - case AddressRestrictionFlag.BlockIncomingAddress: - case AddressRestrictionFlag.AllowOutgoingAddress: - case AddressRestrictionFlag.BlockOutgoingAddress: - return { - restrictionType: Constants.AddressRestrictionFlag[accountRestriction.restrictionFlags], - restrictionAddressValues: accountRestriction.values.map(value => value.address) - }; - case MosaicRestrictionFlag.AllowMosaic: - case MosaicRestrictionFlag.BlockMosaic: - return { - restrictionType: Constants.MosaicRestrictionFlag[accountRestriction.restrictionFlags], - restrictionMosaicValues: accountRestriction.values.map(value => value.id.toHex()) - }; - case OperationRestrictionFlag.AllowOutgoingTransactionType: - case OperationRestrictionFlag.BlockOutgoingTransactionType: - return { - restrictionType: Constants.OperationRestrictionFlag[accountRestriction.restrictionFlags], - restrictionTransactionValues: accountRestriction.values.map(value => Constants.TransactionType[value]) - }; - } - } - - /** - * Format MosaicGlobalRestrictions to readable object. - * @param {object} mosaicRestriction mosaic global restriction dto. - * @returns {object } readable MosaicGlobalRestrictions object. - */ - static formatMosaicGlobalRestriction = mosaicRestriction => { - return { - ...mosaicRestriction, - entryType: Constants.MosaicRestrictionEntryType[mosaicRestriction.entryType], - mosaicId: mosaicRestriction.mosaicId.toHex(), - restrictions: mosaicRestriction.restrictions.map(item => ({ - restrictionKey: item.key.toString(), - restrictionType: Constants.MosaicRestrictionType[item.restrictionType], - restrictionValue: item.restrictionValue.toString(), - referenceMosaicId: '0000000000000000' === item.referenceMosaicId.toHex() - ? mosaicRestriction.mosaicId.toHex() - : item.referenceMosaicId.toHex() - })) - }; - } - - /** - * Format MosaicAddressRestriction to readable object. - * @param {object} addressRestriction address restriction dto. - * @returns {object} Custom address restriction object - */ - static formatMosaicAddressRestriction = addressRestriction => { - return { - ...addressRestriction, - entryType: Constants.MosaicRestrictionEntryType[addressRestriction.entryType], - mosaicId: addressRestriction.mosaicId.toHex(), - targetAddress: addressRestriction.targetAddress.address, - restrictions: addressRestriction.restrictions.map(item => ({ - restrictionKey: item.key.toString(), - restrictionValue: item.restrictionValue.toString() - })) - }; - } - - /** - * Format Account Restriction list dataset into Vue component - * @param {string} address - Address in string format. - * @returns {array} Account Restriction list - */ - static getAccountRestrictionList = async address => { - const accountRestrictions = await this.getAccountRestrictions(address); - - return accountRestrictions; - } - - /** - * Gets Mosaic Restriction list dataset into Vue component - * @param {object} pageInfo - page info such as pageNumber, pageSize - * @param {object} filterValue - search criteria eg. mosaic global or mosaic address - * @param {string} hexOrNamespace - hex value or namespace name - * @returns {array} formatted mosaic restriction list - */ - static getMosaicRestrictionList = async (pageInfo, filterValue, hexOrNamespace) => { - const mosaicId = await helper.hexOrNamespaceToId(hexOrNamespace, 'mosaic'); - - const { pageNumber, pageSize } = pageInfo; - - const searchCriteria = { - pageNumber, - pageSize, - order: Order.Desc, - mosaicId: mosaicId, - ...filterValue - }; - - const mosaicRestrictions = await this.searchMosaicRestrictions(searchCriteria); - - return mosaicRestrictions; - } - - /** - * Gets Mosaic Address Restriction list dataset into Vue component - * @param {object} pageInfo - object for page info such as pageNumber, pageSize - * @param {string} address - account Address - * @returns {array} formatted mosaic address restriction list - */ - static getMosaicAddressRestrictionList = async (pageInfo, address) => { - const { pageNumber, pageSize } = pageInfo; - - const searchCriteria = { - pageNumber, - pageSize, - order: Order.Desc, - entryType: MosaicRestrictionEntryType.ADDRESS, - targetAddress: Address.createFromRawAddress(address) - }; - - const addressRestrictions = await this.searchMosaicRestrictions(searchCriteria); - - return addressRestrictions; - } + /** + * Get Account Restriction from symbol SDK + * @param {string} address - Account address to be created from PublicKey or RawAddress + * @returns {array} AccountRestrictions[] + */ + static getAccountRestrictions = async address => { + let accountRestrictions; + + try { + accountRestrictions = await http.createRepositoryFactory + .createRestrictionAccountRepository() + .getAccountRestrictions(Address.createFromRawAddress(address)) + .toPromise(); + } catch (e) { + // To catch statusCode 404 + throw Error('Account restrictions are not available.'); + } + + return accountRestrictions.restrictions.map(restriction => + this.formatAccountRestriction(restriction)); + }; + + /** + * Gets a mosaic restrictions list from searchCriteria + * @param {object} restrictionMosaicSearchCriteria Object of Search Criteria + * @returns {object} formatted namespace data with pagination info + */ + static searchMosaicRestrictions = async restrictionMosaicSearchCriteria => { + const searchMosaicRestrictions = await http.createRepositoryFactory + .createRestrictionMosaicRepository() + .search(restrictionMosaicSearchCriteria) + .toPromise(); + + return { + ...searchMosaicRestrictions, + data: searchMosaicRestrictions.data.map(mosaicRestriction => { + if (mosaicRestriction instanceof MosaicAddressRestriction) + return this.formatMosaicAddressRestriction(mosaicRestriction); + + if (mosaicRestriction instanceof MosaicGlobalRestriction) + return this.formatMosaicGlobalRestriction(mosaicRestriction); + }) + }; + }; + + /** + * Format AccountRestriction to readable object. + * @param {object} accountRestriction account restriction dto. + * @returns {object} readable AccountRestriction object. + */ + static formatAccountRestriction = accountRestriction => { + switch (accountRestriction.restrictionFlags) { + case AddressRestrictionFlag.AllowIncomingAddress: + case AddressRestrictionFlag.BlockIncomingAddress: + case AddressRestrictionFlag.AllowOutgoingAddress: + case AddressRestrictionFlag.BlockOutgoingAddress: + return { + restrictionType: + Constants.AddressRestrictionFlag[ + accountRestriction.restrictionFlags + ], + restrictionAddressValues: accountRestriction.values.map(value => value.address) + }; + case MosaicRestrictionFlag.AllowMosaic: + case MosaicRestrictionFlag.BlockMosaic: + return { + restrictionType: + Constants.MosaicRestrictionFlag[ + accountRestriction.restrictionFlags + ], + restrictionMosaicValues: accountRestriction.values.map(value => + value.id.toHex()) + }; + case OperationRestrictionFlag.AllowOutgoingTransactionType: + case OperationRestrictionFlag.BlockOutgoingTransactionType: + return { + restrictionType: + Constants.OperationRestrictionFlag[ + accountRestriction.restrictionFlags + ], + restrictionTransactionValues: accountRestriction.values.map(value => Constants.TransactionType[value]) + }; + } + }; + + /** + * Format MosaicGlobalRestrictions to readable object. + * @param {object} mosaicRestriction mosaic global restriction dto. + * @returns {object } readable MosaicGlobalRestrictions object. + */ + static formatMosaicGlobalRestriction = mosaicRestriction => { + return { + ...mosaicRestriction, + entryType: + Constants.MosaicRestrictionEntryType[mosaicRestriction.entryType], + mosaicId: mosaicRestriction.mosaicId.toHex(), + restrictions: mosaicRestriction.restrictions.map(item => ({ + restrictionKey: item.key.toString(), + restrictionType: Constants.MosaicRestrictionType[item.restrictionType], + restrictionValue: item.restrictionValue.toString(), + referenceMosaicId: + '0000000000000000' === item.referenceMosaicId.toHex() + ? mosaicRestriction.mosaicId.toHex() + : item.referenceMosaicId.toHex() + })) + }; + }; + + /** + * Format MosaicAddressRestriction to readable object. + * @param {object} addressRestriction address restriction dto. + * @returns {object} Custom address restriction object + */ + static formatMosaicAddressRestriction = addressRestriction => { + return { + ...addressRestriction, + entryType: + Constants.MosaicRestrictionEntryType[addressRestriction.entryType], + mosaicId: addressRestriction.mosaicId.toHex(), + targetAddress: addressRestriction.targetAddress.address, + restrictions: addressRestriction.restrictions.map(item => ({ + restrictionKey: item.key.toString(), + restrictionValue: item.restrictionValue.toString() + })) + }; + }; + + /** + * Format Account Restriction list dataset into Vue component + * @param {string} address - Address in string format. + * @returns {array} Account Restriction list + */ + static getAccountRestrictionList = async address => { + const accountRestrictions = await this.getAccountRestrictions(address); + + return accountRestrictions; + }; + + /** + * Gets Mosaic Restriction list dataset into Vue component + * @param {object} pageInfo - page info such as pageNumber, pageSize + * @param {object} filterValue - search criteria eg. mosaic global or mosaic address + * @param {string} hexOrNamespace - hex value or namespace name + * @returns {array} formatted mosaic restriction list + */ + static getMosaicRestrictionList = async ( + pageInfo, + filterValue, + hexOrNamespace + ) => { + const mosaicId = await helper.hexOrNamespaceToId(hexOrNamespace, 'mosaic'); + + const { pageNumber, pageSize } = pageInfo; + + const searchCriteria = { + pageNumber, + pageSize, + order: Order.Desc, + mosaicId: mosaicId, + ...filterValue + }; + + const mosaicRestrictions = await this.searchMosaicRestrictions(searchCriteria); + + return mosaicRestrictions; + }; + + /** + * Gets Mosaic Address Restriction list dataset into Vue component + * @param {object} pageInfo - object for page info such as pageNumber, pageSize + * @param {string} address - account Address + * @returns {array} formatted mosaic address restriction list + */ + static getMosaicAddressRestrictionList = async (pageInfo, address) => { + const { pageNumber, pageSize } = pageInfo; + + const searchCriteria = { + pageNumber, + pageSize, + order: Order.Desc, + entryType: MosaicRestrictionEntryType.ADDRESS, + targetAddress: Address.createFromRawAddress(address) + }; + + const addressRestrictions = await this.searchMosaicRestrictions(searchCriteria); + + return addressRestrictions; + }; } export default RestrictionService; diff --git a/src/infrastructure/TransactionService.js b/src/infrastructure/TransactionService.js index bba5fb0fe..a1faafe15 100644 --- a/src/infrastructure/TransactionService.js +++ b/src/infrastructure/TransactionService.js @@ -19,10 +19,7 @@ import http from './http'; import Constants from '../config/constants'; import helper from '../helper'; -import { - LockService, - CreateTransaction -} from '../infrastructure'; +import { LockService, CreateTransaction } from '../infrastructure'; import { toArray } from 'rxjs/operators'; import { TransactionType, @@ -35,722 +32,766 @@ import { } from 'symbol-sdk'; class TransactionService { - /** - * Gets a transaction status for a transaction hash - * @param {string} hash Transaction hash - * @returns {object} TransactionStatus object - */ - static getTransactionStatus = hash => { - return new Promise((resolve, reject) => { - let transactionStatus = { - message: null, - detail: {} - }; - - http.createRepositoryFactory.createTransactionStatusRepository() - .getTransactionStatus(hash) - .toPromise() - .then(response => { - transactionStatus.message = response.group; - transactionStatus.detail = response; - resolve(transactionStatus); - }) - .catch(error => { - // handle REST error https://github.com/nemtech/catapult-rest/pull/499 - // Todo: Remove if statement, after REST error is fix. - if (-1 === error.message.search('statusCode')) { - transactionStatus.message = error.message; - transactionStatus.detail = error; - resolve(transactionStatus); - } - resolve(transactionStatus); - }); - }); - } - - /** - * Gets a transaction from hash. - * @param {string} hash Transaction hash. - * @param {string} transactionGroup transaction status. - * @returns {object} Transaction. - */ - static getTransaction = async (hash, transactionGroup) => { - const transaction = await http.createRepositoryFactory.createTransactionRepository() - .getTransaction(hash, transactionGroup) - .toPromise(); - - return transaction; - } - - /** - * Gets a transaction from searchCriteria - * @param {object} transactionSearchCriteria Object of Search Criteria - * @returns {object} transaction data with pagination info - */ - static searchTransactions = async transactionSearchCriteria => { - const searchTransactions = await http.createRepositoryFactory.createTransactionRepository() - .search(transactionSearchCriteria) - .toPromise(); - - return searchTransactions; - } - - /** - * Gets a transactions from streamer - * @param {object} transactionSearchCriteria Object of Search Criteria - * @returns {array} Transaction[] - */ - static streamerTransactions = async transactionSearchCriteria => { - const streamerTransactions = await http.transactionPaginationStreamer - .search(transactionSearchCriteria).pipe(toArray()) - .toPromise(); - - return streamerTransactions; - } - - /** - * Gets Formatted Transaction Info for Vue component - * @param {string} hash Transaction hash - * @returns {object} Custom Transaction object - */ - static getTransactionInfo = async hash => { - const transactionStatus = await this.getTransactionStatus(hash); - const transactionGroup = transactionStatus.message; - const transaction = await this.getTransaction(hash, transactionGroup); - - const {maxFee, ...formattedTransaction} = await this.createTransactionFromSDK(transaction); - - const transactionInfo = { - ...formattedTransaction, - blockHeight: formattedTransaction.transactionInfo.height || undefined, - transactionHash: formattedTransaction.transactionInfo.hash, - timestamp: formattedTransaction.transactionInfo.timestamp | null, - status: transactionStatus.detail.code, - confirm: transactionStatus.message - }; - - // Display max fees if unconfirmed or partial transaction - if (0 === formattedTransaction.transactionInfo.height) { - Object.assign(transactionInfo, { - maxFee - }); - } else { - Object.assign(transactionInfo, { - effectiveFee: helper.toNetworkCurrency(formattedTransaction.payloadSize * formattedTransaction.transactionInfo.feeMultiplier) - }); - } - - return transactionInfo; - } - - /** - * Gets Formatted Hash Lock Info for Vue component - * @param {string} hash Transaction hash - * @returns {object} Custom Hash Lock object - */ - static getHashLockInfo = async hash => { - const hashInfo = await LockService.getHashLock(hash); - - const mosaics = [new Mosaic(hashInfo.mosaicId, hashInfo.amount)]; - - const mosaicsFieldObject = helper.mosaicsFieldObjectBuilder(mosaics); - - return { - ...hashInfo, - mosaics: mosaicsFieldObject - }; - } - - /** - * Gets array of transactions - * @param {object} pageInfo - object for page info such as pageNumber, pageSize - * @param {object} filterValue - object for search criteria - * @returns {array} Formatted transactionDTO[] - */ - static getTransactionList = async (pageInfo, filterValue) => { - const { pageNumber, pageSize } = pageInfo; - const searchCriteria = { - pageNumber, - pageSize, - order: Order.Desc, - type: [], - group: TransactionGroup.Confirmed, - ...filterValue - }; - - const searchTransactions = await this.searchTransactions(searchCriteria); - - const transactions = { - ...searchTransactions, - data: searchTransactions.data.map(transaction => this.formatTransaction(transaction)) - }; - - await Promise.all(transactions.data.map(async transaction => { - if (transaction?.recipientAddress) { - const { recipientAddress, transactionBody, transactionInfo } = transaction; - - return (transactionBody.recipient = await helper.resolvedAddress(recipientAddress, transactionInfo.height)); - } - })); - - if (searchCriteria.group === TransactionGroup.Partial || searchCriteria.group === TransactionGroup.Unconfirmed) { - return { - ...transactions, - data: transactions.data.map(transaction => ({ - ...transaction, - transactionHash: transaction.transactionInfo.hash, - transactionType: transaction.type, - recipient: transaction.transactionBody?.recipient, - extendGraphicValue: this.extendGraphicValue(transaction) - })) - }; - } - - // Cap data in 50 pages - const totalRecords = 50 * pageSize; - - return { - ...transactions, - totalRecords, - data: transactions.data.map(({ deadline, maxFee, ...transaction }) => ({ - ...transaction, - effectiveFee: helper.toNetworkCurrency(transaction.payloadSize * transaction.transactionInfo.feeMultiplier), - age: helper.convertTimestampToDate(transaction.transactionInfo.timestamp), - height: transaction.transactionInfo.height, - transactionHash: transaction.transactionInfo.hash, - transactionType: transaction.type, - recipient: transaction.transactionBody?.recipient, - extendGraphicValue: this.extendGraphicValue(transaction) - })) - }; - } - - /** - * Format Transaction - * @param {object} transaction transaction object from endpoint. - * @returns {object} readable transactionDTO object. - */ - static formatTransaction = transaction => ({ - ...transaction, - deadline: helper.networkTimestamp(transaction.deadline.adjustedValue), - maxFee: helper.toNetworkCurrency(Number(transaction.maxFee.toString())), - signer: transaction.signer.address.plain(), - transactionBody: this.formatTransactionBody(transaction), - transactionInfo: this.formatTransactionInfo(transaction.transactionInfo) - }) - - /** - * Format Different Type of Transaction such as TransferTransaction - * @param {object} transactionBody transaction body info. - * @returns {object} readable TransactionBody object - */ - static formatTransactionBody = transactionBody => { - switch (transactionBody.type) { - case TransactionType.TRANSFER: - return { - transactionType: transactionBody.type, - recipient: transactionBody.recipientAddress, - mosaics: transactionBody.mosaics.map(mosaic => ({ - ...mosaic, - id: mosaic.id.toHex(), - amount: mosaic.amount.compact().toString() - })), - message: transactionBody.message - }; - - case TransactionType.NAMESPACE_REGISTRATION: - let parentIdHex = transactionBody.parentId ? transactionBody.parentId.toHex() : ''; - - let duration = transactionBody.duration ? transactionBody.duration.compact() : 0; - - return { - transactionType: transactionBody.type, - recipient: http.networkConfig.NamespaceRentalFeeSinkAddress.address, - registrationType: Constants.NamespaceRegistrationType[transactionBody.registrationType], - namespaceName: transactionBody.namespaceName, - namespaceId: transactionBody.namespaceId.toHex(), - parentId: '' === parentIdHex ? Constants.Message.UNAVAILABLE : parentIdHex, - duration: 0 === duration ? Constants.Message.UNLIMITED : duration - }; - - case TransactionType.ADDRESS_ALIAS: - return { - transactionType: transactionBody.type, - aliasAction: Constants.AliasAction[transactionBody.aliasAction], - namespaceId: transactionBody.namespaceId.toHex(), - namespaceName: transactionBody.namespaceId.fullName, - address: transactionBody.address.address - }; - - case TransactionType.MOSAIC_ALIAS: - return { - transactionType: transactionBody.type, - aliasAction: Constants.AliasAction[transactionBody.aliasAction], - namespaceId: transactionBody.namespaceId.id.toHex(), - namespaceName: transactionBody.namespaceId.fullName, - mosaicId: transactionBody.mosaicId.id.toHex() - }; - - case TransactionType.MOSAIC_DEFINITION: - return { - transactionType: transactionBody.type, - recipient: http.networkConfig.MosaicRentalSinkAddress.address, - mosaicId: transactionBody.mosaicId.toHex(), - divisibility: transactionBody.divisibility, - duration: transactionBody.duration.compact(), - nonce: transactionBody.nonce.toHex(), - supplyMutable: transactionBody.flags.supplyMutable, - transferable: transactionBody.flags.transferable, - restrictable: transactionBody.flags.restrictable, - revokable: transactionBody.flags.revokable - }; - - case TransactionType.MOSAIC_SUPPLY_CHANGE: - return { - transactionType: transactionBody.type, - mosaicId: transactionBody.mosaicId.id.toHex(), - action: Constants.MosaicSupplyChangeAction[transactionBody.action], - delta: transactionBody.delta.compact() - }; - case TransactionType.MOSAIC_SUPPLY_REVOCATION: - return { - transactionType: transactionBody.type, - address: transactionBody.sourceAddress.address, - mosaics: [{ - id: transactionBody.mosaic.id.toHex(), - amount: transactionBody.mosaic.amount.compact().toString() - }] - }; - - case TransactionType.MULTISIG_ACCOUNT_MODIFICATION: - return { - transactionType: transactionBody.type, - minApprovalDelta: transactionBody.minApprovalDelta, - minRemovalDelta: transactionBody.minRemovalDelta, - addressAdditions: transactionBody.addressAdditions.map(address => address.address), - addressDeletions: transactionBody.addressDeletions.map(address => address.address) - }; - - case TransactionType.AGGREGATE_COMPLETE: - return { - transactionType: transactionBody.type, - innerTransactions: transactionBody.innerTransactions, - cosignatures: transactionBody.cosignatures.map(cosigner => ({ - ...cosigner, - signer: cosigner.signer.address.address - })) - }; - - case TransactionType.AGGREGATE_BONDED: - return { - transactionType: transactionBody.type, - innerTransactions: transactionBody.innerTransactions, - cosignatures: transactionBody.cosignatures.map(cosigner => ({ - ...cosigner, - signer: cosigner.signer.address.address - })) - }; - - case TransactionType.HASH_LOCK: - return { - transactionType: transactionBody.type, - duration: transactionBody.duration.compact(), - mosaicId: transactionBody.mosaic.id.toHex(), - amount: helper.toNetworkCurrency(transactionBody.mosaic.amount), - hash: transactionBody.hash - }; - - case TransactionType.SECRET_LOCK: - return { - transactionType: transactionBody.type, - duration: transactionBody.duration.compact(), - mosaicId: transactionBody.mosaic.id.toHex(), - amount: helper.toNetworkCurrency(transactionBody.mosaic.amount), - secret: transactionBody.secret, - recipient: transactionBody.recipientAddress, - hashAlgorithm: Constants.LockHashAlgorithm[transactionBody.hashAlgorithm] - }; - - case TransactionType.SECRET_PROOF: - return { - transactionType: transactionBody.type, - hashAlgorithm: Constants.LockHashAlgorithm[transactionBody.hashAlgorithm], - recipient: transactionBody.recipientAddress, - secret: transactionBody.secret, - proof: transactionBody.proof - }; - case TransactionType.ACCOUNT_ADDRESS_RESTRICTION: - return { - transactionType: transactionBody.type, - restrictionType: Constants.AddressRestrictionFlag[transactionBody.restrictionFlags], - restrictionAddressAdditions: transactionBody.restrictionAdditions.map(restriction => { - if (restriction instanceof Address) - return restriction.address; - - return restriction.fullName; - }), - restrictionAddressDeletions: transactionBody.restrictionDeletions.map(restriction => { - if (restriction instanceof Address) - return restriction.address; - - return restriction.fullName; - }) - }; - - case TransactionType.ACCOUNT_MOSAIC_RESTRICTION: - return { - transactionType: transactionBody.type, - restrictionType: Constants.MosaicRestrictionFlag[transactionBody.restrictionFlags], - restrictionMosaicAdditions: transactionBody.restrictionAdditions.map(mosaic => mosaic.id.toHex()), - restrictionMosaicDeletions: transactionBody.restrictionDeletions.map(mosaic => mosaic.id.toHex()) - }; - - case TransactionType.ACCOUNT_OPERATION_RESTRICTION: - return { - transactionType: transactionBody.type, - restrictionType: Constants.OperationRestrictionFlag[transactionBody.restrictionFlags], - restrictionOperationAdditions: transactionBody.restrictionAdditions, - restrictionOperationDeletions: transactionBody.restrictionDeletions - }; - - case TransactionType.MOSAIC_ADDRESS_RESTRICTION: - return { - transactionType: transactionBody.type, - mosaicId: transactionBody.mosaicId.toHex(), - targetAddress: transactionBody.targetAddress, - restrictionKey: transactionBody.restrictionKey.toHex(), - previousRestrictionValue: transactionBody.previousRestrictionValue.toString(), - newRestrictionValue: transactionBody.newRestrictionValue.toString() - }; - - case TransactionType.MOSAIC_GLOBAL_RESTRICTION: - return { - transactionType: transactionBody.type, - referenceMosaicId: '0000000000000000' === transactionBody.referenceMosaicId.toHex() - ? transactionBody.mosaicId.toHex() - : transactionBody.referenceMosaicId.toHex(), - restrictionKey: transactionBody.restrictionKey.toHex(), - previousRestrictionType: Constants.MosaicRestrictionType[transactionBody.previousRestrictionType], - previousRestrictionValue: transactionBody.previousRestrictionValue.compact(), - newRestrictionType: Constants.MosaicRestrictionType[transactionBody.newRestrictionType], - newRestrictionValue: transactionBody.newRestrictionValue.compact() - }; - - case TransactionType.ACCOUNT_METADATA: - return { - transactionType: transactionBody.type, - scopedMetadataKey: transactionBody.scopedMetadataKey.toHex(), - targetAddress: transactionBody.targetAddress.address, - metadataValue: transactionBody.value, - valueSizeDelta: transactionBody.valueSizeDelta - }; - - case TransactionType.MOSAIC_METADATA: - return { - transactionType: transactionBody.type, - scopedMetadataKey: transactionBody.scopedMetadataKey.toHex(), - targetMosaicId: transactionBody.targetMosaicId.toHex(), - targetAddress: transactionBody.targetAddress.address, - metadataValue: transactionBody.value, - valueSizeDelta: transactionBody.valueSizeDelta - }; - - case TransactionType.NAMESPACE_METADATA: - return { - transactionType: transactionBody.type, - scopedMetadataKey: transactionBody.scopedMetadataKey.toHex(), - targetNamespaceId: transactionBody.targetNamespaceId.toHex(), - targetAddress: transactionBody.targetAddress.address, - metadataValue: transactionBody.value, - valueSizeDelta: transactionBody.valueSizeDelta - }; - case TransactionType.VOTING_KEY_LINK: - return { - transactionType: transactionBody.type, - linkAction: Constants.LinkAction[transactionBody.linkAction], - linkedPublicKey: transactionBody.linkedPublicKey, - linkedAccountAddress: Address.createFromPublicKey(transactionBody.linkedPublicKey, http.networkType).plain(), - startEpoch: transactionBody.startEpoch, - endEpoch: transactionBody.endEpoch - }; - case TransactionType.VRF_KEY_LINK: - case TransactionType.NODE_KEY_LINK: - case TransactionType.ACCOUNT_KEY_LINK: - return { - transactionType: transactionBody.type, - linkAction: Constants.LinkAction[transactionBody.linkAction], - linkedPublicKey: transactionBody.linkedPublicKey, - linkedAccountAddress: Address.createFromPublicKey(transactionBody.linkedPublicKey, http.networkType).plain() - }; - } - } - - /** - * Format transactionInfoDTO. - * @param {object} transactionInfo transactionInfoDTO. - * @returns {object} readable transactionInfoDTO object. - */ - static formatTransactionInfo = transactionInfo => { - if (transactionInfo instanceof TransactionInfo) { - return { - ...transactionInfo, - height: transactionInfo.height.compact(), - timestamp: helper.networkTimestamp(Number(transactionInfo.timestamp.toString())) - }; - } - - if (transactionInfo instanceof AggregateTransactionInfo) { - return { - ...transactionInfo - }; - } - - return {}; - } - - /** - * extend graphic value for transaction list. - * @param {object} transactionInfo formatted transaction info. - * @returns {array} graphicValue. - */ - static extendGraphicValue = transactionInfo => { - const { transactionBody } = transactionInfo; - - switch (transactionInfo.type) { - case TransactionType.TRANSFER: - return { - nativeMosaic: helper.getNetworkCurrencyBalance(transactionInfo.mosaics), - message: transactionBody.message, - mosaics: transactionBody.mosaics.filter(mosaic => - mosaic.id !== http.networkCurrency.mosaicId && - mosaic.id !== http.networkCurrency.namespaceId) - }; - case TransactionType.NAMESPACE_REGISTRATION: - return [{ namespace: { - namespaceId: transactionBody.namespaceId, - namespaceName: transactionBody.namespaceName - } - }]; - case TransactionType.ADDRESS_ALIAS: - return [ - { linkAction: transactionBody.aliasAction } - ]; - case TransactionType.MOSAIC_ALIAS: - return [ - { linkAction: transactionBody.aliasAction } - ]; - case TransactionType.MOSAIC_DEFINITION: - return [ - { mosaicId: transactionBody.mosaicId } - ]; - case TransactionType.MOSAIC_SUPPLY_CHANGE: - return [ - { action: transactionBody.action } - ]; - case TransactionType.MOSAIC_SUPPLY_REVOCATION: - return { - mosaics: transactionBody.mosaics - }; - case TransactionType.MULTISIG_ACCOUNT_MODIFICATION: - return [ - { minApprovalDelta: transactionBody.minApprovalDelta }, - { minRemovalDelta: transactionBody.minRemovalDelta } - ]; - case TransactionType.HASH_LOCK: - return [ - { amount: transactionBody.amount } - ]; - case TransactionType.SECRET_LOCK: - return [ - { secret: transactionBody.secret } - ]; - case TransactionType.SECRET_PROOF: - return [ - { proof: transactionBody.proof } - ]; - case TransactionType.ACCOUNT_ADDRESS_RESTRICTION: - return [ - { restrictionAddressAdditions: transactionBody.restrictionAddressAdditions }, - { restrictionAddressDeletions: transactionBody.restrictionAddressDeletions } - ]; - case TransactionType.ACCOUNT_MOSAIC_RESTRICTION: - return [ - { restrictionMosaicAdditions: transactionBody.restrictionMosaicAdditions }, - { restrictionMosaicDeletions: transactionBody.restrictionMosaicDeletions } - ]; - case TransactionType.ACCOUNT_OPERATION_RESTRICTION: - return [ - { restrictionOperationAdditions: transactionBody.restrictionOperationAdditions }, - { restrictionOperationDeletions: transactionBody.restrictionOperationDeletions } - ]; - case TransactionType.MOSAIC_ADDRESS_RESTRICTION: - return [ - { targetAddress: transactionBody.targetAddress } - ]; - case TransactionType.MOSAIC_GLOBAL_RESTRICTION: - return [ - { referenceMosaicId: transactionBody.referenceMosaicId } - ]; - case TransactionType.ACCOUNT_METADATA: - case TransactionType.MOSAIC_METADATA: - case TransactionType.NAMESPACE_METADATA: - return [ - { metadataValue: transactionBody.metadataValue } - ]; - case TransactionType.VOTING_KEY_LINK: - case TransactionType.VRF_KEY_LINK: - case TransactionType.NODE_KEY_LINK: - case TransactionType.ACCOUNT_KEY_LINK: - return [ - { linkAction: transactionBody.linkAction } - ]; - default: - return []; - } - } - - /** - * Build standalone transaction object for Vue components. - * @param {object} transactionDTO - transaction dto from SDK. - * @returns {object} formatted transactionObj. - */ - static createStandaloneTransactionFromSDK = async (transactionDTO, { mosaicInfos, mosaicNames, unresolvedMosaicsMap }) => { - const transactionObj = { - ...transactionDTO, - transactionType: transactionDTO.type, - deadline: helper.networkTimestamp(transactionDTO.deadline.adjustedValue), - maxFee: helper.toNetworkCurrency(transactionDTO.maxFee), - signer: transactionDTO.signer.address.plain(), - transactionInfo: this.formatTransactionInfo(transactionDTO.transactionInfo) - }; - - switch (transactionDTO.type) { - case TransactionType.TRANSFER: - return CreateTransaction.transferTransaction(transactionObj, { - mosaicInfos, - mosaicNames, - unresolvedMosaicsMap - }); - case TransactionType.NAMESPACE_REGISTRATION: - return CreateTransaction.namespaceRegistration(transactionObj); - case TransactionType.ADDRESS_ALIAS: - return CreateTransaction.addressAlias(transactionObj); - case TransactionType.MOSAIC_ALIAS: - return CreateTransaction.mosaicAlias(transactionObj); - case TransactionType.MOSAIC_DEFINITION: - return CreateTransaction.mosaicDefinition(transactionObj); - case TransactionType.MOSAIC_SUPPLY_CHANGE: - return CreateTransaction.mosaicSupplyChange(transactionObj); - case TransactionType.MOSAIC_SUPPLY_REVOCATION: - return CreateTransaction.mosaicSupplyRevocation(transactionObj, { - mosaicInfos, - mosaicNames, - unresolvedMosaicsMap - }); - case TransactionType.MULTISIG_ACCOUNT_MODIFICATION: - return CreateTransaction.multisigAccountModification(transactionObj); - case TransactionType.HASH_LOCK: - return CreateTransaction.hashLock(transactionObj, { - mosaicInfos, - mosaicNames, - unresolvedMosaicsMap - }); - case TransactionType.SECRET_LOCK: - return CreateTransaction.secretLock(transactionObj, { - mosaicInfos, - mosaicNames, - unresolvedMosaicsMap - }); - case TransactionType.SECRET_PROOF: - return CreateTransaction.secretProof(transactionObj); - case TransactionType.ACCOUNT_ADDRESS_RESTRICTION: - return CreateTransaction.accountAddressRestriction(transactionObj); - case TransactionType.ACCOUNT_MOSAIC_RESTRICTION: - return CreateTransaction.accountMosaicRestriction(transactionObj); - case TransactionType.ACCOUNT_OPERATION_RESTRICTION: - return CreateTransaction.accountOperationRestriction(transactionObj); - case TransactionType.MOSAIC_ADDRESS_RESTRICTION: - return CreateTransaction.mosaicAddressRestriction(transactionObj); - case TransactionType.MOSAIC_GLOBAL_RESTRICTION: - return CreateTransaction.mosaicGlobalRestriction(transactionObj); - case TransactionType.ACCOUNT_METADATA: - return CreateTransaction.accountMetadata(transactionObj); - case TransactionType.MOSAIC_METADATA: - return CreateTransaction.mosaicMetadata(transactionObj); - case TransactionType.NAMESPACE_METADATA: - return CreateTransaction.namespaceMetadata(transactionObj); - case TransactionType.VOTING_KEY_LINK: - return CreateTransaction.votingKeyLink(transactionObj); - case TransactionType.VRF_KEY_LINK: - return CreateTransaction.vrfKeyLink(transactionObj); - case TransactionType.NODE_KEY_LINK: - return CreateTransaction.nodeKeyLink(transactionObj); - case TransactionType.ACCOUNT_KEY_LINK: - return CreateTransaction.accountKeyLink(transactionObj); - default: - throw new Error('Unimplemented transaction with type ' + transactionDTO.type); - } - } - - /** - * Build transaction object for Vue components. - * @param {object} transactionDTO - transaction dto from SDK - * @returns {object} transactionObj - */ - static createTransactionFromSDK = async transactionDTO => { - if (transactionDTO.type === TransactionType.AGGREGATE_BONDED - || transactionDTO.type === TransactionType.AGGREGATE_COMPLETE) { - - const { - mosaicInfos, - mosaicNames, - unresolvedMosaicsMap - } = await helper.getTransactionMosaicInfoAndNamespace(transactionDTO.innerTransactions); - - const innerTransactions = transactionDTO.innerTransactions - ? await Promise.all(transactionDTO.innerTransactions.map(async (transaction, index) => { - return { - index: index + 1, - ...await this.createStandaloneTransactionFromSDK(transaction, { - mosaicInfos, - mosaicNames, - unresolvedMosaicsMap - }) - }; - })) : []; - - return { - ...transactionDTO, - innerTransactions, - cosignatures: transactionDTO.cosignatures ? transactionDTO.cosignatures.map(cosignature => { - return { - ...cosignature, - signature: cosignature.signature, - signer: cosignature.signer.address.plain() - }; - }) : [], - deadline: helper.networkTimestamp(transactionDTO.deadline.adjustedValue), - maxFee: helper.toNetworkCurrency(transactionDTO.maxFee), - signer: transactionDTO.signer.address.plain(), - transactionInfo: this.formatTransactionInfo(transactionDTO.transactionInfo), - transactionBody: { - transactionType: transactionDTO.type - } - }; - } else { - const { - mosaicInfos, - mosaicNames, - unresolvedMosaicsMap - } = await helper.getTransactionMosaicInfoAndNamespace([transactionDTO]); - - return this.createStandaloneTransactionFromSDK(transactionDTO, { - mosaicInfos, - mosaicNames, - unresolvedMosaicsMap - }); - } - } + /** + * Gets a transaction status for a transaction hash + * @param {string} hash Transaction hash + * @returns {object} TransactionStatus object + */ + static getTransactionStatus = hash => { + return new Promise((resolve, reject) => { + let transactionStatus = { + message: null, + detail: {} + }; + + http.createRepositoryFactory + .createTransactionStatusRepository() + .getTransactionStatus(hash) + .toPromise() + .then(response => { + transactionStatus.message = response.group; + transactionStatus.detail = response; + resolve(transactionStatus); + }) + .catch(error => { + // handle REST error https://github.com/nemtech/catapult-rest/pull/499 + // Todo: Remove if statement, after REST error is fix. + if (-1 === error.message.search('statusCode')) { + transactionStatus.message = error.message; + transactionStatus.detail = error; + resolve(transactionStatus); + } + resolve(transactionStatus); + }); + }); + }; + + /** + * Gets a transaction from hash. + * @param {string} hash Transaction hash. + * @param {string} transactionGroup transaction status. + * @returns {object} Transaction. + */ + static getTransaction = async (hash, transactionGroup) => { + const transaction = await http.createRepositoryFactory + .createTransactionRepository() + .getTransaction(hash, transactionGroup) + .toPromise(); + + return transaction; + }; + + /** + * Gets a transaction from searchCriteria + * @param {object} transactionSearchCriteria Object of Search Criteria + * @returns {object} transaction data with pagination info + */ + static searchTransactions = async transactionSearchCriteria => { + const searchTransactions = await http.createRepositoryFactory + .createTransactionRepository() + .search(transactionSearchCriteria) + .toPromise(); + + return searchTransactions; + }; + + /** + * Gets a transactions from streamer + * @param {object} transactionSearchCriteria Object of Search Criteria + * @returns {array} Transaction[] + */ + static streamerTransactions = async transactionSearchCriteria => { + const streamerTransactions = await http.transactionPaginationStreamer + .search(transactionSearchCriteria) + .pipe(toArray()) + .toPromise(); + + return streamerTransactions; + }; + + /** + * Gets Formatted Transaction Info for Vue component + * @param {string} hash Transaction hash + * @returns {object} Custom Transaction object + */ + static getTransactionInfo = async hash => { + const transactionStatus = await this.getTransactionStatus(hash); + const transactionGroup = transactionStatus.message; + const transaction = await this.getTransaction(hash, transactionGroup); + + const { maxFee, ...formattedTransaction } = + await this.createTransactionFromSDK(transaction); + + const transactionInfo = { + ...formattedTransaction, + blockHeight: formattedTransaction.transactionInfo.height || undefined, + transactionHash: formattedTransaction.transactionInfo.hash, + timestamp: formattedTransaction.transactionInfo.timestamp | null, + status: transactionStatus.detail.code, + confirm: transactionStatus.message + }; + + // Display max fees if unconfirmed or partial transaction + if (0 === formattedTransaction.transactionInfo.height) { + Object.assign(transactionInfo, { + maxFee + }); + } else { + Object.assign(transactionInfo, { + effectiveFee: helper.toNetworkCurrency(formattedTransaction.payloadSize * + formattedTransaction.transactionInfo.feeMultiplier) + }); + } + + return transactionInfo; + }; + + /** + * Gets Formatted Hash Lock Info for Vue component + * @param {string} hash Transaction hash + * @returns {object} Custom Hash Lock object + */ + static getHashLockInfo = async hash => { + const hashInfo = await LockService.getHashLock(hash); + + const mosaics = [new Mosaic(hashInfo.mosaicId, hashInfo.amount)]; + + const mosaicsFieldObject = helper.mosaicsFieldObjectBuilder(mosaics); + + return { + ...hashInfo, + mosaics: mosaicsFieldObject + }; + }; + + /** + * Gets array of transactions + * @param {object} pageInfo - object for page info such as pageNumber, pageSize + * @param {object} filterValue - object for search criteria + * @returns {array} Formatted transactionDTO[] + */ + static getTransactionList = async (pageInfo, filterValue) => { + const { pageNumber, pageSize } = pageInfo; + const searchCriteria = { + pageNumber, + pageSize, + order: Order.Desc, + type: [], + group: TransactionGroup.Confirmed, + ...filterValue + }; + + const searchTransactions = await this.searchTransactions(searchCriteria); + + const transactions = { + ...searchTransactions, + data: searchTransactions.data.map(transaction => + this.formatTransaction(transaction)) + }; + + await Promise.all(transactions.data.map(async transaction => { + if (transaction?.recipientAddress) { + const { recipientAddress, transactionBody, transactionInfo } = + transaction; + + return (transactionBody.recipient = await helper.resolvedAddress( + recipientAddress, + transactionInfo.height + )); + } + })); + + if ( + searchCriteria.group === TransactionGroup.Partial || + searchCriteria.group === TransactionGroup.Unconfirmed + ) { + return { + ...transactions, + data: transactions.data.map(transaction => ({ + ...transaction, + transactionHash: transaction.transactionInfo.hash, + transactionType: transaction.type, + recipient: transaction.transactionBody?.recipient, + extendGraphicValue: this.extendGraphicValue(transaction) + })) + }; + } + + // Cap data in 50 pages + const totalRecords = 50 * pageSize; + + return { + ...transactions, + totalRecords, + data: transactions.data.map(({ deadline, maxFee, ...transaction }) => ({ + ...transaction, + effectiveFee: helper.toNetworkCurrency(transaction.payloadSize * transaction.transactionInfo.feeMultiplier), + age: helper.convertTimestampToDate(transaction.transactionInfo.timestamp), + height: transaction.transactionInfo.height, + transactionHash: transaction.transactionInfo.hash, + transactionType: transaction.type, + recipient: transaction.transactionBody?.recipient, + extendGraphicValue: this.extendGraphicValue(transaction) + })) + }; + }; + + /** + * Format Transaction + * @param {object} transaction transaction object from endpoint. + * @returns {object} readable transactionDTO object. + */ + static formatTransaction = transaction => ({ + ...transaction, + deadline: helper.networkTimestamp(transaction.deadline.adjustedValue), + maxFee: helper.toNetworkCurrency(Number(transaction.maxFee.toString())), + signer: transaction.signer.address.plain(), + transactionBody: this.formatTransactionBody(transaction), + transactionInfo: this.formatTransactionInfo(transaction.transactionInfo) + }); + + /** + * Format Different Type of Transaction such as TransferTransaction + * @param {object} transactionBody transaction body info. + * @returns {object} readable TransactionBody object + */ + static formatTransactionBody = transactionBody => { + switch (transactionBody.type) { + case TransactionType.TRANSFER: + return { + transactionType: transactionBody.type, + recipient: transactionBody.recipientAddress, + mosaics: transactionBody.mosaics.map(mosaic => ({ + ...mosaic, + id: mosaic.id.toHex(), + amount: mosaic.amount.compact().toString() + })), + message: transactionBody.message + }; + + case TransactionType.NAMESPACE_REGISTRATION: + let parentIdHex = transactionBody.parentId + ? transactionBody.parentId.toHex() + : ''; + + let duration = transactionBody.duration + ? transactionBody.duration.compact() + : 0; + + return { + transactionType: transactionBody.type, + recipient: http.networkConfig.NamespaceRentalFeeSinkAddress.address, + registrationType: + Constants.NamespaceRegistrationType[ + transactionBody.registrationType + ], + namespaceName: transactionBody.namespaceName, + namespaceId: transactionBody.namespaceId.toHex(), + parentId: + '' === parentIdHex ? Constants.Message.UNAVAILABLE : parentIdHex, + duration: 0 === duration ? Constants.Message.UNLIMITED : duration + }; + + case TransactionType.ADDRESS_ALIAS: + return { + transactionType: transactionBody.type, + aliasAction: Constants.AliasAction[transactionBody.aliasAction], + namespaceId: transactionBody.namespaceId.toHex(), + namespaceName: transactionBody.namespaceId.fullName, + address: transactionBody.address.address + }; + + case TransactionType.MOSAIC_ALIAS: + return { + transactionType: transactionBody.type, + aliasAction: Constants.AliasAction[transactionBody.aliasAction], + namespaceId: transactionBody.namespaceId.id.toHex(), + namespaceName: transactionBody.namespaceId.fullName, + mosaicId: transactionBody.mosaicId.id.toHex() + }; + + case TransactionType.MOSAIC_DEFINITION: + return { + transactionType: transactionBody.type, + recipient: http.networkConfig.MosaicRentalSinkAddress.address, + mosaicId: transactionBody.mosaicId.toHex(), + divisibility: transactionBody.divisibility, + duration: transactionBody.duration.compact(), + nonce: transactionBody.nonce.toHex(), + supplyMutable: transactionBody.flags.supplyMutable, + transferable: transactionBody.flags.transferable, + restrictable: transactionBody.flags.restrictable, + revokable: transactionBody.flags.revokable + }; + + case TransactionType.MOSAIC_SUPPLY_CHANGE: + return { + transactionType: transactionBody.type, + mosaicId: transactionBody.mosaicId.id.toHex(), + action: Constants.MosaicSupplyChangeAction[transactionBody.action], + delta: transactionBody.delta.compact() + }; + case TransactionType.MOSAIC_SUPPLY_REVOCATION: + return { + transactionType: transactionBody.type, + address: transactionBody.sourceAddress.address, + mosaics: [ + { + id: transactionBody.mosaic.id.toHex(), + amount: transactionBody.mosaic.amount.compact().toString() + } + ] + }; + + case TransactionType.MULTISIG_ACCOUNT_MODIFICATION: + return { + transactionType: transactionBody.type, + minApprovalDelta: transactionBody.minApprovalDelta, + minRemovalDelta: transactionBody.minRemovalDelta, + addressAdditions: transactionBody.addressAdditions.map(address => address.address), + addressDeletions: transactionBody.addressDeletions.map(address => address.address) + }; + + case TransactionType.AGGREGATE_COMPLETE: + return { + transactionType: transactionBody.type, + innerTransactions: transactionBody.innerTransactions, + cosignatures: transactionBody.cosignatures.map(cosigner => ({ + ...cosigner, + signer: cosigner.signer.address.address + })) + }; + + case TransactionType.AGGREGATE_BONDED: + return { + transactionType: transactionBody.type, + innerTransactions: transactionBody.innerTransactions, + cosignatures: transactionBody.cosignatures.map(cosigner => ({ + ...cosigner, + signer: cosigner.signer.address.address + })) + }; + + case TransactionType.HASH_LOCK: + return { + transactionType: transactionBody.type, + duration: transactionBody.duration.compact(), + mosaicId: transactionBody.mosaic.id.toHex(), + amount: helper.toNetworkCurrency(transactionBody.mosaic.amount), + hash: transactionBody.hash + }; + + case TransactionType.SECRET_LOCK: + return { + transactionType: transactionBody.type, + duration: transactionBody.duration.compact(), + mosaicId: transactionBody.mosaic.id.toHex(), + amount: helper.toNetworkCurrency(transactionBody.mosaic.amount), + secret: transactionBody.secret, + recipient: transactionBody.recipientAddress, + hashAlgorithm: + Constants.LockHashAlgorithm[transactionBody.hashAlgorithm] + }; + + case TransactionType.SECRET_PROOF: + return { + transactionType: transactionBody.type, + hashAlgorithm: + Constants.LockHashAlgorithm[transactionBody.hashAlgorithm], + recipient: transactionBody.recipientAddress, + secret: transactionBody.secret, + proof: transactionBody.proof + }; + case TransactionType.ACCOUNT_ADDRESS_RESTRICTION: + return { + transactionType: transactionBody.type, + restrictionType: + Constants.AddressRestrictionFlag[transactionBody.restrictionFlags], + restrictionAddressAdditions: transactionBody.restrictionAdditions.map(restriction => { + if (restriction instanceof Address) + return restriction.address; + + return restriction.fullName; + }), + restrictionAddressDeletions: transactionBody.restrictionDeletions.map(restriction => { + if (restriction instanceof Address) + return restriction.address; + + return restriction.fullName; + }) + }; + + case TransactionType.ACCOUNT_MOSAIC_RESTRICTION: + return { + transactionType: transactionBody.type, + restrictionType: + Constants.MosaicRestrictionFlag[transactionBody.restrictionFlags], + restrictionMosaicAdditions: transactionBody.restrictionAdditions.map(mosaic => mosaic.id.toHex()), + restrictionMosaicDeletions: transactionBody.restrictionDeletions.map(mosaic => mosaic.id.toHex()) + }; + + case TransactionType.ACCOUNT_OPERATION_RESTRICTION: + return { + transactionType: transactionBody.type, + restrictionType: + Constants.OperationRestrictionFlag[ + transactionBody.restrictionFlags + ], + restrictionOperationAdditions: transactionBody.restrictionAdditions, + restrictionOperationDeletions: transactionBody.restrictionDeletions + }; + + case TransactionType.MOSAIC_ADDRESS_RESTRICTION: + return { + transactionType: transactionBody.type, + mosaicId: transactionBody.mosaicId.toHex(), + targetAddress: transactionBody.targetAddress, + restrictionKey: transactionBody.restrictionKey.toHex(), + previousRestrictionValue: + transactionBody.previousRestrictionValue.toString(), + newRestrictionValue: transactionBody.newRestrictionValue.toString() + }; + + case TransactionType.MOSAIC_GLOBAL_RESTRICTION: + return { + transactionType: transactionBody.type, + referenceMosaicId: + '0000000000000000' === transactionBody.referenceMosaicId.toHex() + ? transactionBody.mosaicId.toHex() + : transactionBody.referenceMosaicId.toHex(), + restrictionKey: transactionBody.restrictionKey.toHex(), + previousRestrictionType: + Constants.MosaicRestrictionType[ + transactionBody.previousRestrictionType + ], + previousRestrictionValue: + transactionBody.previousRestrictionValue.compact(), + newRestrictionType: + Constants.MosaicRestrictionType[transactionBody.newRestrictionType], + newRestrictionValue: transactionBody.newRestrictionValue.compact() + }; + + case TransactionType.ACCOUNT_METADATA: + return { + transactionType: transactionBody.type, + scopedMetadataKey: transactionBody.scopedMetadataKey.toHex(), + targetAddress: transactionBody.targetAddress.address, + metadataValue: transactionBody.value, + valueSizeDelta: transactionBody.valueSizeDelta + }; + + case TransactionType.MOSAIC_METADATA: + return { + transactionType: transactionBody.type, + scopedMetadataKey: transactionBody.scopedMetadataKey.toHex(), + targetMosaicId: transactionBody.targetMosaicId.toHex(), + targetAddress: transactionBody.targetAddress.address, + metadataValue: transactionBody.value, + valueSizeDelta: transactionBody.valueSizeDelta + }; + + case TransactionType.NAMESPACE_METADATA: + return { + transactionType: transactionBody.type, + scopedMetadataKey: transactionBody.scopedMetadataKey.toHex(), + targetNamespaceId: transactionBody.targetNamespaceId.toHex(), + targetAddress: transactionBody.targetAddress.address, + metadataValue: transactionBody.value, + valueSizeDelta: transactionBody.valueSizeDelta + }; + case TransactionType.VOTING_KEY_LINK: + return { + transactionType: transactionBody.type, + linkAction: Constants.LinkAction[transactionBody.linkAction], + linkedPublicKey: transactionBody.linkedPublicKey, + linkedAccountAddress: Address.createFromPublicKey( + transactionBody.linkedPublicKey, + http.networkType + ).plain(), + startEpoch: transactionBody.startEpoch, + endEpoch: transactionBody.endEpoch + }; + case TransactionType.VRF_KEY_LINK: + case TransactionType.NODE_KEY_LINK: + case TransactionType.ACCOUNT_KEY_LINK: + return { + transactionType: transactionBody.type, + linkAction: Constants.LinkAction[transactionBody.linkAction], + linkedPublicKey: transactionBody.linkedPublicKey, + linkedAccountAddress: Address.createFromPublicKey( + transactionBody.linkedPublicKey, + http.networkType + ).plain() + }; + } + }; + + /** + * Format transactionInfoDTO. + * @param {object} transactionInfo transactionInfoDTO. + * @returns {object} readable transactionInfoDTO object. + */ + static formatTransactionInfo = transactionInfo => { + if (transactionInfo instanceof TransactionInfo) { + return { + ...transactionInfo, + height: transactionInfo.height.compact(), + timestamp: helper.networkTimestamp(Number(transactionInfo.timestamp.toString())) + }; + } + + if (transactionInfo instanceof AggregateTransactionInfo) { + return { + ...transactionInfo + }; + } + + return {}; + }; + + /** + * extend graphic value for transaction list. + * @param {object} transactionInfo formatted transaction info. + * @returns {array} graphicValue. + */ + static extendGraphicValue = transactionInfo => { + const { transactionBody } = transactionInfo; + + switch (transactionInfo.type) { + case TransactionType.TRANSFER: + return { + nativeMosaic: helper.getNetworkCurrencyBalance(transactionInfo.mosaics), + message: transactionBody.message, + mosaics: transactionBody.mosaics.filter(mosaic => + mosaic.id !== http.networkCurrency.mosaicId && + mosaic.id !== http.networkCurrency.namespaceId) + }; + case TransactionType.NAMESPACE_REGISTRATION: + return [ + { + namespace: { + namespaceId: transactionBody.namespaceId, + namespaceName: transactionBody.namespaceName + } + } + ]; + case TransactionType.ADDRESS_ALIAS: + return [{ linkAction: transactionBody.aliasAction }]; + case TransactionType.MOSAIC_ALIAS: + return [{ linkAction: transactionBody.aliasAction }]; + case TransactionType.MOSAIC_DEFINITION: + return [{ mosaicId: transactionBody.mosaicId }]; + case TransactionType.MOSAIC_SUPPLY_CHANGE: + return [{ action: transactionBody.action }]; + case TransactionType.MOSAIC_SUPPLY_REVOCATION: + return { + mosaics: transactionBody.mosaics + }; + case TransactionType.MULTISIG_ACCOUNT_MODIFICATION: + return [ + { minApprovalDelta: transactionBody.minApprovalDelta }, + { minRemovalDelta: transactionBody.minRemovalDelta } + ]; + case TransactionType.HASH_LOCK: + return [{ amount: transactionBody.amount }]; + case TransactionType.SECRET_LOCK: + return [{ secret: transactionBody.secret }]; + case TransactionType.SECRET_PROOF: + return [{ proof: transactionBody.proof }]; + case TransactionType.ACCOUNT_ADDRESS_RESTRICTION: + return [ + { + restrictionAddressAdditions: + transactionBody.restrictionAddressAdditions + }, + { + restrictionAddressDeletions: + transactionBody.restrictionAddressDeletions + } + ]; + case TransactionType.ACCOUNT_MOSAIC_RESTRICTION: + return [ + { + restrictionMosaicAdditions: + transactionBody.restrictionMosaicAdditions + }, + { + restrictionMosaicDeletions: + transactionBody.restrictionMosaicDeletions + } + ]; + case TransactionType.ACCOUNT_OPERATION_RESTRICTION: + return [ + { + restrictionOperationAdditions: + transactionBody.restrictionOperationAdditions + }, + { + restrictionOperationDeletions: + transactionBody.restrictionOperationDeletions + } + ]; + case TransactionType.MOSAIC_ADDRESS_RESTRICTION: + return [{ targetAddress: transactionBody.targetAddress }]; + case TransactionType.MOSAIC_GLOBAL_RESTRICTION: + return [{ referenceMosaicId: transactionBody.referenceMosaicId }]; + case TransactionType.ACCOUNT_METADATA: + case TransactionType.MOSAIC_METADATA: + case TransactionType.NAMESPACE_METADATA: + return [{ metadataValue: transactionBody.metadataValue }]; + case TransactionType.VOTING_KEY_LINK: + case TransactionType.VRF_KEY_LINK: + case TransactionType.NODE_KEY_LINK: + case TransactionType.ACCOUNT_KEY_LINK: + return [{ linkAction: transactionBody.linkAction }]; + default: + return []; + } + }; + + /** + * Build standalone transaction object for Vue components. + * @param {object} transactionDTO - transaction dto from SDK. + * @returns {object} formatted transactionObj. + */ + static createStandaloneTransactionFromSDK = async ( + transactionDTO, + { mosaicInfos, mosaicNames, unresolvedMosaicsMap } + ) => { + const transactionObj = { + ...transactionDTO, + transactionType: transactionDTO.type, + deadline: helper.networkTimestamp(transactionDTO.deadline.adjustedValue), + maxFee: helper.toNetworkCurrency(transactionDTO.maxFee), + signer: transactionDTO.signer.address.plain(), + transactionInfo: this.formatTransactionInfo(transactionDTO.transactionInfo) + }; + + switch (transactionDTO.type) { + case TransactionType.TRANSFER: + return CreateTransaction.transferTransaction(transactionObj, { + mosaicInfos, + mosaicNames, + unresolvedMosaicsMap + }); + case TransactionType.NAMESPACE_REGISTRATION: + return CreateTransaction.namespaceRegistration(transactionObj); + case TransactionType.ADDRESS_ALIAS: + return CreateTransaction.addressAlias(transactionObj); + case TransactionType.MOSAIC_ALIAS: + return CreateTransaction.mosaicAlias(transactionObj); + case TransactionType.MOSAIC_DEFINITION: + return CreateTransaction.mosaicDefinition(transactionObj); + case TransactionType.MOSAIC_SUPPLY_CHANGE: + return CreateTransaction.mosaicSupplyChange(transactionObj); + case TransactionType.MOSAIC_SUPPLY_REVOCATION: + return CreateTransaction.mosaicSupplyRevocation(transactionObj, { + mosaicInfos, + mosaicNames, + unresolvedMosaicsMap + }); + case TransactionType.MULTISIG_ACCOUNT_MODIFICATION: + return CreateTransaction.multisigAccountModification(transactionObj); + case TransactionType.HASH_LOCK: + return CreateTransaction.hashLock(transactionObj, { + mosaicInfos, + mosaicNames, + unresolvedMosaicsMap + }); + case TransactionType.SECRET_LOCK: + return CreateTransaction.secretLock(transactionObj, { + mosaicInfos, + mosaicNames, + unresolvedMosaicsMap + }); + case TransactionType.SECRET_PROOF: + return CreateTransaction.secretProof(transactionObj); + case TransactionType.ACCOUNT_ADDRESS_RESTRICTION: + return CreateTransaction.accountAddressRestriction(transactionObj); + case TransactionType.ACCOUNT_MOSAIC_RESTRICTION: + return CreateTransaction.accountMosaicRestriction(transactionObj); + case TransactionType.ACCOUNT_OPERATION_RESTRICTION: + return CreateTransaction.accountOperationRestriction(transactionObj); + case TransactionType.MOSAIC_ADDRESS_RESTRICTION: + return CreateTransaction.mosaicAddressRestriction(transactionObj); + case TransactionType.MOSAIC_GLOBAL_RESTRICTION: + return CreateTransaction.mosaicGlobalRestriction(transactionObj); + case TransactionType.ACCOUNT_METADATA: + return CreateTransaction.accountMetadata(transactionObj); + case TransactionType.MOSAIC_METADATA: + return CreateTransaction.mosaicMetadata(transactionObj); + case TransactionType.NAMESPACE_METADATA: + return CreateTransaction.namespaceMetadata(transactionObj); + case TransactionType.VOTING_KEY_LINK: + return CreateTransaction.votingKeyLink(transactionObj); + case TransactionType.VRF_KEY_LINK: + return CreateTransaction.vrfKeyLink(transactionObj); + case TransactionType.NODE_KEY_LINK: + return CreateTransaction.nodeKeyLink(transactionObj); + case TransactionType.ACCOUNT_KEY_LINK: + return CreateTransaction.accountKeyLink(transactionObj); + default: + throw new Error('Unimplemented transaction with type ' + transactionDTO.type); + } + }; + + /** + * Build transaction object for Vue components. + * @param {object} transactionDTO - transaction dto from SDK + * @returns {object} transactionObj + */ + static createTransactionFromSDK = async transactionDTO => { + if ( + transactionDTO.type === TransactionType.AGGREGATE_BONDED || + transactionDTO.type === TransactionType.AGGREGATE_COMPLETE + ) { + const { mosaicInfos, mosaicNames, unresolvedMosaicsMap } = + await helper.getTransactionMosaicInfoAndNamespace(transactionDTO.innerTransactions); + + const innerTransactions = transactionDTO.innerTransactions + ? await Promise.all(transactionDTO.innerTransactions.map(async (transaction, index) => { + return { + index: index + 1, + ...(await this.createStandaloneTransactionFromSDK(transaction, { + mosaicInfos, + mosaicNames, + unresolvedMosaicsMap + })) + }; + })) + : []; + + return { + ...transactionDTO, + innerTransactions, + cosignatures: transactionDTO.cosignatures + ? transactionDTO.cosignatures.map(cosignature => { + return { + ...cosignature, + signature: cosignature.signature, + signer: cosignature.signer.address.plain() + }; + }) + : [], + deadline: helper.networkTimestamp(transactionDTO.deadline.adjustedValue), + maxFee: helper.toNetworkCurrency(transactionDTO.maxFee), + signer: transactionDTO.signer.address.plain(), + transactionInfo: this.formatTransactionInfo(transactionDTO.transactionInfo), + transactionBody: { + transactionType: transactionDTO.type + } + }; + } else { + const { mosaicInfos, mosaicNames, unresolvedMosaicsMap } = + await helper.getTransactionMosaicInfoAndNamespace([transactionDTO]); + + return this.createStandaloneTransactionFromSDK(transactionDTO, { + mosaicInfos, + mosaicNames, + unresolvedMosaicsMap + }); + } + }; } export default TransactionService; diff --git a/src/infrastructure/http.js b/src/infrastructure/http.js index 57ffe3f1f..3e2ce43bb 100644 --- a/src/infrastructure/http.js +++ b/src/infrastructure/http.js @@ -20,7 +20,10 @@ import accountLabels from '../config/accountLabels'; import globalConfig from '../config/globalConfig'; import { NamespaceService } from '../infrastructure'; import * as symbol from 'symbol-sdk'; -import { Configuration, NodeApi } from 'symbol-statistics-service-typescript-fetch-client'; +import { + Configuration, + NodeApi +} from 'symbol-statistics-service-typescript-fetch-client'; let NODE_URL; @@ -39,140 +42,169 @@ let NATIVE_NAMESPACES; let EPOCH_ADJUSTMENT; export default class http { - static init = async (nodeUrl, marketDataUrl) => { - NODE_URL = nodeUrl; - MARKET_DATA_URL = marketDataUrl; - - [NETWORK_TYPE, GENERATION_HASH, NETWORK_PROPERTIES, EPOCH_ADJUSTMENT, NETWORK_CURRECY] = await Promise.all([ - http.createRepositoryFactory.getNetworkType().toPromise(), - http.createRepositoryFactory.getGenerationHash().toPromise(), - http.createRepositoryFactory.createNetworkRepository().getNetworkProperties() - .toPromise(), - http.createRepositoryFactory.getEpochAdjustment().toPromise(), - http.createRepositoryFactory.getCurrencies().toPromise() - ]); - - NATIVE_NAMESPACES = await NamespaceService.getNativeNamespaces() || []; - } - - static get networkCurrency () { - return { - namespaceName: NETWORK_CURRECY?.currency.namespaceId?.fullName || globalConfig.networkConfig.namespaceName, - namespaceId: NETWORK_CURRECY?.currency.namespaceId?.id?.toHex() || globalConfig.networkConfig.namespaceId, - mosaicId: NETWORK_CURRECY?.currency.mosaicId?.toHex() || undefined, - divisibility: NETWORK_CURRECY?.currency.divisibility || globalConfig.networkConfig.divisibility - }; - } - - static get nativeNamespaces () { - return NATIVE_NAMESPACES; - } - - static get networkProperties () { - return new symbol.NetworkConfiguration(NETWORK_PROPERTIES.network, NETWORK_PROPERTIES.chain, NETWORK_PROPERTIES.plugins); - } - - static get networkConfig () { - const { - chain: { totalChainImportance, blockGenerationTargetTime }, - plugins: { namespace, mosaic } - } = this.networkProperties; - const convertedTotalChainImportance = +totalChainImportance.replace(/'/g, ''); - const convertedNamespaceGracePeriodDuration = +namespace.namespaceGracePeriodDuration.replace(/d/g, ''); - const convertedBlockGenerationTargetTime = +blockGenerationTargetTime.replace(/s/g, ''); - const blockPerday = (60 / convertedBlockGenerationTargetTime) * 60 * 24; - - return { - MosaicRentalSinkAddress: symbol.Address.createFromRawAddress(mosaic.mosaicRentalFeeSinkAddress), - NamespaceRentalFeeSinkAddress: symbol.Address.createFromRawAddress(namespace.namespaceRentalFeeSinkAddress), - NetworkType: this.networkType, - NemsisTimestamp: this.epochAdjustment, - TargetBlockTime: convertedBlockGenerationTargetTime, - NamespaceGraceDuration: convertedNamespaceGracePeriodDuration * blockPerday, - TotalChainImportance: convertedTotalChainImportance - }; - } - - static get timezone () { - return globalConfig.timezone || 'Local'; - } - - static get marketDataUrl () { - return MARKET_DATA_URL; - } - - static get nodeUrl () { - return NODE_URL; - } - - static get generationHash () { - return GENERATION_HASH; - } - - static get networkType () { - return NETWORK_TYPE; - } - - static get epochAdjustment () { - return EPOCH_ADJUSTMENT; - } - - static get accountLabels () { - return accountLabels || {}; - } - - static get createRepositoryFactory () { - return new symbol.RepositoryFactoryHttp(this.nodeUrl, { - networkType: this.networkType, - generationHash: this.generationHash - }); - } - - static get mosaicService () { - const accountRepository = this.createRepositoryFactory.createAccountRepository(); - const mosaicRepository = this.createRepositoryFactory.createMosaicRepository(); - - return new symbol.MosaicService(accountRepository, mosaicRepository); - } - - static get namespaceService () { - const namespaceRepository = this.createRepositoryFactory.createNamespaceRepository(); - - return new symbol.NamespaceService(namespaceRepository); - } - - static get blockPaginationStreamer () { - return new symbol.BlockPaginationStreamer(this.createRepositoryFactory.createBlockRepository()); - } - - static transactionStatementPaginationStreamer () { - return symbol.ReceiptPaginationStreamer.transactionStatements(this.createRepositoryFactory.createReceiptRepository()); - } - - static addressResolutionStatementPaginationStreamer () { - return symbol.ReceiptPaginationStreamer.addressResolutionStatements(this.createRepositoryFactory.createReceiptRepository()); - } - - static mosaicResolutionStatementPaginationStreamer () { - return symbol.ReceiptPaginationStreamer.mosaicResolutionStatements(this.createRepositoryFactory.createReceiptRepository()); - } - - static get transactionPaginationStreamer () { - return new symbol.TransactionPaginationStreamer(this.createRepositoryFactory.createTransactionRepository()); - } - - static statisticServiceRestClient () { - try { - const statisticsServiceUrl = globalConfig.endpoints.statisticsService; - - if (statisticsServiceUrl && statisticsServiceUrl.length) { - return new NodeApi(new Configuration({ - fetchApi: fetch, - basePath: statisticsServiceUrl - })); - } else { throw Error('Statistics service endpoint is not provided'); } - } catch (error) { - console.error(error); - } - } + static init = async (nodeUrl, marketDataUrl) => { + NODE_URL = nodeUrl; + MARKET_DATA_URL = marketDataUrl; + + [ + NETWORK_TYPE, + GENERATION_HASH, + NETWORK_PROPERTIES, + EPOCH_ADJUSTMENT, + NETWORK_CURRECY + ] = await Promise.all([ + http.createRepositoryFactory.getNetworkType().toPromise(), + http.createRepositoryFactory.getGenerationHash().toPromise(), + http.createRepositoryFactory + .createNetworkRepository() + .getNetworkProperties() + .toPromise(), + http.createRepositoryFactory.getEpochAdjustment().toPromise(), + http.createRepositoryFactory.getCurrencies().toPromise() + ]); + + NATIVE_NAMESPACES = (await NamespaceService.getNativeNamespaces()) || []; + }; + + static get networkCurrency() { + return { + namespaceName: + NETWORK_CURRECY?.currency.namespaceId?.fullName || + globalConfig.networkConfig.namespaceName, + namespaceId: + NETWORK_CURRECY?.currency.namespaceId?.id?.toHex() || + globalConfig.networkConfig.namespaceId, + mosaicId: NETWORK_CURRECY?.currency.mosaicId?.toHex() || undefined, + divisibility: + NETWORK_CURRECY?.currency.divisibility || + globalConfig.networkConfig.divisibility + }; + } + + static get nativeNamespaces() { + return NATIVE_NAMESPACES; + } + + static get networkProperties() { + return new symbol.NetworkConfiguration( + NETWORK_PROPERTIES.network, + NETWORK_PROPERTIES.chain, + NETWORK_PROPERTIES.plugins + ); + } + + static get networkConfig() { + const { + chain: { totalChainImportance, blockGenerationTargetTime }, + plugins: { namespace, mosaic } + } = this.networkProperties; + const convertedTotalChainImportance = +totalChainImportance.replace( + /'/g, + '' + ); + const convertedNamespaceGracePeriodDuration = + +namespace.namespaceGracePeriodDuration.replace(/d/g, ''); + const convertedBlockGenerationTargetTime = + +blockGenerationTargetTime.replace(/s/g, ''); + const blockPerday = (60 / convertedBlockGenerationTargetTime) * 60 * 24; + + return { + MosaicRentalSinkAddress: symbol.Address.createFromRawAddress(mosaic.mosaicRentalFeeSinkAddress), + NamespaceRentalFeeSinkAddress: symbol.Address.createFromRawAddress(namespace.namespaceRentalFeeSinkAddress), + NetworkType: this.networkType, + NemsisTimestamp: this.epochAdjustment, + TargetBlockTime: convertedBlockGenerationTargetTime, + NamespaceGraceDuration: + convertedNamespaceGracePeriodDuration * blockPerday, + TotalChainImportance: convertedTotalChainImportance + }; + } + + static get timezone() { + return globalConfig.timezone || 'Local'; + } + + static get marketDataUrl() { + return MARKET_DATA_URL; + } + + static get nodeUrl() { + return NODE_URL; + } + + static get generationHash() { + return GENERATION_HASH; + } + + static get networkType() { + return NETWORK_TYPE; + } + + static get epochAdjustment() { + return EPOCH_ADJUSTMENT; + } + + static get accountLabels() { + return accountLabels || {}; + } + + static get createRepositoryFactory() { + return new symbol.RepositoryFactoryHttp(this.nodeUrl, { + networkType: this.networkType, + generationHash: this.generationHash + }); + } + + static get mosaicService() { + const accountRepository = + this.createRepositoryFactory.createAccountRepository(); + const mosaicRepository = + this.createRepositoryFactory.createMosaicRepository(); + + return new symbol.MosaicService(accountRepository, mosaicRepository); + } + + static get namespaceService() { + const namespaceRepository = + this.createRepositoryFactory.createNamespaceRepository(); + + return new symbol.NamespaceService(namespaceRepository); + } + + static get blockPaginationStreamer() { + return new symbol.BlockPaginationStreamer(this.createRepositoryFactory.createBlockRepository()); + } + + static transactionStatementPaginationStreamer() { + return symbol.ReceiptPaginationStreamer.transactionStatements(this.createRepositoryFactory.createReceiptRepository()); + } + + static addressResolutionStatementPaginationStreamer() { + return symbol.ReceiptPaginationStreamer.addressResolutionStatements(this.createRepositoryFactory.createReceiptRepository()); + } + + static mosaicResolutionStatementPaginationStreamer() { + return symbol.ReceiptPaginationStreamer.mosaicResolutionStatements(this.createRepositoryFactory.createReceiptRepository()); + } + + static get transactionPaginationStreamer() { + return new symbol.TransactionPaginationStreamer(this.createRepositoryFactory.createTransactionRepository()); + } + + static statisticServiceRestClient() { + try { + const statisticsServiceUrl = globalConfig.endpoints.statisticsService; + + if (statisticsServiceUrl && statisticsServiceUrl.length) { + return new NodeApi(new Configuration({ + fetchApi: fetch, + basePath: statisticsServiceUrl + })); + } else { + throw Error('Statistics service endpoint is not provided'); + } + } catch (error) { + console.error(error); + } + } } diff --git a/src/store/account.js b/src/store/account.js index 7ef56fa0a..c00cdb184 100644 --- a/src/store/account.js +++ b/src/store/account.js @@ -37,37 +37,39 @@ import { Address } from 'symbol-sdk'; const managers = [ new Pagination({ name: 'timeline', - fetchFunction: (pageInfo, filterValue) => AccountService.getAccountList(pageInfo, filterValue), + fetchFunction: (pageInfo, filterValue) => + AccountService.getAccountList(pageInfo, filterValue), pageInfo: { pageSize: Constants.PageSize }, filter: filters.account }), - new DataSet( - 'info', - address => AccountService.getAccountInfo(address) - ), - new DataSet( - 'OwnedMosaic', - address => AccountService.getAccountMosaicList(address) - ), + new DataSet('info', address => AccountService.getAccountInfo(address)), + new DataSet('OwnedMosaic', address => + AccountService.getAccountMosaicList(address)), new Pagination({ name: 'OwnedNamespace', fetchFunction: (pageInfo, filterValue, store) => - AccountService.getAccountNamespaceList(pageInfo, filterValue, store.getters.getCurrentAccountAddress), + AccountService.getAccountNamespaceList( + pageInfo, + filterValue, + store.getters.getCurrentAccountAddress + ), pageInfo: { pageSize: 10 }, filter: filters.namespace }), - new DataSet( - 'multisig', - address => MultisigService.getMultisigAccountInfo(address) - ), + new DataSet('multisig', address => + MultisigService.getMultisigAccountInfo(address)), new Pagination({ name: 'transactions', fetchFunction: (pageInfo, filterValue, store) => - AccountService.getAccountTransactionList(pageInfo, filterValue, store.getters.getCurrentAccountAddress), + AccountService.getAccountTransactionList( + pageInfo, + filterValue, + store.getters.getCurrentAccountAddress + ), pageInfo: { pageSize: 10 }, @@ -76,7 +78,10 @@ const managers = [ new Pagination({ name: 'harvestedBlocks', fetchFunction: (pageInfo, filterValue, store) => - AccountService.getAccountHarvestedReceiptList(pageInfo, store.getters.getCurrentAccountAddress), + AccountService.getAccountHarvestedReceiptList( + pageInfo, + store.getters.getCurrentAccountAddress + ), pageInfo: { pageSize: 10 } @@ -84,7 +89,11 @@ const managers = [ new Pagination({ name: 'receipt', fetchFunction: (pageInfo, filterValue, store) => - AccountService.getAccountReceiptList(pageInfo, filterValue, store.getters.getCurrentAccountAddress), + AccountService.getAccountReceiptList( + pageInfo, + filterValue, + store.getters.getCurrentAccountAddress + ), pageInfo: { pageSize: 10 }, @@ -93,7 +102,10 @@ const managers = [ new Pagination({ name: 'mosaicAddressRestrictions', fetchFunction: (pageInfo, filterValue, store) => - RestrictionService.getMosaicAddressRestrictionList(pageInfo, store.getters.getCurrentAccountAddress), + RestrictionService.getMosaicAddressRestrictionList( + pageInfo, + store.getters.getCurrentAccountAddress + ), pageInfo: { pageSize: Constants.PageSize } @@ -101,7 +113,11 @@ const managers = [ new Pagination({ name: 'metadatas', fetchFunction: (pageInfo, filterValue, store) => - AccountService.getAccountMetadataList(pageInfo, filterValue, store.getters.getCurrentAccountAddress), + AccountService.getAccountMetadataList( + pageInfo, + filterValue, + store.getters.getCurrentAccountAddress + ), pageInfo: { pageSize: 10 }, @@ -110,7 +126,10 @@ const managers = [ new Pagination({ name: 'hashLocks', fetchFunction: (pageInfo, filterValue, store) => - AccountService.getAccountHashLockList(pageInfo, store.getters.getCurrentAccountAddress), + AccountService.getAccountHashLockList( + pageInfo, + store.getters.getCurrentAccountAddress + ), pageInfo: { pageSize: 10 } @@ -118,15 +137,16 @@ const managers = [ new Pagination({ name: 'secretLocks', fetchFunction: (pageInfo, filterValue, store) => - AccountService.getAccountSecretLockList(pageInfo, store.getters.getCurrentAccountAddress), + AccountService.getAccountSecretLockList( + pageInfo, + store.getters.getCurrentAccountAddress + ), pageInfo: { pageSize: 10 } }), - new DataSet( - 'accountRestrictions', - address => RestrictionService.getAccountRestrictionList(address) - ) + new DataSet('accountRestrictions', address => + RestrictionService.getAccountRestrictionList(address)) ]; const LOCK = Lock.create(); @@ -143,15 +163,18 @@ export default { ...getGettersFromManagers(managers), getInitialized: state => state.initialized, getActivityBucketList: state => state.info?.data.activityBucket || [], - getSupplementalPublicKeys: state => state.info?.data.supplementalPublicKeys || {}, + getSupplementalPublicKeys: state => + state.info?.data.supplementalPublicKeys || {}, getVotingKeyList: state => state.info?.data.votingList || [], getCurrentAccountAddress: state => state.currentAccountAddress, balanceWidget: (state, getters) => ({ - address: state.currentAccountAddress ? Address - .createFromRawAddress(state.currentAccountAddress) - .pretty() : '', + address: state.currentAccountAddress + ? Address.createFromRawAddress(state.currentAccountAddress).pretty() + : '', mosaic: getters.OwnedMosaic?.data[0], - alias: getters.info?.data?.accountAliasNames /* || Constants.Message.UNAVAILABLE */ + alias: + getters.info?.data + ?.accountAliasNames /* || Constants.Message.UNAVAILABLE */ }) }, mutations: { @@ -166,7 +189,7 @@ export default { actions: { ...getActionsFromManagers(managers), // Initialize the account model. - async initialize ({ commit, dispatch, getters }) { + async initialize({ commit, dispatch, getters }) { const callback = async () => { await dispatch('initializePage'); }; @@ -175,22 +198,22 @@ export default { }, // Uninitialize the account model. - async uninitialize ({ commit, dispatch, getters }) { + async uninitialize({ commit, dispatch, getters }) { const callback = async () => { dispatch('uninitializeDetail'); - getters.timeline?.uninitialize(); + getters.timeline?.uninitialize(); }; await LOCK.uninitialize(callback, commit, dispatch, getters); }, // Fetch data from the SDK and initialize the page. - initializePage (context) { + initializePage(context) { context.getters.timeline.setStore(context).initialFetch(); }, // Fetch account data by address, publicKey or alias namespaceName - async fetchAccountDetail (context, payload) { + async fetchAccountDetail(context, payload) { let { address } = payload; try { @@ -208,22 +231,28 @@ export default { context.getters.OwnedNamespace.setStore(context).initialFetch(address); context.getters.multisig.setStore(context).initialFetch(address); context.getters.metadatas.setStore(context).initialFetch(address); - context.getters.mosaicAddressRestrictions.setStore(context).initialFetch(address); + context.getters.mosaicAddressRestrictions + .setStore(context) + .initialFetch(address); context.getters.harvestedBlocks.setStore(context).initialFetch(address); - context.getters.accountRestrictions.setStore(context).initialFetch(address); + context.getters.accountRestrictions + .setStore(context) + .initialFetch(address); context.getters.hashLocks.setStore(context).initialFetch(address); context.getters.secretLocks.setStore(context).initialFetch(address); context.getters.receipt.setStore(context).initialFetch(address); }, - uninitializeDetail (context) { + uninitializeDetail(context) { context.getters.info.setStore(context).uninitialize(); context.getters.OwnedMosaic.setStore(context).uninitialize(); context.getters.OwnedNamespace.setStore(context).uninitialize(); context.getters.multisig.setStore(context).uninitialize(); context.getters.transactions.setStore(context).uninitialize(); context.getters.metadatas.setStore(context).uninitialize(); - context.getters.mosaicAddressRestrictions.setStore(context).uninitialize(); + context.getters.mosaicAddressRestrictions + .setStore(context) + .uninitialize(); context.getters.harvestedBlocks.setStore(context).uninitialize(); context.getters.accountRestrictions.setStore(context).uninitialize(); context.getters.hashLocks.setStore(context).uninitialize(); diff --git a/src/store/namespace.js b/src/store/namespace.js index 296e0a157..cef7e27f2 100644 --- a/src/store/namespace.js +++ b/src/store/namespace.js @@ -33,24 +33,25 @@ const LOCK = Lock.create(); const managers = [ new Pagination({ name: 'timeline', - fetchFunction: (pageInfo, filterValue) => NamespaceService.getNamespaceList(pageInfo, filterValue), + fetchFunction: (pageInfo, filterValue) => + NamespaceService.getNamespaceList(pageInfo, filterValue), pageInfo: { pageSize: Constants.PageSize }, filter: filters.namespace }), - new DataSet( - 'info', - namespaceOrHex => NamespaceService.getNamespaceInfo(namespaceOrHex) - ), - new DataSet( - 'namespaceLevel', - namespaceOrHex => NamespaceService.getNamespaceLevelList(namespaceOrHex) - ), + new DataSet('info', namespaceOrHex => + NamespaceService.getNamespaceInfo(namespaceOrHex)), + new DataSet('namespaceLevel', namespaceOrHex => + NamespaceService.getNamespaceLevelList(namespaceOrHex)), new Pagination({ name: 'metadatas', fetchFunction: (pageInfo, filterValue, store) => - NamespaceService.getNamespaceMetadataList(pageInfo, filterValue, store.getters.getCurrentNamespaceId), + NamespaceService.getNamespaceMetadataList( + pageInfo, + filterValue, + store.getters.getCurrentNamespaceId + ), pageInfo: { pageSize: 10 }, @@ -59,7 +60,10 @@ const managers = [ new Pagination({ name: 'balanceTransferReceipt', fetchFunction: (pageInfo, filterValue, store) => - NamespaceService.getNamespaceBalanceTransferReceipt(pageInfo, store.getters.getCurrentNamespaceId), + NamespaceService.getNamespaceBalanceTransferReceipt( + pageInfo, + store.getters.getCurrentNamespaceId + ), pageInfo: { pageSize: 10 } @@ -67,7 +71,10 @@ const managers = [ new Pagination({ name: 'artifactExpiryReceipt', fetchFunction: (pageInfo, filterValue, store) => - NamespaceService.getNamespaceArtifactExpiryReceipt(pageInfo, store.getters.getCurrentNamespaceId), + NamespaceService.getNamespaceArtifactExpiryReceipt( + pageInfo, + store.getters.getCurrentNamespaceId + ), pageInfo: { pageSize: 10 } @@ -85,7 +92,8 @@ export default { getters: { getInitialized: state => state.initialized, getCurrentNamespaceId: state => state.currentNamespaceId, - getRecentList: state => state.timeline?.data?.filter((item, index) => 4 > index) || [], + getRecentList: state => + state.timeline?.data?.filter((item, index) => 4 > index) || [], ...getGettersFromManagers(managers) }, mutations: { @@ -100,7 +108,7 @@ export default { actions: { ...getActionsFromManagers(managers), // Initialize the namespace model. - async initialize ({ commit, dispatch, getters }) { + async initialize({ commit, dispatch, getters }) { const callback = async () => { await dispatch('initializePage'); }; @@ -109,7 +117,7 @@ export default { }, // Uninitialize the namespace model. - async uninitialize ({ commit, dispatch, getters }) { + async uninitialize({ commit, dispatch, getters }) { const callback = async () => { dispatch('uninitializeDetail'); getters.timeline?.uninitialize(); @@ -119,22 +127,30 @@ export default { }, // Fetch data from the SDK and initialize the page. - initializePage (context) { + initializePage(context) { context.getters.timeline.setStore(context).initialFetch(); }, // Fetch data from the SDK. - fetchNamespaceInfo (context, payload) { + fetchNamespaceInfo(context, payload) { context.dispatch('uninitializeDetail'); context.commit('setCurrentNamespaceId', payload.namespaceId); context.getters.info.setStore(context).initialFetch(payload.namespaceId); - context.getters.namespaceLevel.setStore(context).initialFetch(payload.namespaceId); - context.getters.metadatas.setStore(context).initialFetch(payload.namespaceId); - context.getters.balanceTransferReceipt.setStore(context).initialFetch(payload.namespaceId); - context.getters.artifactExpiryReceipt.setStore(context).initialFetch(payload.namespaceId); + context.getters.namespaceLevel + .setStore(context) + .initialFetch(payload.namespaceId); + context.getters.metadatas + .setStore(context) + .initialFetch(payload.namespaceId); + context.getters.balanceTransferReceipt + .setStore(context) + .initialFetch(payload.namespaceId); + context.getters.artifactExpiryReceipt + .setStore(context) + .initialFetch(payload.namespaceId); }, - uninitializeDetail (context) { + uninitializeDetail(context) { context.getters.info.setStore(context).uninitialize(); context.getters.namespaceLevel.setStore(context).uninitialize(); context.getters.metadatas.setStore(context).uninitialize(); From 9ab046d330e0a9ccc6cb3df9c5f51714673cbd4a Mon Sep 17 00:00:00 2001 From: Anthony Law Date: Sat, 25 Feb 2023 01:59:35 +0800 Subject: [PATCH 6/7] [explorer] fix: unable load peer node in page, and filter only available node in list #1167 * [explorer] task: refactor on the method, and filter available node from network * [explorer] task: refactor node stats widget, rename method and unit test * [explorer] fix: peer node unable load on node page * [explorer] task: unit test improvement * [explorer] task: more unit test improvement * [explorer] fix: lint issue --- __tests__/TestHelper.js | 47 ++++ .../widgets/NodeStatsWidget.spec.js | 121 +++++++++ __tests__/infrastructure/NodeService.spec.js | 243 ++++++++++++++++++ src/components/widgets/NodeStatsWidget.vue | 37 +-- src/config/pages/node-detail.json | 14 + src/infrastructure/NodeService.js | 160 +++++++----- src/store/chain.js | 2 +- src/store/node.js | 2 +- 8 files changed, 526 insertions(+), 100 deletions(-) create mode 100644 __tests__/components/widgets/NodeStatsWidget.spec.js create mode 100644 __tests__/infrastructure/NodeService.spec.js diff --git a/__tests__/TestHelper.js b/__tests__/TestHelper.js index 3899f2230..58ba7d1b7 100644 --- a/__tests__/TestHelper.js +++ b/__tests__/TestHelper.js @@ -364,6 +364,53 @@ const TestHelper = { cosignatures, innerTransactions }; + }, + generateNodePeerStatus: isAvailable => { + return { + isAvailable, + lastStatusCheck: 1676809816662 + }; + }, + generateNodeApiStatus: isAvailable => { + return { + isAvailable, + nodePublicKey: '4DA6FB57FA168EEBBCB68DA4DDC8DA7BCF41EC93FB22A33DF510DB0F2670F623', + chainHeight: 2027193, + finalization: { + height: 2031992, + epoch: 1413, + point: 7, + hash: '6B687D9B689611C90A1094A7430E78914F22A2570C80D3E42D520EB08091A973' + }, + nodeStatus: { + apiNode: 'up', + db: 'up' + }, + restVersion: '2.4.2', + restGatewayUrl: 'localhost.com', + isHttpsEnabled: true + }; + }, + nodeCommonField: { + version: 16777989, + publicKey: '016DC1622EE42EF9E4D215FA1112E89040DD7AED83007283725CE9BA550272F5', + networkGenerationHashSeed: '57F7DA205008026C776CB6AED843393F04CD458E0AA2D9F1D5F31A402072B2D6', + port: 7900, + networkIdentifier: 104, + host: 'node.com', + friendlyName: 'node', + lastAvailable: '2023-02-19T12:36:04.524Z', + hostDetail: {}, + location: '', + ip: '127.0.0.1', + organization: '', + as: '', + continent: '', + country: '', + region: '', + city: '', + district: '', + zip: '' } }; diff --git a/__tests__/components/widgets/NodeStatsWidget.spec.js b/__tests__/components/widgets/NodeStatsWidget.spec.js new file mode 100644 index 000000000..74c3941c7 --- /dev/null +++ b/__tests__/components/widgets/NodeStatsWidget.spec.js @@ -0,0 +1,121 @@ +import NodeStatsWidget from '../../../src/components/widgets/NodeStatsWidget.vue'; +import { i18n } from '../../../src/config'; +import { createLocalVue, mount } from '@vue/test-utils'; +import Vuex from 'vuex'; + +const setupStoreMount = stats => { + const nodeModule = { + namespaced: true, + getters: { + nodeStats:() => stats + } + }; + + const uiModule = { + namespaced: true, + getters: { + getNameByKey: state => key => i18n.getName(key) + } + }; + + const store = new Vuex.Store({ + modules: { + node: nodeModule, + ui:uiModule + } + }); + + const propsData = { + dataGetter: 'node/nodeStats' + }; + + return mount(NodeStatsWidget, { + store, + localVue, + propsData, + stubs: { + 'b-card': true, + 'b-container': true, + 'b-row': true, + 'b-col': true, + 'b-button': true, + 'router-link': true + } + }); +}; + +const localVue = createLocalVue(); +localVue.use(Vuex); + +describe('NodeStatsWidget', () => { + describe('nodeRoles', () => { + const assertNodeStatCounts = (stats, expected) => { + // Arrange + Act: + const wrapper = setupStoreMount(stats); + + // Assert: + expect(wrapper.vm.nodeRoles).toEqual(expected); + }; + + const generateNodeStats = stats => { + return [ + { + name: 'Total count', + count: stats.reduce((acc, val) => acc + val, 0) + }, + { + name: 'Peer node', + count: stats[0] + }, + { + name: 'Api node', + count: stats[1] + }, + { + name: 'Peer Api node', + count: stats[2] + }, + { + name: 'Voting node', + count: stats[3] + }, + { + name: 'Peer Voting node', + count: stats[4] + }, + { + name: 'Api Voting node', + count: stats[5] + }, + { + name: 'Peer Api Voting node', + count: stats[6] + } + ]; + }; + + it('returns node types stats when data is present', () => { + assertNodeStatCounts( + { + 1: 3, + 2: 7, + 3: 4, + 4: 6, + 5: 8, + 6: 12, + 7: 5 + }, + generateNodeStats([3, 7, 4, 6, 8, 12, 5]) + ); + }); + + it('returns node types stats with 0 count when no data is present', () => { + assertNodeStatCounts( + {}, + generateNodeStats([0, 0, 0, 0, 0, 0, 0]) + ); + }); + + it('returns empty when data is undefined', () => assertNodeStatCounts(undefined, [])); + }); +}); diff --git a/__tests__/infrastructure/NodeService.spec.js b/__tests__/infrastructure/NodeService.spec.js new file mode 100644 index 000000000..d818eeca5 --- /dev/null +++ b/__tests__/infrastructure/NodeService.spec.js @@ -0,0 +1,243 @@ +import { NodeService } from '../../src/infrastructure'; +import http from '../../src/infrastructure/http'; +import TestHelper from '../TestHelper'; + +describe('Node Service', () => { + // Arrange: + const { + generateNodePeerStatus, + generateNodeApiStatus, + nodeCommonField + } = TestHelper; + + const statisticServiceNodeResponse = [ + { + roles: 1, + peerStatus: generateNodePeerStatus(true), + ...nodeCommonField + }, + { + roles: 2, + apiStatus: generateNodeApiStatus(false), + ...nodeCommonField + }, + { + roles: 3, + peerStatus: generateNodePeerStatus(true), + apiStatus: generateNodeApiStatus(false), + ...nodeCommonField + }, + { + roles: 3, + peerStatus: generateNodePeerStatus(true), + apiStatus: generateNodeApiStatus(true), + ...nodeCommonField + }, + { + roles: 3, + peerStatus: generateNodePeerStatus(false), + apiStatus: generateNodeApiStatus(false), + ...nodeCommonField + }, + { + roles: 5, + peerStatus: generateNodePeerStatus(true), + ...nodeCommonField + }, + { + roles: 5, + peerStatus: generateNodePeerStatus(false), + ...nodeCommonField + }, + { + roles: 7, + peerStatus: generateNodePeerStatus(true), + apiStatus: generateNodeApiStatus(true), + ...nodeCommonField + } + ]; + + const runStatisticServiceFailResponseTests = (statisticServiceMethod, NodeServiceMethod) => { + it('throws error when statistic services fail response', async () => { + // Arrange: + const error = new Error(`Statistics service ${statisticServiceMethod} error`); + + http.statisticServiceRestClient = jest.fn().mockImplementation(() => { + return { + [statisticServiceMethod]: jest.fn().mockRejectedValue(error) + }; + }); + + // Act + Assert: + await expect(NodeService[NodeServiceMethod]()).rejects.toThrow(error); + }); + }; + + describe('getAvailableNodes', () => { + it('returns available node from statistic services', async () => { + // Arrange: + http.statisticServiceRestClient = jest.fn().mockImplementation(() => { + return { + getNodes: jest.fn().mockResolvedValue(statisticServiceNodeResponse) + }; + }); + + const expectNodeFormattedCommonField = { + network: 'MAINNET', + address: 'NDY2CXBR6SK3G7UWVXZT6YQTVJKHKFMPU74ZOYY', + nodePublicKey: + '016DC1622EE42EF9E4D215FA1112E89040DD7AED83007283725CE9BA550272F5', + version: '1.0.3.5' + }; + + // Act: + const result = await NodeService.getAvailableNodes(); + + // Assert: + expect(result).toEqual([ + { + ...nodeCommonField, + ...expectNodeFormattedCommonField, + apiEndpoint: 'N/A', + roles: 'Peer node', + rolesRaw: 1, + peerStatus: generateNodePeerStatus(true) + }, + { + ...nodeCommonField, + ...expectNodeFormattedCommonField, + apiEndpoint: 'localhost.com', + roles: 'Peer Api node', + rolesRaw: 3, + peerStatus: generateNodePeerStatus(true), + apiStatus: generateNodeApiStatus(false) + }, + { + ...nodeCommonField, + ...expectNodeFormattedCommonField, + apiEndpoint: 'localhost.com', + roles: 'Peer Api node', + rolesRaw: 3, + peerStatus: generateNodePeerStatus(true), + apiStatus: generateNodeApiStatus(true) + }, + { + ...nodeCommonField, + ...expectNodeFormattedCommonField, + apiEndpoint: 'N/A', + roles: 'Peer Voting node', + rolesRaw: 5, + peerStatus: generateNodePeerStatus(true) + }, + { + ...nodeCommonField, + ...expectNodeFormattedCommonField, + apiEndpoint: 'localhost.com', + roles: 'Peer Api Voting node', + rolesRaw: 7, + peerStatus: generateNodePeerStatus(true), + apiStatus: generateNodeApiStatus(true) + } + ]); + }); + + runStatisticServiceFailResponseTests('getNodes', 'getAvailableNodes'); + }); + + describe('getNodeStats', () => { + it('return nodes count with 7 types of roles', async () => { + // Arrange: + http.statisticServiceRestClient = jest.fn().mockImplementation(() => { + return { + getNodes: jest.fn().mockResolvedValue(statisticServiceNodeResponse) + }; + }); + + // Act: + const nodeStats = await NodeService.getNodeStats(); + + // Assert: + expect(nodeStats).toEqual({ + 1: 1, + 2: 0, + 3: 2, + 4: 0, + 5: 1, + 6: 0, + 7: 1 + }); + }); + }); + + describe('getNodeInfo', () => { + // Arrange: + Date.now = jest.fn(() => new Date('2023-02-21')); + + const expectedPeerStatus = { + connectionStatus: true, + lastStatusCheck: '2023-02-19 12:30:16' + }; + + const expectedAPIStatus = { + apiNodeStatus: true, + connectionStatus: false, + databaseStatus: true, + isHttpsEnabled: true, + lastStatusCheck: '2023-02-21 00:00:00', + restVersion: '2.4.2' + }; + + const expectedChainInfoStatus = { + height: 2027193, + finalizedHeight: 2031992, + finalizationEpoch: 1413, + finalizationPoint: 7, + finalizedHash: '6B687D9B689611C90A1094A7430E78914F22A2570C80D3E42D520EB08091A973', + lastStatusCheck: '2023-02-21 00:00:00' + }; + + const assertNodeStatus = async (node, expectedResult) => { + // Arrange: + http.statisticServiceRestClient = jest.fn().mockImplementation(() => { + return { + getNode: jest.fn().mockResolvedValue(node) + }; + }); + + // Act: + const { apiStatus, chainInfo, peerStatus } = + await NodeService.getNodeInfo(node.publicKey); + + // Assert: + expect(apiStatus).toEqual(expectedResult.apiStatus); + expect(chainInfo).toEqual(expectedResult.chainInfo); + expect(peerStatus).toEqual(expectedResult.peerStatus); + }; + + it('returns peer node status when peer status is present', async () => { + await assertNodeStatus(statisticServiceNodeResponse[0], { + peerStatus: expectedPeerStatus, + apiStatus: {}, + chainInfo: {} + }); + }); + + it('returns api node status and chain info when api status is present', async () => { + await assertNodeStatus(statisticServiceNodeResponse[1], { + peerStatus: {}, + apiStatus: expectedAPIStatus, + chainInfo: expectedChainInfoStatus + }); + }); + + it('returns chain info, api and peer node status when both status is present', async () => { + await assertNodeStatus(statisticServiceNodeResponse[2], { + peerStatus: expectedPeerStatus, + apiStatus: expectedAPIStatus, + chainInfo: expectedChainInfoStatus + }); + }); + + runStatisticServiceFailResponseTests('getNode', 'getNodeInfo'); + }); +}); diff --git a/src/components/widgets/NodeStatsWidget.vue b/src/components/widgets/NodeStatsWidget.vue index 8216fcf6d..f4d28a100 100644 --- a/src/components/widgets/NodeStatsWidget.vue +++ b/src/components/widgets/NodeStatsWidget.vue @@ -38,10 +38,6 @@ import Card from '@/components/containers/Card.vue'; import ButtonMore from '@/components/controls/ButtonMore.vue'; import Constants from '../../config/constants'; -import IconOrange from '../../styles/img/connector_orange.png'; -import IconBlue from '../../styles/img/connector_blue.png'; -import IconGreen from '../../styles/img/connector_green.png'; -import IconPink from '../../styles/img/connector_pink.png'; export default { components: { @@ -70,7 +66,7 @@ export default { }, nodeRoles () { - const data = this.data?.nodeTypes; + const data = this.data; if (!data) return []; @@ -78,50 +74,35 @@ export default { return [ { name: this.getNameByKey('allNodes'), - count: Array.from(Array(8).keys()).reduce((acc, val) => acc + (data[val] || 0)), - icon: IconBlue + count: Array.from(Array(8).keys()).reduce((acc, val) => acc + (data[val] || 0)) }, { name: Constants.RoleType[1], - count: data[1] || 0, - icon: IconBlue, - color: 'blue' + count: data[1] || 0 }, { name: Constants.RoleType[2], - count: data[2] || 0, - icon: IconPink, - color: 'pink' + count: data[2] || 0 }, { name: Constants.RoleType[3], - count: data[3] || 0, - icon: IconPink, - color: 'pink' + count: data[3] || 0 }, { name: Constants.RoleType[4], - count: data[4] || 0, - icon: IconGreen, - color: 'green' + count: data[4] || 0 }, { name: Constants.RoleType[5], - count: data[5] || 0, - icon: IconGreen, - color: 'green' + count: data[5] || 0 }, { name: Constants.RoleType[6], - count: data[6] || 0, - icon: IconOrange, - color: 'orange' + count: data[6] || 0 }, { name: Constants.RoleType[7], - count: data[7] || 0, - icon: IconOrange, - color: 'orange' + count: data[7] || 0 } ]; }, diff --git a/src/config/pages/node-detail.json b/src/config/pages/node-detail.json index 63c7ffea6..376f975be 100644 --- a/src/config/pages/node-detail.json +++ b/src/config/pages/node-detail.json @@ -83,6 +83,20 @@ "lastStatusCheck" ] }, + { + "layoutOptions": "adaptive", + "type": "CardTable", + "title": "nodePeerStatusTitle", + "managerGetter": "node/info", + "dataGetter": "node/peerStatus", + "errorMessage": "nodeDetailError", + "pagination": "none", + "hideEmptyData": true, + "fields": [ + "connectionStatus", + "lastStatusCheck" + ] + }, { "layoutOptions": "full-width", "type": "CardTable", diff --git a/src/infrastructure/NodeService.js b/src/infrastructure/NodeService.js index 98a7c4966..39f93635f 100644 --- a/src/infrastructure/NodeService.js +++ b/src/infrastructure/NodeService.js @@ -45,24 +45,6 @@ class NodeService { .toPromise(); }; - /** - * Get Node Peers from symbol SDK. - * @returns {array} NodeInfo[] - */ - static getNodePeers = async () => { - let nodePeers = []; - - try { - nodePeers = await http.statisticServiceRestClient().getNodes(); - } catch (e) { - console.error('Statistics service getNodes error: ', e); - } - - return nodePeers - .map(nodeInfo => this.formatNodeInfo(nodeInfo)) - .sort((a, b) => a.friendlyName.localeCompare(b.friendlyName)); - }; - /** * Get node health status by endpoint. * @param {string} currentUrl api-node endpoint such as http:localhost:3000 @@ -106,15 +88,40 @@ class NodeService { }); /** - * Format Node Peers dataset into Vue Component. + * Get available node list from statistic service. + * @returns {array} NodeInfo[] + */ + static getAvailableNodes = async () => { + try { + const nodePeers = await http.statisticServiceRestClient().getNodes(); + + return nodePeers + .filter(({ apiStatus, roles, peerStatus }) => { + if (1 === roles || 4 === roles || 5 === roles) + return peerStatus?.isAvailable; + else if (3 === roles || 6 === roles || 7 === roles) + return apiStatus?.isAvailable || peerStatus?.isAvailable; + else + return apiStatus?.isAvailable; + }) + .map(nodeInfo => this.formatNodeInfo(nodeInfo)) + .sort((a, b) => a.friendlyName.localeCompare(b.friendlyName)); + } catch (e) { + console.error(e); + throw Error('Statistics service getNodes error'); + } + }; + + /** + * Format Available Node Peers dataset into Vue Component. * @param {string} filter role filter. * @returns {object} Node peers object for Vue component. */ - static getNodePeerList = async filter => { - let nodePeers = await this.getNodePeers(); + static getAvailableNodeList = async filter => { + const availableNodes = await this.getAvailableNodes(); return { - data: nodePeers + data: availableNodes .filter(el => !filter.rolesRaw || el.rolesRaw === filter.rolesRaw) .map(el => { let node = { @@ -158,46 +165,35 @@ class NodeService { }; static getNodeInfo = async publicKey => { - let node = {}; - try { - node = await http.statisticServiceRestClient().getNode(publicKey); - } catch (e) { - throw Error('Statistics service getNode error: ', e); - } - const formattedNode = this.formatNodeInfo(node); - - if ( - 2 === formattedNode.rolesRaw || - 3 === formattedNode.rolesRaw || - 6 === formattedNode.rolesRaw || - 7 === formattedNode.rolesRaw - ) { - const { - finalization, - chainHeight, - lastStatusCheck, - nodeStatus, - isAvailable, - isHttpsEnabled, - restVersion - } = formattedNode.apiStatus; - - // // Api status - formattedNode.apiStatus = { - connectionStatus: isAvailable, - databaseStatus: - 'up' === nodeStatus?.db || Constants.Message.UNAVAILABLE, - apiNodeStatus: - 'up' === nodeStatus?.apiNode || Constants.Message.UNAVAILABLE, - isHttpsEnabled, - restVersion, - lastStatusCheck: moment - .utc(lastStatusCheck) - .format('YYYY-MM-DD HH:mm:ss') - }; - - if (finalization && chainHeight) { + const node = await http.statisticServiceRestClient().getNode(publicKey); + const formattedNode = this.formatNodeInfo(node); + + if (formattedNode?.apiStatus) { + const { + finalization, + chainHeight, + lastStatusCheck, + nodeStatus, + isAvailable, + isHttpsEnabled, + restVersion + } = formattedNode.apiStatus; + + // Api status + formattedNode.apiStatus = { + connectionStatus: isAvailable, + databaseStatus: + 'up' === nodeStatus?.db || Constants.Message.UNAVAILABLE, + apiNodeStatus: + 'up' === nodeStatus?.apiNode || Constants.Message.UNAVAILABLE, + isHttpsEnabled, + restVersion, + lastStatusCheck: moment + .utc(lastStatusCheck) + .format('YYYY-MM-DD HH:mm:ss') + }; + // Chain info formattedNode.chainInfo = { height: chainHeight, @@ -210,20 +206,44 @@ class NodeService { .format('YYYY-MM-DD HH:mm:ss') }; } else { + formattedNode.apiStatus = {}; formattedNode.chainInfo = {}; } + + if (formattedNode?.peerStatus) { + const { isAvailable, lastStatusCheck } = formattedNode.peerStatus; + + formattedNode.peerStatus = { + connectionStatus: isAvailable, + lastStatusCheck: moment + .utc(lastStatusCheck) + .format('YYYY-MM-DD HH:mm:ss') + }; + } else { + formattedNode.peerStatus = {}; + } + + return formattedNode; + } catch (e) { + console.error(e); + throw Error('Statistics service getNode error'); } - if (formattedNode?.peerStatus) - formattedNode.peerStatus.lastStatusCheck = moment(formattedNode.peerStatus.lastStatusCheck).format('YYYY-MM-DD HH:mm:ss'); - return formattedNode; }; static getNodeStats = async () => { - try { - return await http.statisticServiceRestClient().getNodeStats(); - } catch (e) { - throw Error('Statistics service getNodeStats error: ', e); - } + const availableNodes = await this.getAvailableNodes(); + + let nodeTypes = {}; + + // 7 types of roles + Array.from(Array(7).keys()).map(index => { + Object.assign(nodeTypes, { + [index + 1]: availableNodes.filter(node => node.rolesRaw === index + 1) + .length + }); + }); + + return nodeTypes; }; static getNodeHeightStats = async () => { @@ -252,7 +272,7 @@ class NodeService { }; static getNodeListCSV = async filter => { - const nodes = await this.getNodePeerList(filter); + const nodes = await this.getActiveNodeList(filter); const formattedData = nodes.data.map((node, index) => ({ no: index + 1, diff --git a/src/store/chain.js b/src/store/chain.js index bc7322b5d..285cdf81d 100644 --- a/src/store/chain.js +++ b/src/store/chain.js @@ -138,7 +138,7 @@ export default { // commit('setMarketData', { marketData, graphData }); if (nodeStats) - commit('setNodeStats', nodeStats.nodeTypes); + commit('setNodeStats', nodeStats); }, async getChainInfo ({ commit }) { diff --git a/src/store/node.js b/src/store/node.js index bb3afabc0..7b8fccf0e 100644 --- a/src/store/node.js +++ b/src/store/node.js @@ -31,7 +31,7 @@ import { NodeService, StatisticService } from '../infrastructure'; const managers = [ new Pagination({ name: 'timeline', - fetchFunction: (pageInfo, filterValue) => NodeService.getNodePeerList(filterValue), + fetchFunction: (pageInfo, filterValue) => NodeService.getAvailableNodeList(filterValue), filter: filters.nodeRoles }), new DataSet( From a1f6a39927d1a2c5745133a3801a980ea361890b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 7 May 2023 09:59:21 +0000 Subject: [PATCH 7/7] [dependency]: bump eslint from 7.32.0 to 8.40.0 Bumps [eslint](https://github.com/eslint/eslint) from 7.32.0 to 8.40.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v7.32.0...v8.40.0) --- updated-dependencies: - dependency-name: eslint dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package-lock.json | 828 ++++++++++++++++++++++++++++++---------------- package.json | 2 +- 2 files changed, 540 insertions(+), 290 deletions(-) diff --git a/package-lock.json b/package-lock.json index e09dd3102..7f3456532 100644 --- a/package-lock.json +++ b/package-lock.json @@ -45,7 +45,7 @@ "babel-eslint": "^10.1.0", "babel-jest": "^28.1.1", "cypress": "^10.1.0", - "eslint": "^7.32.0", + "eslint": "^8.40.0", "eslint-plugin-babel": "^5.3.1", "eslint-plugin-import": "^2.26.0", "eslint-plugin-vue": "^9.1.0", @@ -1940,30 +1940,75 @@ "ms": "^2.1.1" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", - "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", + "debug": "^4.3.2", + "espree": "^9.5.2", + "globals": "^13.19.0", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", - "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -1975,6 +2020,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/@eslint/eslintrc/node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -1987,6 +2053,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@eslint/js": { + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.40.0.tgz", + "integrity": "sha512-ElyB54bJIhXQYVKjDSvCkPO1iU1tSAeVQJbllWJq1XQSmmA4dgFk8CbiBGpiOPxleE48vDogxCtmMYku4HSVLA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/@hapi/address": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz", @@ -2047,19 +2122,32 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", - "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.0", + "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", - "minimatch": "^3.0.4" + "minimatch": "^3.0.5" }, "engines": { "node": ">=10.10.0" } }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@humanwhocodes/object-schema": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", @@ -11874,57 +11962,57 @@ } }, "node_modules/eslint": { - "version": "7.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", - "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.3", - "@humanwhocodes/config-array": "^0.5.0", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.40.0.tgz", + "integrity": "sha512-bvR+TsP9EHL3TqNtj9sCNJVAFK3fBN8Q7g5waghxyRsPLIMwL73XSKnZFK0hk/O2ANC+iAoq6PWMQ+IfBAJIiQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.40.0", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", - "debug": "^4.0.1", + "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", + "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", - "table": "^6.0.9", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -12324,15 +12412,6 @@ "node": ">=4" } }, - "node_modules/eslint/node_modules/@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.10.4" - } - }, "node_modules/eslint/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -12348,6 +12427,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "node_modules/eslint/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -12402,55 +12487,75 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^1.1.0" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": ">=6" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/mysticatea" + "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", "dev": true, "engines": { - "node": ">=4" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/eslint/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "dependencies": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" }, "engines": { - "node": ">= 6" + "node": ">=10.13.0" } }, "node_modules/eslint/node_modules/globals": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", - "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -12471,6 +12576,27 @@ "node": ">=8" } }, + "node_modules/eslint/node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/eslint/node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -12484,6 +12610,21 @@ "node": ">= 0.8.0" } }, + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint/node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -12501,6 +12642,36 @@ "node": ">= 0.8.0" } }, + "node_modules/eslint/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint/node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -12519,33 +12690,6 @@ "node": ">= 0.8.0" } }, - "node_modules/eslint/node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/eslint/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint/node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -12619,17 +12763,44 @@ } }, "node_modules/espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", "dev": true, "dependencies": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/esprima": { @@ -12646,9 +12817,9 @@ } }, "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -13665,12 +13836,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true - }, "node_modules/functions-have-names": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", @@ -13962,6 +14127,12 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, "node_modules/growly": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", @@ -20402,6 +20573,16 @@ "node": ">=0.6.0" } }, + "node_modules/js-sdsl": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz", + "integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, "node_modules/js-sha256": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", @@ -24047,15 +24228,6 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", @@ -28403,18 +28575,6 @@ "eslint": ">=6.0.0" } }, - "node_modules/vue-eslint-parser/node_modules/acorn": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/vue-eslint-parser/node_modules/eslint-scope": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", @@ -28437,20 +28597,6 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/vue-eslint-parser/node_modules/espree": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", - "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", - "dev": true, - "dependencies": { - "acorn": "^8.7.1", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "node_modules/vue-eslint-parser/node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", @@ -30394,6 +30540,18 @@ "fd-slicer": "~1.1.0" } }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zwitch": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", @@ -31714,32 +31872,76 @@ } } }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "dev": true + } + } + }, + "@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true + }, "@eslint/eslintrc": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", - "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", "dev": true, "requires": { "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", + "debug": "^4.3.2", + "espree": "^9.5.2", + "globals": "^13.19.0", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "globals": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", - "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "dev": true, "requires": { "type-fest": "^0.20.2" } }, + "ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, "type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -31748,6 +31950,12 @@ } } }, + "@eslint/js": { + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.40.0.tgz", + "integrity": "sha512-ElyB54bJIhXQYVKjDSvCkPO1iU1tSAeVQJbllWJq1XQSmmA4dgFk8CbiBGpiOPxleE48vDogxCtmMYku4HSVLA==", + "dev": true + }, "@hapi/address": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz", @@ -31805,16 +32013,22 @@ } }, "@humanwhocodes/config-array": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", - "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", "dev": true, "requires": { - "@humanwhocodes/object-schema": "^1.2.0", + "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", - "minimatch": "^3.0.4" + "minimatch": "^3.0.5" } }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, "@humanwhocodes/object-schema": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", @@ -39731,62 +39945,53 @@ } }, "eslint": { - "version": "7.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", - "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", - "dev": true, - "requires": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.3", - "@humanwhocodes/config-array": "^0.5.0", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.40.0.tgz", + "integrity": "sha512-bvR+TsP9EHL3TqNtj9sCNJVAFK3fBN8Q7g5waghxyRsPLIMwL73XSKnZFK0hk/O2ANC+iAoq6PWMQ+IfBAJIiQ==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.40.0", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", - "debug": "^4.0.1", + "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", + "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", - "table": "^6.0.9", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "text-table": "^0.2.0" }, "dependencies": { - "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -39796,6 +40001,12 @@ "color-convert": "^2.0.1" } }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -39832,42 +40043,51 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "eslint-scope": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", "dev": true, "requires": { - "eslint-visitor-keys": "^1.1.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" } }, "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "dev": true + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "requires": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" } }, "globals": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", - "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -39879,6 +40099,21 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -39889,6 +40124,15 @@ "type-check": "~0.4.0" } }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -39903,6 +40147,24 @@ "word-wrap": "^1.2.3" } }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -39915,21 +40177,6 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -40267,14 +40514,28 @@ "dev": true }, "espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", "dev": true, "requires": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "dependencies": { + "acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true + }, + "eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "dev": true + } } }, "esprima": { @@ -40284,9 +40545,9 @@ "dev": true }, "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "requires": { "estraverse": "^5.1.0" @@ -41094,12 +41355,6 @@ "functions-have-names": "^1.2.2" } }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true - }, "functions-have-names": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", @@ -41318,6 +41573,12 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, "growly": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", @@ -46294,6 +46555,12 @@ "integrity": "sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==", "dev": true }, + "js-sdsl": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz", + "integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==", + "dev": true + }, "js-sha256": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", @@ -49217,12 +49484,6 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, "promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", @@ -52664,12 +52925,6 @@ "semver": "^7.3.6" }, "dependencies": { - "acorn": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", - "dev": true - }, "eslint-scope": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", @@ -52686,17 +52941,6 @@ "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", "dev": true }, - "espree": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", - "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", - "dev": true, - "requires": { - "acorn": "^8.7.1", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" - } - }, "estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", @@ -54266,6 +54510,12 @@ "fd-slicer": "~1.1.0" } }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + }, "zwitch": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", diff --git a/package.json b/package.json index cd2af366d..6dc6b1a71 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "babel-eslint": "^10.1.0", "babel-jest": "^28.1.1", "cypress": "^10.1.0", - "eslint": "^7.32.0", + "eslint": "^8.40.0", "eslint-plugin-babel": "^5.3.1", "eslint-plugin-import": "^2.26.0", "eslint-plugin-vue": "^9.1.0",