From 1b3966e6a064021ad69048abbccb867d69973aa5 Mon Sep 17 00:00:00 2001 From: Anthony Law Date: Wed, 23 Mar 2022 00:55:19 +0800 Subject: [PATCH] [explorer] fix: improvement blocks listing page (#1013) * fix: remove chain info on top right blocks listing page * feat: add inflation data * fix: refactor code * fix: re-arrage block listing table head * feat: new translation * Fix: get inflation from statement receipt * task: fix indent * task: removed unused code * task: update get block list unit test * task: add missing library * task: lint fix import order --- __tests__/TestHelper.js | 15 +++++++++++ __tests__/infrastructure/BlockService.spec.js | 20 ++++++++++++++- src/config/i18n/en-us.json | 3 ++- src/config/i18n/es.json | 3 ++- src/config/i18n/ja.json | 3 ++- src/config/i18n/pt.json | 3 ++- src/config/i18n/ru.json | 3 ++- src/config/i18n/ua.json | 3 ++- src/config/i18n/zh.json | 3 ++- src/config/pages/block-list.json | 13 +++++----- src/infrastructure/BlockService.js | 25 +++++++++++++++---- src/store/block.js | 25 ++++++++++++++----- 12 files changed, 93 insertions(+), 26 deletions(-) diff --git a/__tests__/TestHelper.js b/__tests__/TestHelper.js index fc5c2e31a..6ab7b14c3 100644 --- a/__tests__/TestHelper.js +++ b/__tests__/TestHelper.js @@ -230,6 +230,21 @@ const TestHelper = { new Mosaic(new MosaicId('7F2D26E89342D398'), UInt64.fromUint(5)) ] }; + }, + /** + * Mock inflation statement. + * @param {number} amount reward amount. + * @param {number} height block height. + * @returns {object} inflation statement. + */ + mockInflationStatement: (amount, height) => { + return { + amount: UInt64.fromUint(amount), + height: UInt64.fromUint(height), + mosaicId: new MosaicId('6BED913FA20223F8'), + type: 20803, + version: 1 + }; } }; diff --git a/__tests__/infrastructure/BlockService.spec.js b/__tests__/infrastructure/BlockService.spec.js index d273d0ae6..830d7fa61 100644 --- a/__tests__/infrastructure/BlockService.spec.js +++ b/__tests__/infrastructure/BlockService.spec.js @@ -1,5 +1,5 @@ -import { BlockService, AccountService, NodeService } from '../../src/infrastructure'; import Helper from '../../src/helper'; +import { BlockService, AccountService, NodeService, ReceiptService } from '../../src/infrastructure'; import TestHelper from '../TestHelper'; import { restore, stub } from 'sinon'; @@ -73,11 +73,13 @@ describe('Block Service', () => { let getAccounts = {}; let searchBlocks = {}; let getStorageInfo = {}; + let searchReceipts = {}; beforeAll(async () => { getAccounts = stub(AccountService, 'getAccounts'); searchBlocks = stub(BlockService, 'searchBlocks'); getStorageInfo = stub(NodeService, 'getStorageInfo'); + searchReceipts = stub(ReceiptService, 'searchReceipts'); getStorageInfo.returns(Promise.resolve({ numBlocks: 100 @@ -102,10 +104,24 @@ describe('Block Service', () => { }) }; + const mockBalanceTransferReceipt = { + data: { + inflationStatement: { + data: mockSearchBlocks.data.map(block => { + // mock reward amount + const amount = block.height * 1000000; + return TestHelper.mockInflationStatement(amount, block.height); + }) + } + } + }; + getAccounts.returns(Promise.resolve(mockAccounts)); searchBlocks.returns(Promise.resolve(mockSearchBlocks)); + searchReceipts.returns(Promise.resolve(mockBalanceTransferReceipt)); + // Act: const blockList = await BlockService.getBlockList(pageInfo); @@ -115,8 +131,10 @@ describe('Block Service', () => { expect(blockList.pageSize).toEqual(pageInfo.pageSize); expect(blockList.data[0].harvester.signer).toEqual(accounts[0].address.plain()); expect(blockList.data).toHaveLength(10); + blockList.data.forEach((block, index) => { expect(block.age).toEqual(Helper.convertToUTCDate(epochAdjustment + index + 1)); + expect(block.blockReward).toEqual(Helper.toNetworkCurrency(block.height * 1000000)); expect(block).toHaveProperty('harvester'); expect(block.harvester).toHaveProperty('signer'); expect(block.harvester).toHaveProperty('linkedAddress'); diff --git a/src/config/i18n/en-us.json b/src/config/i18n/en-us.json index 2c447211a..70fd47375 100644 --- a/src/config/i18n/en-us.json +++ b/src/config/i18n/en-us.json @@ -416,5 +416,6 @@ "recentNamespaces": "Recent Namespaces", "lastEpoch": "Last Epoch", "currentEpoch": "Current Epoch", - "value": "Value" + "value": "Value", + "blockReward": "Block Reward" } diff --git a/src/config/i18n/es.json b/src/config/i18n/es.json index c2ba45dd7..5511ec15e 100644 --- a/src/config/i18n/es.json +++ b/src/config/i18n/es.json @@ -412,5 +412,6 @@ "recentNamespaces": "Recent Namespaces", "lastEpoch": "Last Epoch", "currentEpoch": "Current Epoch", - "value": "Value" + "value": "Value", + "blockReward": "Block Reward" } diff --git a/src/config/i18n/ja.json b/src/config/i18n/ja.json index 0b7420872..107fc8cd8 100644 --- a/src/config/i18n/ja.json +++ b/src/config/i18n/ja.json @@ -416,5 +416,6 @@ "recentNamespaces": "直近ネームスペース", "lastEpoch": "Last Epoch", "currentEpoch": "Current Epoch", - "value": "値" + "value": "値", + "blockReward": "ブロック報酬" } diff --git a/src/config/i18n/pt.json b/src/config/i18n/pt.json index 597b88701..7bfd385a3 100644 --- a/src/config/i18n/pt.json +++ b/src/config/i18n/pt.json @@ -410,5 +410,6 @@ "recentNamespaces": "Recent Namespaces", "lastEpoch": "Last Epoch", "currentEpoch": "Current Epoch", - "value": "Value" + "value": "Value", + "blockReward": "Block Reward" } diff --git a/src/config/i18n/ru.json b/src/config/i18n/ru.json index 1dfc59942..d89e97626 100644 --- a/src/config/i18n/ru.json +++ b/src/config/i18n/ru.json @@ -416,5 +416,6 @@ "recentNamespaces": "Недавние Пространства имен", "lastEpoch": "Last Epoch", "currentEpoch": "Current Epoch", - "value": "Value" + "value": "Value", + "blockReward": "Награда за блок" } diff --git a/src/config/i18n/ua.json b/src/config/i18n/ua.json index 152025d9f..b372ae54c 100644 --- a/src/config/i18n/ua.json +++ b/src/config/i18n/ua.json @@ -412,5 +412,6 @@ "recentNamespaces": "Recent Namespaces", "lastEpoch": "Last Epoch", "currentEpoch": "Current Epoch", - "value": "Value" + "value": "Value", + "blockReward": "Block Reward" } diff --git a/src/config/i18n/zh.json b/src/config/i18n/zh.json index cbfb9a93e..af1e1ac2f 100644 --- a/src/config/i18n/zh.json +++ b/src/config/i18n/zh.json @@ -412,5 +412,6 @@ "recentNamespaces": "Recent Namespaces", "lastEpoch": "Last Epoch", "currentEpoch": "Current Epoch", - "value": "值" + "value": "值", + "blockReward": "Block Reward" } diff --git a/src/config/pages/block-list.json b/src/config/pages/block-list.json index b105d548a..0d3d7dcec 100644 --- a/src/config/pages/block-list.json +++ b/src/config/pages/block-list.json @@ -14,24 +14,23 @@ "type": "CardTable", "title": "blocksTitle", "managerGetter": "block/timeline", - "infoTextGetter": "block/infoText", "pagination": "server", "hasFilter": false, - "hasInfoText": true, + "hasInfoText": false, "fields": [ "height", - "blockType", "age", + "harvester", "totalTransactions", "statements", - "totalFee", - "timestamp", - "harvester" + "blockReward", + "totalFee" ], "mobileFields": [ "height", "age", - "transactions", + "totalTransactions", + "blockReward", "totalFee" ] } diff --git a/src/infrastructure/BlockService.js b/src/infrastructure/BlockService.js index 44e7c9f13..9398cd4e9 100644 --- a/src/infrastructure/BlockService.js +++ b/src/infrastructure/BlockService.js @@ -16,14 +16,14 @@ * */ -import NodeService from './NodeService'; import http from './http'; import { Constants } from '../config'; import helper from '../helper'; -import { TransactionService, ReceiptService, AccountService } 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 } from 'symbol-sdk'; +import { UInt64, TransactionGroup, Order, BlockOrderBy, BlockType, ReceiptType } from 'symbol-sdk'; class BlockService { /** @@ -131,19 +131,34 @@ class BlockService { const signerAddress = blocks.data.map(block => block.signer); - const accountInfos = await AccountService.getAccounts(signerAddress); + // 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 { numBlocks } = await NodeService.getStorageInfo(); + 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.convertToUTCDate(block.timestamp), + blockReward: helper.toNetworkCurrency(blockReward), harvester: { signer: block.signer, linkedAddress: supplementalPublicKeys.linked === Constants.Message.UNAVAILABLE diff --git a/src/store/block.js b/src/store/block.js index 0cb6e62fc..4efe1d938 100644 --- a/src/store/block.js +++ b/src/store/block.js @@ -30,8 +30,10 @@ import helper from '../helper'; import { ListenerService, BlockService, - AccountService + AccountService, + ReceiptService } from '../infrastructure'; +import { UInt64, ReceiptType } from 'symbol-sdk'; import Vue from 'vue'; const managers = [ @@ -91,10 +93,8 @@ export default { merkleInfo: state => state.info?.data?.merkleInfo || {}, resolutionStatement: state => state.blockReceipts?.data?.resolutionStatements || [], - currentBlockHeight: state => state.currentBlockHeight, - infoText: (s, g, rs, rootGetters) => - rootGetters['ui/getNameByKey']('chainHeight') + ': ' + (rootGetters['chain/getChainInfo'] - && rootGetters['chain/getChainInfo'].currentHeight ? rootGetters['chain/getChainInfo'].currentHeight : 0) }, + currentBlockHeight: state => state.currentBlockHeight + }, mutations: { ...getMutationsFromManagers(managers), setInitialized: (state, initialized) => { @@ -134,13 +134,26 @@ export default { if (null === getters.getSubscription) { const subscription = await ListenerService.subscribeNewBlock( async item => { - const latestBlock = await BlockService.getBlockByHeight(item.height.compact()); + const blockHeight = Number(item.height.toString()); + + const [latestBlock, balanceTransferReceipt] = await Promise.all([ + BlockService.getBlockByHeight(blockHeight), + ReceiptService.searchReceipts({ + height: UInt64.fromUint(blockHeight), + receiptTypes: [ReceiptType.Inflation] + }) + ]); const { supplementalPublicKeys } = await AccountService.getAccount(latestBlock.signer); + const inflationRate = balanceTransferReceipt.data.inflationStatement.data[0]; + + const blockReward = Number(inflationRate?.amount.toString()) || 0; + getters.timeline.addLatestItem({ ...latestBlock, age: helper.convertToUTCDate(latestBlock.timestamp), + blockReward: helper.toNetworkCurrency(blockReward), harvester: { signer: latestBlock.signer, linkedAddress: supplementalPublicKeys.linked === Constants.Message.UNAVAILABLE