From 65ae4a5cbed7d67193efeb4ea52f46a4305eaf27 Mon Sep 17 00:00:00 2001 From: AnthonyLaw Date: Wed, 24 Jul 2024 23:02:23 +0800 Subject: [PATCH 1/5] [explorer] fix: handle mosaic namespace expire query mosaic statement --- __tests__/helper.spec.js | 132 ++++++++++++++++++++++++++++----------- src/helper.js | 95 +++++++++++++++++++++++----- 2 files changed, 175 insertions(+), 52 deletions(-) diff --git a/__tests__/helper.spec.js b/__tests__/helper.spec.js index d607ea2d..879e257b 100644 --- a/__tests__/helper.spec.js +++ b/__tests__/helper.spec.js @@ -1,6 +1,6 @@ import TestHelper from './TestHelper'; import Helper from '../src/helper'; -import { MosaicService, NamespaceService } from '../src/infrastructure'; +import { MosaicService, NamespaceService, ReceiptService } from '../src/infrastructure'; import http from '../src/infrastructure/http'; import { restore, stub } from 'sinon'; import { Mosaic, MosaicId, NamespaceId, NamespaceName, UInt64 } from 'symbol-sdk'; @@ -219,6 +219,44 @@ describe('Helper', () => { expect(result).toStrictEqual(new MosaicId(mockTestMosaic.idHex)); expect(stubGetLinkedMosaicId.callCount).toBe(1); }); + + it('returns mosaic id and called mosaic statement query', async () => { + // Arrange: + const mockNamespaceId = new NamespaceId(mockTestMosaic.namespaceName); + + const stubSearchMosaicResolutionStatements = jest.spyOn(ReceiptService, 'searchMosaicResolutionStatements') + .mockReturnValue(Promise.resolve({ + data: [{ + height: UInt64.fromUint(10), + mosaicResolutionEntries: ['22D2D90A27738AA0'], + resolutionEntries: [ + { + resolved: new MosaicId('22D2D90A27738AA0'), + source: { + primaryId: 0, + secondaryId: 0 + } + } + ], + resolutionType: 'Mosaic', + unresolved: 'C6D6C5A3883DF4F4' + }] + })); + + // Act: + const result = await Helper.resolveMosaicId(mockNamespaceId, { + height: UInt64.fromUint(10), + primaryId: 1, + secondaryId: 0 + }); + + // Assert: + expect(result).toStrictEqual(new MosaicId('22D2D90A27738AA0')); + expect(stubGetLinkedMosaicId.callCount).toBe(0); + expect(stubSearchMosaicResolutionStatements).toHaveBeenCalledWith({ + height: UInt64.fromUint(10) + }); + }); }); describe('getNetworkCurrencyBalance', () => { @@ -275,21 +313,19 @@ describe('Helper', () => { describe('getTransactionMosaicInfoAndNamespace', () => { it('returns mosaics mapping, empty mosaic info and namespace when transaction included network mosaic', async () => { // Arrange: - const mockTransactions = [ - { - ...TestHelper.mockTransaction({ - height: 1, - timestamp: 10 - }), - mosaics: [ - new Mosaic(new NamespaceId(http.networkCurrency.namespaceName), UInt64.fromUint(20)), - new Mosaic(new MosaicId(http.networkCurrency.mosaicId), UInt64.fromUint(1)) - ] - } - ]; + const mockTransactions = { + ...TestHelper.mockTransaction({ + height: 1, + timestamp: 10 + }), + mosaics: [ + new Mosaic(new NamespaceId(http.networkCurrency.namespaceName), UInt64.fromUint(20)), + new Mosaic(new MosaicId(http.networkCurrency.mosaicId), UInt64.fromUint(1)) + ] + }; // Act: - const { unresolvedMosaicsMap, mosaicInfos, mosaicNames} = await Helper.getTransactionMosaicInfoAndNamespace(mockTransactions); + const { unresolvedMosaicsMap, mosaicInfos, mosaicNames } = await Helper.getTransactionMosaicInfoAndNamespace(mockTransactions); // Assert: expect(unresolvedMosaicsMap).toStrictEqual({ @@ -302,27 +338,33 @@ describe('Helper', () => { it('returns mosaics mapping, mosaic info and namespace when transaction exits', async () => { // Arrange: - const mockTransactions = [ - { - ...TestHelper.mockTransaction({ - height: 1, - timestamp: 10 - }), - mosaics: [ - new Mosaic(new NamespaceId(http.networkCurrency.namespaceName), UInt64.fromUint(20)), - new Mosaic(new MosaicId(http.networkCurrency.mosaicId), UInt64.fromUint(1)) - ] + const mockAggregateTransaction = { + transactionInfo: { + height: 10, + index: 1 }, - TestHelper.mockLockFundsTransaction(), - TestHelper.mockMosaicSupplyRevocationTransaction(new Mosaic( - new MosaicId(mockTestMosaic.idHex), - UInt64.fromUint(1) - )), - TestHelper.mockSecretLockTransaction(new Mosaic( - new NamespaceId(mockTestSecretLockMosaic.namespaceName), - UInt64.fromUint(20) - )) - ]; + innerTransactions: [ + { + ...TestHelper.mockTransaction({ + height: 1, + timestamp: 10 + }), + mosaics: [ + new Mosaic(new NamespaceId(http.networkCurrency.namespaceName), UInt64.fromUint(20)), + new Mosaic(new MosaicId(http.networkCurrency.mosaicId), UInt64.fromUint(1)) + ] + }, + TestHelper.mockLockFundsTransaction(), + TestHelper.mockMosaicSupplyRevocationTransaction(new Mosaic( + new MosaicId(mockTestMosaic.idHex), + UInt64.fromUint(1) + )), + TestHelper.mockSecretLockTransaction(new Mosaic( + new NamespaceId(mockTestSecretLockMosaic.namespaceName), + UInt64.fromUint(20) + )) + ] + }; const mockMosaicInfos = [ TestHelper.mockMosaicInfo(mockTestMosaic.idHex, 'TC46AZWUIZYZ2WVGLVEZYNZHSIFAD3AFDPUJMEA', 10, 0), @@ -359,9 +401,27 @@ describe('Helper', () => { .resolves(Promise.resolve(mockMosaicNames)); stub(NamespaceService, 'getLinkedMosaicId').resolves(Promise.resolve(new MosaicId(mockTestSecretLockMosaic.idHex))); + stub(ReceiptService, 'searchMosaicResolutionStatements').resolves(Promise.resolve({ + data: [{ + height: UInt64.fromUint(10), + mosaicResolutionEntries: ['22D2D90A27738AA0'], + resolutionEntries: [ + { + resolved: new MosaicId('22D2D90A27738AA0'), + source: { + primaryId: 1, + secondaryId: 2 + } + } + ], + resolutionType: 'Mosaic', + unresolved: 'DEBBC3DA600F2B48' + }] + })); // Act: - const { unresolvedMosaicsMap, mosaicInfos, mosaicNames} = await Helper.getTransactionMosaicInfoAndNamespace(mockTransactions); + const { unresolvedMosaicsMap, mosaicInfos, mosaicNames } = + await Helper.getTransactionMosaicInfoAndNamespace(mockAggregateTransaction); // Assert: expect(unresolvedMosaicsMap).toStrictEqual({ @@ -376,7 +436,7 @@ describe('Helper', () => { it('returns empty when transactions empty', async () => { // Arrange: - const transactions = []; + const transactions = {}; // Act: const {mosaicInfos, mosaicNames, unresolvedMosaicsMap} = await Helper.getTransactionMosaicInfoAndNamespace(transactions); diff --git a/src/helper.js b/src/helper.js index 81662af3..f745b361 100644 --- a/src/helper.js +++ b/src/helper.js @@ -562,16 +562,37 @@ class helper { /** * To resolved unresolvedMosaicId. * @param {NamespaceId | MosaicId} unresolvedMosaicId - NamespaceId | MosaicId + * @param {object | undefined} transactionLocation Location of transaction for which to perform the resolution. * @returns {MosaicId} MosaicId */ - static resolveMosaicId = async unresolvedMosaicId => { - if (!(unresolvedMosaicId instanceof NamespaceId)) + static resolveMosaicId = async (unresolvedMosaicId, transactionLocation = undefined) => { + if (0n === (BigInt(`0x${unresolvedMosaicId.id.toHex()}`) & (1n << 63n))) return unresolvedMosaicId; if (unresolvedMosaicId.id.toHex() === http.networkCurrency.namespaceId) return new MosaicId(http.networkCurrency.mosaicId); - return await NamespaceService.getLinkedMosaicId(unresolvedMosaicId); + if (!transactionLocation) + return await NamespaceService.getLinkedMosaicId(unresolvedMosaicId); + + const searchCriteria = { + height: UInt64.fromUint(transactionLocation.height) + }; + + const mosaicResolutionStatements = + await ReceiptService.searchMosaicResolutionStatements(searchCriteria); + + const isReceiptSourceLessThanEqual = (lhs, rhs) => + lhs.primaryId < rhs.primaryId || (lhs.primaryId === rhs.primaryId && lhs.secondaryId <= rhs.secondaryId); + + for (const statement of mosaicResolutionStatements.data) { + if (statement.unresolved === unresolvedMosaicId.id.toHex()) { + for (const entry of statement.resolutionEntries) { + if (isReceiptSourceLessThanEqual(entry.source, transactionLocation)) + return entry.resolved; + } + } + } }; /** @@ -771,22 +792,65 @@ class helper { return 1 === pageNumber ? 0 : (pageNumber - 1) * pageSize; }; - static getTransactionMosaicInfoAndNamespace = async transactions => { + static getTransactionMosaicInfoAndNamespace = async transactionObject => { const unresolvedMosaics = []; - // collect unresolved mosaics from transactions - transactions.map(transactionDTO => { - switch (transactionDTO.type) { + const makeTransactionLocation = (transactionInfo, secondaryId = undefined) => { + if (!transactionInfo?.height) + return undefined; + + return { + height: transactionInfo.height, + primaryId: transactionInfo.index + 1, + secondaryId: undefined === secondaryId ? 0 : secondaryId + }; + }; + + const addUnresolvedMosaic = (mosaicId, transactionLocation) => { + unresolvedMosaics.push({ + mosaicId, + transactionLocation + }); + }; + + const processTransaction = (transaction, transactionLocation) => { + switch (transaction.type) { case TransactionType.TRANSFER: - unresolvedMosaics.push(...transactionDTO.mosaics); - return; + transaction.mosaics.forEach(mosaic => { + addUnresolvedMosaic(mosaic.id, transactionLocation); + }); + break; + case TransactionType.MOSAIC_METADATA: + addUnresolvedMosaic(transaction.targetMosaicId, transactionLocation); + break; + case TransactionType.MOSAIC_DEFINITION: + case TransactionType.MOSAIC_SUPPLY_CHANGE: + addUnresolvedMosaic(transaction.mosaicId, transactionLocation); + break; case TransactionType.MOSAIC_SUPPLY_REVOCATION: case TransactionType.HASH_LOCK: case TransactionType.SECRET_LOCK: - unresolvedMosaics.push(transactionDTO.mosaic); - return; + addUnresolvedMosaic(transaction.mosaic.id, transactionLocation); + break; } - }); + }; + + if (transactionObject.innerTransactions) { + transactionObject.innerTransactions.forEach(innerTx => { + processTransaction( + innerTx, + makeTransactionLocation( + transactionObject.transactionInfo, + innerTx.transactionInfo.index + 1 + ) + ); + }); + } else { + processTransaction( + transactionObject, + makeTransactionLocation(transactionObject.transactionInfo) + ); + } return await helper.getMosaicInfoAndNamespace(unresolvedMosaics); }; @@ -795,17 +859,16 @@ class helper { const unresolvedMosaicsMap = {}; // create resolved mosaic mapping - const resolvedMosaics = await Promise.all(unresolvedMosaics.map(async mosaic => { - const resolvedMosaicId = await helper.resolveMosaicId(mosaic.id); + const resolvedMosaics = await Promise.all(unresolvedMosaics.map(async ({mosaicId, transactionLocation}) => { + const resolvedMosaicId = await helper.resolveMosaicId(mosaicId, transactionLocation); - unresolvedMosaicsMap[mosaic.id.toHex()] = resolvedMosaicId.toHex(); + unresolvedMosaicsMap[mosaicId.toHex()] = resolvedMosaicId.toHex(); return resolvedMosaicId; })); // skip networkCurrency mosaic const resolvedMosaicIds = resolvedMosaics - .map(mosaic => mosaic.id) .filter(mosaicId => mosaicId.toHex() !== http.networkCurrency.mosaicId); // filter duplicated mosaic id From fb218fafd0a8347c2e6ae068b6e523234d701152 Mon Sep 17 00:00:00 2001 From: AnthonyLaw Date: Thu, 25 Jul 2024 14:20:58 +0800 Subject: [PATCH 2/5] [explorer] fix: Added missing transaction --- src/helper.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/helper.js b/src/helper.js index f745b361..d20b08e4 100644 --- a/src/helper.js +++ b/src/helper.js @@ -823,6 +823,7 @@ class helper { case TransactionType.MOSAIC_METADATA: addUnresolvedMosaic(transaction.targetMosaicId, transactionLocation); break; + case TransactionType.MOSAIC_ADDRESS_RESTRICTION: case TransactionType.MOSAIC_DEFINITION: case TransactionType.MOSAIC_SUPPLY_CHANGE: addUnresolvedMosaic(transaction.mosaicId, transactionLocation); From 02243adf1b03206ee53f5e76de67651fce92ab67 Mon Sep 17 00:00:00 2001 From: AnthonyLaw Date: Thu, 25 Jul 2024 14:22:31 +0800 Subject: [PATCH 3/5] [explorer] fix: Refactor few transactions --- __tests__/config/jest.setup.js | 6 +- .../infrastructure/CreateTransaction.spec.js | 124 +++++++++++++++++- src/infrastructure/CreateTransaction.js | 62 +++++---- 3 files changed, 159 insertions(+), 33 deletions(-) diff --git a/__tests__/config/jest.setup.js b/__tests__/config/jest.setup.js index 6bc3b5bd..3aafdd4d 100644 --- a/__tests__/config/jest.setup.js +++ b/__tests__/config/jest.setup.js @@ -1,5 +1,8 @@ + /* Mock init http */ jest.mock('../../src/infrastructure/http', () => { + const { Address } = require('symbol-sdk'); + return { networkType: 152, epochAdjustment: 1615853185, @@ -15,7 +18,8 @@ jest.mock('../../src/infrastructure/http', () => { NetworkType: 152, NemsisTimestamp: 1637848847, NamespaceGraceDuration: 2880, - TotalChainImportance: 7842928625000000 + TotalChainImportance: 7842928625000000, + MosaicRentalSinkAddress: Address.createFromRawAddress('TATYW7IJN2TDBINTESRU66HHS5HMC4YVW7GGDRA') }, nativeNamespaces: [ { diff --git a/__tests__/infrastructure/CreateTransaction.spec.js b/__tests__/infrastructure/CreateTransaction.spec.js index 862f0f11..8cd180a5 100644 --- a/__tests__/infrastructure/CreateTransaction.spec.js +++ b/__tests__/infrastructure/CreateTransaction.spec.js @@ -8,6 +8,7 @@ import { Convert, Mosaic, MosaicId, + MosaicNonce, NamespaceId, NamespaceName, NetworkType, @@ -70,11 +71,16 @@ describe('CreateTransaction', () => { targetMosaicId: new MosaicId('7F2D26E89342D398') }; - const getMosaicAliasNamesStub = stub(Helper, 'getMosaicAliasNames'); - getMosaicAliasNamesStub.returns(Promise.resolve(['N/A'])); - // Act: - const mosaicMetadataObject = await CreateTransaction.mosaicMetadata(mockMosaicMetadata); + const mosaicMetadataObject = await CreateTransaction.mosaicMetadata(mockMosaicMetadata, { + mosaicNames: [{ + names: [], + mosaicId: '7F2D26E89342D398' + }], + unresolvedMosaicsMap: { + '7F2D26E89342D398': '7F2D26E89342D398' + } + }); // Assert: expect(mosaicMetadataObject.transactionBody).toEqual({ @@ -282,4 +288,114 @@ describe('CreateTransaction', () => { }] }); }); + + it('returns mosaicDefinition', async () => { + // Arrange: + const mockMosaicDefinition = { + type: 16717, + network: 152, + version: 1, + mosaicId: new MosaicId('7F2D26E89342D398'), + divisibility: 0, + duration: UInt64.fromUint(1000), + nonce: MosaicNonce.createRandom(), + flags: { + supplyMutable: false, + transferable: true, + restrictable: true, + revokable: true + } + }; + + // Act: + const mosaicDefinitionObject = await CreateTransaction.mosaicDefinition(mockMosaicDefinition, { + unresolvedMosaicsMap: { + '7F2D26E89342D398': '7F2D26E89342D398' + } + }); + + // Assert: + expect(mosaicDefinitionObject.transactionBody).toEqual({ + transactionType: mockMosaicDefinition.type, + recipient: 'TATYW7IJN2TDBINTESRU66HHS5HMC4YVW7GGDRA', + mosaicId: '7F2D26E89342D398', + divisibility: 0, + duration: 1000, + nonce: mockMosaicDefinition.nonce.toHex(), + supplyMutable: false, + transferable: true, + restrictable: true, + revokable: true + }); + }); + + const assertMosaicSupplyChange = async (action, expectedAction) => { + // Arrange: + const mockMosaicSupplyChange = { + type: 16973, + mosaicId: new MosaicId('7F2D26E89342D398'), + action: action, + delta: UInt64.fromUint(100) + }; + + // Act: + const mosaicSupplyChangeObject = await CreateTransaction.mosaicSupplyChange(mockMosaicSupplyChange, { + unresolvedMosaicsMap: { + '7F2D26E89342D398': '7F2D26E89342D398' + } + }); + + // Assert: + expect(mosaicSupplyChangeObject.transactionBody).toEqual({ + transactionType: mockMosaicSupplyChange.type, + mosaicId: '7F2D26E89342D398', + action: expectedAction, + delta: 100 + }); + }; + + it('returns mosaicSupplyChange in increase action', async () => { + await assertMosaicSupplyChange(1, 'Increase'); + }); + + it('returns mosaicSupplyChange in decrease action', async () => { + await assertMosaicSupplyChange(0, 'Decrease'); + }); + + it('returns mosaicAddressRestriction', async () => { + // Arrange: + const mockMosaicAddressRestriction = { + type: 16977, + mosaicId: new MosaicId('7F2D26E89342D398'), + targetAddress: randomAddress, + restrictionKey: UInt64.fromUint(1), + previousRestrictionValue: UInt64.fromUint(10), + newRestrictionValue: UInt64.fromUint(20), + transactionInfo: { + height: UInt64.fromUint(1) + } + }; + + // Act: + const mosaicAddressRestrictionObject = await CreateTransaction.mosaicAddressRestriction(mockMosaicAddressRestriction, { + mosaicNames: [{ + names: [], + mosaicId: '7F2D26E89342D398' + }], + unresolvedMosaicsMap: { + '7F2D26E89342D398': '7F2D26E89342D398' + } + }); + + // Assert: + expect(mosaicAddressRestrictionObject.transactionBody).toEqual({ + transactionType: mockMosaicAddressRestriction.type, + mosaicId: '7F2D26E89342D398', + mosaicAliasNames: ['N/A'], + targetAddress: randomAddress.plain(), + restrictionKey: '0000000000000001', + previousRestrictionValue: '10', + newRestrictionValue: '20' + }); + }); }); diff --git a/src/infrastructure/CreateTransaction.js b/src/infrastructure/CreateTransaction.js index d34ffade..293fb253 100644 --- a/src/infrastructure/CreateTransaction.js +++ b/src/infrastructure/CreateTransaction.js @@ -19,7 +19,7 @@ import http from './http'; import { Constants } from '../config'; import helper from '../helper'; -import { NamespaceService } from '../infrastructure'; +import { MosaicService, NamespaceService } from '../infrastructure'; import { Address, Convert, Mosaic, MosaicId } from 'symbol-sdk'; class CreateTransaction { @@ -116,15 +116,16 @@ class CreateTransaction { }; }; - static mosaicDefinition = async transactionObj => { - const resolvedMosaic = await helper.resolveMosaicId(transactionObj.mosaicId); + static mosaicDefinition = async (transactionObj, { + unresolvedMosaicsMap + }) => { return { ...transactionObj, transactionBody: { transactionType: transactionObj.type, recipient: http.networkConfig.MosaicRentalSinkAddress.address, - mosaicId: resolvedMosaic.toHex(), + mosaicId: unresolvedMosaicsMap[transactionObj.mosaicId.toHex()], divisibility: transactionObj.divisibility, duration: transactionObj.duration.compact(), nonce: transactionObj.nonce.toHex(), @@ -136,14 +137,14 @@ class CreateTransaction { }; }; - static mosaicSupplyChange = async transactionObj => { - const resolvedMosaic = await helper.resolveMosaicId(transactionObj.mosaicId); - + static mosaicSupplyChange = async (transactionObj, { + unresolvedMosaicsMap + }) => { return { ...transactionObj, transactionBody: { transactionType: transactionObj.type, - mosaicId: resolvedMosaic.toHex(), + mosaicId: unresolvedMosaicsMap[transactionObj.mosaicId.toHex()], action: Constants.MosaicSupplyChangeAction[transactionObj.action], delta: transactionObj.delta.compact() } @@ -331,23 +332,26 @@ class CreateTransaction { }; }; - static mosaicAddressRestriction = async transactionObj => { + static mosaicAddressRestriction = async (transactionObj, { + mosaicNames, + unresolvedMosaicsMap + }) => { 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); + const targetAddress = await helper.resolvedAddress( + transactionObj.targetAddress, + transactionInfo.height + ); + + const mosaicAliasNames = MosaicService.extractMosaicNamespace({ + mosaicId: unresolvedMosaicsMap[transactionObj.mosaicId.toHex()] + },mosaicNames); return { ...transactionObj, transactionBody: { transactionType: transactionObj.type, - mosaicId: resolvedMosaic.toHex(), + mosaicId: unresolvedMosaicsMap[transactionObj.mosaicId.toHex()], mosaicAliasNames, targetAddress: targetAddress, restrictionKey: transactionObj.restrictionKey.toHex(), @@ -404,24 +408,26 @@ class CreateTransaction { }; }; - static mosaicMetadata = async transactionObj => { + static mosaicMetadata = async (transactionObj, { + mosaicNames, + unresolvedMosaicsMap + }) => { const { transactionInfo } = transactionObj; - const [resolvedMosaic, resolvedAddress] = await Promise.all([ - helper.resolveMosaicId(transactionObj.targetMosaicId), - helper.resolvedAddress( - transactionObj.targetAddress, - transactionInfo.height - ) - ]); + const resolvedAddress = await helper.resolvedAddress( + transactionObj.targetAddress, + transactionInfo.height + ); - const mosaicAliasNames = await helper.getMosaicAliasNames(resolvedMosaic); + const mosaicAliasNames = MosaicService.extractMosaicNamespace({ + mosaicId: unresolvedMosaicsMap[transactionObj.targetMosaicId.toHex()] + }, mosaicNames); return { ...transactionObj, transactionBody: { transactionType: transactionObj.type, scopedMetadataKey: transactionObj.scopedMetadataKey.toHex(), - targetMosaicId: resolvedMosaic.toHex(), + targetMosaicId: unresolvedMosaicsMap[transactionObj.targetMosaicId.toHex()], targetMosaicAliasNames: mosaicAliasNames, targetAddress: resolvedAddress, metadataValue: `${Convert.uint8ToHex(transactionObj.value)} (Text: ${Convert.uint8ToUtf8(transactionObj.value)})`, From b28d0d45f05ab26b521f0cadd6f282c44dd1ce38 Mon Sep 17 00:00:00 2001 From: AnthonyLaw Date: Thu, 25 Jul 2024 16:40:01 +0800 Subject: [PATCH 4/5] [explorer] fix: Added mosaic names, and unresolved mosaic map params in CreateTransaction method --- .../infrastructure/TransactionService.spec.js | 70 ++++++++++++++++++- src/infrastructure/TransactionService.js | 22 ++++-- 2 files changed, 84 insertions(+), 8 deletions(-) diff --git a/__tests__/infrastructure/TransactionService.spec.js b/__tests__/infrastructure/TransactionService.spec.js index d398c6c4..b7bfd81b 100644 --- a/__tests__/infrastructure/TransactionService.spec.js +++ b/__tests__/infrastructure/TransactionService.spec.js @@ -1,9 +1,9 @@ import Helper from '../../src/helper'; -import { LockService, TransactionService } from '../../src/infrastructure'; +import { CreateTransaction, LockService, TransactionService } from '../../src/infrastructure'; import http from '../../src/infrastructure/http'; import TestHelper from '../TestHelper'; import { restore, stub } from 'sinon'; -import { MosaicId, TransactionGroup, UInt64 } from 'symbol-sdk'; +import { Account, MosaicId, NetworkType, TransactionGroup, UInt64 } from 'symbol-sdk'; describe('Transaction Service', () => { afterEach(restore); @@ -321,4 +321,70 @@ describe('Transaction Service', () => { }); }); }); + + describe('createStandaloneTransactionFromSDK', () => { + const commonTransactionDTO = { + deadline: { + adjustedValue: 8266897456 + }, + maxFee: UInt64.fromUint(1000000), + signer: { + address: Account.generateNewAccount(NetworkType.TEST_NET).address + }, + transactionInfo: {} + }; + + const assertTransactionCreation = async (transactionType, transactionTypeName, expectedParams) => { + // Arrange: + const mockTransactionDTO = { + ...commonTransactionDTO, + type: transactionType + }; + + jest.spyOn(CreateTransaction, transactionTypeName).mockResolvedValue(); + + // Act: + await TransactionService.createStandaloneTransactionFromSDK(mockTransactionDTO, { + mosaicInfos: [], + mosaicNames: [], + unresolvedMosaicsMap: {} + }); + + // Assert: + expect(CreateTransaction[transactionTypeName]).toHaveBeenCalledWith({ + deadline: 1646115744, + maxFee: '1.000000', + signer: mockTransactionDTO.signer.address.plain(), + transactionInfo: {}, + transactionType: transactionType, + type: transactionType + }, expectedParams); + }; + + it('called mosaic definition', async () => { + await assertTransactionCreation(16717, 'mosaicDefinition', { + unresolvedMosaicsMap: {} + }); + }); + + it('called mosaic supply change', async () => { + await assertTransactionCreation(16973, 'mosaicSupplyChange', { + unresolvedMosaicsMap: {} + }); + }); + + it('called mosaic address restriction', async () => { + await assertTransactionCreation(16977, 'mosaicAddressRestriction', { + mosaicNames: [], + unresolvedMosaicsMap: {} + }); + }); + + it('called mosaic metadata', async () => { + await assertTransactionCreation(16964, 'mosaicMetadata', { + mosaicNames: [], + unresolvedMosaicsMap: {} + }); + }); + }); }); diff --git a/src/infrastructure/TransactionService.js b/src/infrastructure/TransactionService.js index e2805cce..8b872cb1 100644 --- a/src/infrastructure/TransactionService.js +++ b/src/infrastructure/TransactionService.js @@ -681,9 +681,13 @@ class TransactionService { case TransactionType.MOSAIC_ALIAS: return CreateTransaction.mosaicAlias(transactionObj); case TransactionType.MOSAIC_DEFINITION: - return CreateTransaction.mosaicDefinition(transactionObj); + return CreateTransaction.mosaicDefinition(transactionObj, { + unresolvedMosaicsMap + }); case TransactionType.MOSAIC_SUPPLY_CHANGE: - return CreateTransaction.mosaicSupplyChange(transactionObj); + return CreateTransaction.mosaicSupplyChange(transactionObj, { + unresolvedMosaicsMap + }); case TransactionType.MOSAIC_SUPPLY_REVOCATION: return CreateTransaction.mosaicSupplyRevocation(transactionObj, { mosaicInfos, @@ -713,13 +717,19 @@ class TransactionService { case TransactionType.ACCOUNT_OPERATION_RESTRICTION: return CreateTransaction.accountOperationRestriction(transactionObj); case TransactionType.MOSAIC_ADDRESS_RESTRICTION: - return CreateTransaction.mosaicAddressRestriction(transactionObj); + return CreateTransaction.mosaicAddressRestriction(transactionObj, { + mosaicNames, + unresolvedMosaicsMap + }); 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); + return CreateTransaction.mosaicMetadata(transactionObj, { + mosaicNames, + unresolvedMosaicsMap + }); case TransactionType.NAMESPACE_METADATA: return CreateTransaction.namespaceMetadata(transactionObj); case TransactionType.VOTING_KEY_LINK: @@ -746,7 +756,7 @@ class TransactionService { transactionDTO.type === TransactionType.AGGREGATE_COMPLETE ) { const { mosaicInfos, mosaicNames, unresolvedMosaicsMap } = - await helper.getTransactionMosaicInfoAndNamespace(transactionDTO.innerTransactions); + await helper.getTransactionMosaicInfoAndNamespace(transactionDTO); const innerTransactions = transactionDTO.innerTransactions ? await Promise.all(transactionDTO.innerTransactions.map(async (transaction, index) => { @@ -783,7 +793,7 @@ class TransactionService { }; } else { const { mosaicInfos, mosaicNames, unresolvedMosaicsMap } = - await helper.getTransactionMosaicInfoAndNamespace([transactionDTO]); + await helper.getTransactionMosaicInfoAndNamespace(transactionDTO); return this.createStandaloneTransactionFromSDK(transactionDTO, { mosaicInfos, From 4c83eb4d424bcc0a21dc9c3c8ca9fd0d9bba9118 Mon Sep 17 00:00:00 2001 From: AnthonyLaw Date: Thu, 25 Jul 2024 16:40:34 +0800 Subject: [PATCH 5/5] [explorer] fix: Refactor mosaics properties --- src/infrastructure/AccountService.js | 5 ++++- src/infrastructure/ReceiptExtractor.js | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/infrastructure/AccountService.js b/src/infrastructure/AccountService.js index c575a674..1d413b9e 100644 --- a/src/infrastructure/AccountService.js +++ b/src/infrastructure/AccountService.js @@ -511,7 +511,10 @@ class AccountService { const accountSecretLocks = await LockService.searchSecretLocks(searchCriteria); - const mosaics = accountSecretLocks.data.map(secretlock => new Mosaic(secretlock.mosaicId, secretlock.amount)); + const mosaics = accountSecretLocks.data.map(secretlock => ({ + mosaicId: secretlock.mosaicId, + transactionLocation: undefined + })); const { mosaicInfos, mosaicNames, unresolvedMosaicsMap } = await helper.getMosaicInfoAndNamespace(mosaics); diff --git a/src/infrastructure/ReceiptExtractor.js b/src/infrastructure/ReceiptExtractor.js index 5c451334..065f2f47 100644 --- a/src/infrastructure/ReceiptExtractor.js +++ b/src/infrastructure/ReceiptExtractor.js @@ -6,7 +6,10 @@ class ReceiptExtractor { static balanceChangeReceipt = async transactionStatement => { let balanceChangeReceipt = []; - const mosaics = transactionStatement.map(statement => new Mosaic(statement.mosaicId, statement.amount)); + const mosaics = transactionStatement.map(statement => ({ + mosaicId: statement.mosaicId, + transactionLocation: undefined + })); const { mosaicInfos, mosaicNames, unresolvedMosaicsMap } = await helper.getMosaicInfoAndNamespace(mosaics);