diff --git a/CHANGELOG.md b/CHANGELOG.md index b2c624521..c2b215cfb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +- [Removed index file from apiCalls folder](https://github.com/multiversx/mx-sdk-dapp/pull/1663) +- [~~Removed unused~~ Added comment togetActiveTransactionsStatus method](https://github.com/multiversx/mx-sdk-dapp/pull/1662) - [Fixed coverage badge title](https://github.com/multiversx/mx-sdk-dapp/pull/1661) - [Fixed coverage badge](https://github.com/multiversx/mx-sdk-dapp/pull/1660) diff --git a/coverage-total.json b/coverage-total.json index bac332489..ef49cef10 100644 --- a/coverage-total.json +++ b/coverage-total.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, "label": "Coverage", - "message": "75.85%", + "message": "76.71%", "color": "yellow" } diff --git a/scripts/template-dapps-integration.sh b/scripts/template-dapps-integration.sh index 698529b48..30139cb42 100644 --- a/scripts/template-dapps-integration.sh +++ b/scripts/template-dapps-integration.sh @@ -34,6 +34,9 @@ git clone https://github.com/multiversx/mx-template-dapp.git echo "cd mx-template-dapp..." cd mx-template-dapp +echo "git checkout development..." +git checkout development + echo "Installing dependencies mx-template-dapp..." yarn install diff --git a/src/__mocks__/data/index.ts b/src/__mocks__/data/index.ts index becac21df..cf3a0313c 100644 --- a/src/__mocks__/data/index.ts +++ b/src/__mocks__/data/index.ts @@ -1,4 +1,5 @@ export * from './account'; +export * from './testToken'; export * from './networkConfig'; export * from './dappConfig'; export * from './socketResponse'; diff --git a/src/__mocks__/data/testToken.ts b/src/__mocks__/data/testToken.ts new file mode 100644 index 000000000..7addb611b --- /dev/null +++ b/src/__mocks__/data/testToken.ts @@ -0,0 +1,62 @@ +import { testAddress } from '__mocks__/accountConfig'; + +export const testToken = { + type: 'FungibleESDT', + identifier: 'TEST-e3d1b7', + name: 'TEST', + ticker: 'TEST', + owner: testAddress, + minted: '0', + burnt: '0', + initialMinted: '1000000000000000000000000000', + decimals: 18, + isPaused: false, + assets: { + website: 'https://test.io', + description: 'Test token is a test token for testing the token details.', + status: 'active', + pngUrl: + 'https://tools.multiversx.com/assets-cdn/devnet/tokens/TEST-e3d1b7/icon.png', + svgUrl: + 'https://tools.multiversx.com/assets-cdn/devnet/tokens/TEST-e3d1b7/icon.svg', + lockedAccounts: { + [testAddress]: 'Locked Account #1' + }, + extraTokens: ['TEST-e3d1b7', 'TEST-e3d1b7'], + ledgerSignature: + '30450221009c58ae35af3a770221bd6e1fd8ad023d23e49e52cbca64f1a225c497935ee30402203fc97dae339a44872bc0415e684c35e0990720ba977ce6dabebfe72e6dc37591', + social: { + email: 'hello@test.io', + blog: 'https://medium.com/@test', + twitter: 'https://twitter.com/test', + telegram: 'https://t.me/test', + discord: 'https://discord.gg/test' + } + }, + transactions: 1754, + transactionsLastUpdatedAt: 1762511057, + transfers: 40882, + transfersLastUpdatedAt: 1762511183, + accounts: 416, + accountsLastUpdatedAt: 1762513938, + canUpgrade: true, + canMint: false, + canBurn: false, + canChangeOwner: true, + canAddSpecialRoles: true, + canPause: true, + canFreeze: true, + canWipe: true, + price: 3.114724747645395, + marketCap: 3114724747.6453953, + supply: '1000000000', + circulatingSupply: '1000000000', + mexPairType: 'core', + totalLiquidity: 1963.9864633925347, + totalVolume24h: 0, + isLowLiquidity: true, + lowLiquidityThresholdPercent: 0.5, + tradesCount: 8273, + balance: '663088410050705591065', + valueUsd: 2065.3378806617707 +}; diff --git a/src/__mocks__/server.ts b/src/__mocks__/server.ts index 2b904e5e3..8f93d5517 100644 --- a/src/__mocks__/server.ts +++ b/src/__mocks__/server.ts @@ -17,6 +17,7 @@ import { socketResponse, websocketConfig } from './data'; +import { testToken } from './data/testToken'; import { wrapEgldContract } from './data/wrapEgldContract'; export const mockResponse = @@ -49,6 +50,10 @@ const handlers = [ `${testNetwork.apiAddress}/accounts/${testAddress}`, mockResponse(account) ), + rest.get( + `${testNetwork.apiAddress}/tokens/${testToken.identifier}`, + mockResponse(testToken) + ), rest.get( `${testNetwork.apiAddress}/accounts/${wrapEgldContract.address}`, mockResponse(wrapEgldContract) diff --git a/src/apiCalls/account/getScamAddressData.ts b/src/apiCalls/account/getScamAddressData.ts index 66725b4b9..a9b6c8290 100644 --- a/src/apiCalls/account/getScamAddressData.ts +++ b/src/apiCalls/account/getScamAddressData.ts @@ -1,5 +1,5 @@ import axios from 'axios'; -import { getCleanApiAddress } from 'apiCalls/utils'; +import { getCleanApiAddress } from 'apiCalls/utils/getCleanApiAddress'; import { TIMEOUT } from 'constants/index'; import { ScamInfoType } from 'types/account.types'; import { ACCOUNTS_ENDPOINT } from '../endpoints'; diff --git a/src/apiCalls/account/index.ts b/src/apiCalls/account/index.ts deleted file mode 100644 index 02c1b3280..000000000 --- a/src/apiCalls/account/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './getAccountFromApi'; diff --git a/src/apiCalls/account/tests/getAccountFromApi.test.ts b/src/apiCalls/account/tests/getAccountFromApi.test.ts new file mode 100644 index 000000000..32cacf1d0 --- /dev/null +++ b/src/apiCalls/account/tests/getAccountFromApi.test.ts @@ -0,0 +1,48 @@ +import { server, rest, testNetwork } from '__mocks__'; +import { ACCOUNTS_ENDPOINT } from 'apiCalls/endpoints'; +import { getAccountFromApi } from '../getAccountFromApi'; + +describe('getAccountFromApi tests', () => { + afterEach(() => { + server.resetHandlers(); + }); + it('should return null when address is empty', async () => { + const account = await getAccountFromApi({ + address: '', + baseURL: testNetwork.apiAddress + }); + expect(account).toBeNull(); + }); + it('should return null and log an error if account is not found', async () => { + const missingAccountAddress = + 'erd1qqqqqqqqqqqqqpgqc978caxxxxxxxxxxxxxxxxxxxxxxxxxxxxplllss'; + + const consoleSpy = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined); + + server.use( + rest.get( + `${testNetwork.apiAddress}/${ACCOUNTS_ENDPOINT}/${missingAccountAddress}`, + (_req, res, ctx) => { + return res(ctx.status(404), ctx.json({ error: 'account not found' })); + } + ) + ); + + try { + const account = await getAccountFromApi({ + address: missingAccountAddress, + baseURL: testNetwork.apiAddress + }); + + expect(account).toBeNull(); + expect(consoleSpy).toHaveBeenCalledWith( + 'error fetching configuration for ', + missingAccountAddress + ); + } finally { + consoleSpy.mockRestore(); + } + }); +}); diff --git a/src/apiCalls/configuration/getGasStationMetadata.ts b/src/apiCalls/configuration/getGasStationMetadata.ts index f34330de7..44928b0ce 100644 --- a/src/apiCalls/configuration/getGasStationMetadata.ts +++ b/src/apiCalls/configuration/getGasStationMetadata.ts @@ -1,5 +1,6 @@ import axios from 'axios'; import { getCleanApiAddress } from 'apiCalls/configuration/getCleanApiAddress'; +import { GAS_STATION_ENDPOINT } from 'apiCalls/endpoints'; import { NetworkType } from 'types/network.types'; interface IGasStationApiResponse { @@ -8,8 +9,6 @@ interface IGasStationApiResponse { faster: number; } -const GAS_STATION_ENDPOINT = 'transactions/ppu'; - export async function getGasStationMetadataFromApi( shard: number, customApiAddress?: string diff --git a/src/apiCalls/configuration/index.ts b/src/apiCalls/configuration/index.ts deleted file mode 100644 index e9209fc75..000000000 --- a/src/apiCalls/configuration/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './getServerConfiguration'; -export * from './getNetworkConfigFromApi'; diff --git a/src/apiCalls/endpoints.ts b/src/apiCalls/endpoints.ts index 257240548..cd822f8c2 100644 --- a/src/apiCalls/endpoints.ts +++ b/src/apiCalls/endpoints.ts @@ -19,6 +19,7 @@ export const ROLES_ENDPOINT = 'roles'; export const SC_RESULTS_ENDPOINT = 'sc-results'; export const STAKE_ENDPOINT = 'stake'; export const TOKENS_ENDPOINT = 'tokens'; +export const GAS_STATION_ENDPOINT = 'transactions/ppu'; export const TRANSACTIONS_COUNT_ENDPOINT = 'transactions/count'; export const TRANSACTIONS_BATCH = 'batch'; export const TRANSACTIONS_ENDPOINT = 'transactions'; diff --git a/src/apiCalls/index.ts b/src/apiCalls/index.ts deleted file mode 100644 index 12953a48f..000000000 --- a/src/apiCalls/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from './account'; -export * from './configuration'; -export * from './endpoints'; -export * from './tokens'; -export * from './utils'; -export * from './websocket'; diff --git a/src/apiCalls/tokens/index.ts b/src/apiCalls/tokens/index.ts deleted file mode 100644 index 671c5a368..000000000 --- a/src/apiCalls/tokens/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './getTokenDetails'; diff --git a/src/apiCalls/transactions/tests/getServerTransactionsByHashes.test.ts b/src/apiCalls/transactions/tests/getServerTransactionsByHashes.test.ts index f0cd23962..733ebcfbf 100644 --- a/src/apiCalls/transactions/tests/getServerTransactionsByHashes.test.ts +++ b/src/apiCalls/transactions/tests/getServerTransactionsByHashes.test.ts @@ -18,6 +18,10 @@ const tx = { }; describe('getServerTransactionsByHashes', () => { + afterEach(() => { + server.resetHandlers(); + }); + it('returns transactions for provided hashes', async () => { const hash = tx.txHash; diff --git a/src/apiCalls/transactions/tests/getTransactionByHash.test.ts b/src/apiCalls/transactions/tests/getTransactionByHash.test.ts index b0e79e498..e7799e782 100644 --- a/src/apiCalls/transactions/tests/getTransactionByHash.test.ts +++ b/src/apiCalls/transactions/tests/getTransactionByHash.test.ts @@ -34,6 +34,9 @@ describe('getTransactionByHash', () => { jest.resetAllMocks(); (networkSelector as jest.Mock).mockReturnValue(testNetwork); }); + afterEach(() => { + server.resetHandlers(); + }); it('returns a transaction for the provided hash', async () => { // Use MSW to intercept the request to testNetwork.apiAddress diff --git a/src/apiCalls/transactions/tests/getTransactionsByHashes.test.ts b/src/apiCalls/transactions/tests/getTransactionsByHashes.test.ts index d8893c42d..8d987f3a5 100644 --- a/src/apiCalls/transactions/tests/getTransactionsByHashes.test.ts +++ b/src/apiCalls/transactions/tests/getTransactionsByHashes.test.ts @@ -20,6 +20,9 @@ describe('getTransactionsByHashes test', () => { } }); }); + afterEach(() => { + server.resetHandlers(); + }); it('maps server transactions to tracked results for provided pending hashes', async () => { const pending = { ...mockPendingTransaction }; diff --git a/src/apiCalls/utils/index.ts b/src/apiCalls/utils/index.ts deleted file mode 100644 index ccf8b37a2..000000000 --- a/src/apiCalls/utils/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './axiosFetcher'; -export * from './axiosInstance'; -export * from './getCleanApiAddress'; diff --git a/src/apiCalls/websocket/index.ts b/src/apiCalls/websocket/index.ts deleted file mode 100644 index a1842b167..000000000 --- a/src/apiCalls/websocket/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './getWebsocketUrl'; diff --git a/src/controllers/TransactionsTableController/tests/transactionsTableController.test.ts b/src/controllers/TransactionsTableController/tests/transactionsTableController.test.ts index ef1fb2258..27ea00c15 100644 --- a/src/controllers/TransactionsTableController/tests/transactionsTableController.test.ts +++ b/src/controllers/TransactionsTableController/tests/transactionsTableController.test.ts @@ -1,5 +1,5 @@ import { testAddress } from '__mocks__'; -import { account } from '__mocks__/data'; +import { account, testToken } from '__mocks__/data'; import { getPersistedTokenDetails } from 'apiCalls/tokens/getPersistedTokenDetails'; import { AssetType } from 'types/account.types'; import { TransactionServerStatusesEnum } from 'types/enums.types'; @@ -27,7 +27,7 @@ jest.mock('utils/operations/timeRemaining', () => ({ const mockTransactionBase: ServerTransactionType = { txHash: 'tx1', - tokenIdentifier: 'TEST-123', + tokenIdentifier: testToken.identifier, sender: testAddress, receiver: account.address, senderShard: 0, @@ -85,6 +85,14 @@ const mockParams = { }; describe('TransactionsTableController', () => { + beforeEach(() => { + (getPersistedTokenDetails as jest.Mock).mockResolvedValue({ + assets: { + lockedAccounts: {} + } + }); + }); + it('should handle locked accounts', async () => { (getPersistedTokenDetails as jest.Mock).mockResolvedValue({ assets: { diff --git a/src/index.ts b/src/index.ts index 4351f5438..64c4c5c32 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,3 @@ -export * from './apiCalls'; export * from './controllers'; export * from './constants'; export * from './services'; diff --git a/src/managers/ToastManager/helpers/tests/createToastsFromTransactions.test.ts b/src/managers/ToastManager/helpers/tests/createToastsFromTransactions.test.ts index c8656d471..34a5fb650 100644 --- a/src/managers/ToastManager/helpers/tests/createToastsFromTransactions.test.ts +++ b/src/managers/ToastManager/helpers/tests/createToastsFromTransactions.test.ts @@ -59,6 +59,7 @@ describe('createToastsFromTransactions', () => { afterEach(() => { jest.spyOn(Date, 'now').mockRestore(); + server.resetHandlers(); }); const mockTransactionsEndpoint = ( diff --git a/src/managers/ToastManager/tests/ToastManager.test.ts b/src/managers/ToastManager/tests/ToastManager.test.ts index cc7ad7711..5f8b14aea 100644 --- a/src/managers/ToastManager/tests/ToastManager.test.ts +++ b/src/managers/ToastManager/tests/ToastManager.test.ts @@ -23,6 +23,9 @@ describe('ToastManager subscription reacts to transaction completion', () => { ) ); }); + afterEach(() => { + server.resetHandlers(); + }); it('moves toast from pending to completed when status changes to success and starts lifetime', async () => { const startTime = 1_700_000_000_000; // arbitrary diff --git a/src/methods/initApp/websocket/initializeWebsocketConnection.ts b/src/methods/initApp/websocket/initializeWebsocketConnection.ts index aa084ccbd..a0ddfe838 100644 --- a/src/methods/initApp/websocket/initializeWebsocketConnection.ts +++ b/src/methods/initApp/websocket/initializeWebsocketConnection.ts @@ -1,5 +1,5 @@ import { io } from 'socket.io-client'; -import { getWebsocketUrl } from 'apiCalls/websocket'; +import { getWebsocketUrl } from 'apiCalls/websocket/getWebsocketUrl'; import { WebsocketConnectionStatusEnum, websocketConnection diff --git a/src/providers/strategies/LedgerProviderStrategy/helpers/authenticateLedgerAccount.ts b/src/providers/strategies/LedgerProviderStrategy/helpers/authenticateLedgerAccount.ts index e9dd29cee..32cb503f4 100644 --- a/src/providers/strategies/LedgerProviderStrategy/helpers/authenticateLedgerAccount.ts +++ b/src/providers/strategies/LedgerProviderStrategy/helpers/authenticateLedgerAccount.ts @@ -1,7 +1,7 @@ import { HWProvider } from '@multiversx/sdk-hw-provider/out'; import { BigNumber } from 'bignumber.js'; -import { ACCOUNTS_ENDPOINT } from 'apiCalls'; +import { ACCOUNTS_ENDPOINT } from 'apiCalls/endpoints'; import { IEventBus } from 'lib/sdkDappUi'; import { LedgerConnectStateManager } from 'managers/internal/LedgerConnectStateManager/LedgerConnectStateManager'; import { getExplorerAddress } from 'methods/network/getExplorerAddress'; diff --git a/src/providers/strategies/helpers/signTransactions/helpers/getCommonData/getCommonData.ts b/src/providers/strategies/helpers/signTransactions/helpers/getCommonData/getCommonData.ts index 2839cfa8e..2979bb547 100644 --- a/src/providers/strategies/helpers/signTransactions/helpers/getCommonData/getCommonData.ts +++ b/src/providers/strategies/helpers/signTransactions/helpers/getCommonData/getCommonData.ts @@ -1,5 +1,5 @@ import { BigNumber } from 'bignumber.js'; -import { ACCOUNTS_ENDPOINT } from 'apiCalls'; +import { ACCOUNTS_ENDPOINT } from 'apiCalls/endpoints'; import { getPersistedTokenDetails } from 'apiCalls/tokens/getPersistedTokenDetails'; import { MULTI_TRANSFER_EGLD_TOKEN } from 'constants/mvx.constants'; import { safeWindow } from 'constants/window.constants'; diff --git a/src/providers/strategies/helpers/signTransactions/helpers/getCommonData/helpers/getExtractTransactionsInfo.ts b/src/providers/strategies/helpers/signTransactions/helpers/getCommonData/helpers/getExtractTransactionsInfo.ts index 2e269285b..3080b19e0 100644 --- a/src/providers/strategies/helpers/signTransactions/helpers/getCommonData/helpers/getExtractTransactionsInfo.ts +++ b/src/providers/strategies/helpers/signTransactions/helpers/getCommonData/helpers/getExtractTransactionsInfo.ts @@ -1,4 +1,4 @@ -import { getAccountFromApi } from 'apiCalls/account'; +import { getAccountFromApi } from 'apiCalls/account/getAccountFromApi'; import { getScamAddressData } from 'apiCalls/account/getScamAddressData'; import { SigningErrorsEnum } from 'types/enums.types'; diff --git a/src/providers/strategies/helpers/signTransactions/helpers/getCommonData/tests/getCommonData.test.ts b/src/providers/strategies/helpers/signTransactions/helpers/getCommonData/tests/getCommonData.test.ts index 377210d31..7435fc134 100644 --- a/src/providers/strategies/helpers/signTransactions/helpers/getCommonData/tests/getCommonData.test.ts +++ b/src/providers/strategies/helpers/signTransactions/helpers/getCommonData/tests/getCommonData.test.ts @@ -1,3 +1,9 @@ +import { account, mockResponse, rest, server } from '__mocks__'; +import { + testAddress, + testNetwork, + testReceiver +} from '__mocks__/accountConfig'; import { Transaction } from 'lib/sdkCore'; import { MultiSignTransactionType, @@ -32,7 +38,11 @@ jest.mock('methods/network/getExplorerAddress', () => ({ getExplorerAddress: jest.fn(() => 'http://devnet-explorer.multiversx.com') })); -describe('getCommonData', () => { +describe('getCommonData tests', () => { + afterEach(() => { + server.resetHandlers(); + }); + it('should return the common data without ppu', async () => { const commonData = await getCommonData({ ...mockData, @@ -110,6 +120,35 @@ describe('getCommonData', () => { } }); }); + it('should work for transactions signed by guardian', async () => { + // Use MSW to intercept the request to testNetwork.apiAddress + server.use( + rest.get( + `${testNetwork.apiAddress}/accounts/${testAddress}`, // fetching the account of the sender + mockResponse({ + ...account, + activeGuardianAddress: testReceiver, + activeGuardianActivationEpoch: 351, + activeGuardianServiceUid: 'MultiversXSafeguard' + }) + ) + ); + const customMockData = { + ...mockData, + allTransactions: [ + // keep one transaction + { + ...mockGetCommonDataInput.allTransactions[0], + transaction: Transaction.newFromPlainObject( + mockGetCommonDataInput.allTransactions[0].transaction + ) + } + ], + address: testReceiver // we are logged in as the guardianAddress + }; + const commonData = await getCommonData(customMockData); + expect(commonData.commonData.address).toBe(testReceiver); + }); }); describe('when the gas limit is updated', () => { diff --git a/src/utils/transactions/getActiveTransactionsStatus.ts b/src/utils/transactions/getActiveTransactionsStatus.ts index 6211bd279..ddc41494a 100644 --- a/src/utils/transactions/getActiveTransactionsStatus.ts +++ b/src/utils/transactions/getActiveTransactionsStatus.ts @@ -13,6 +13,7 @@ export interface GetActiveTransactionsStatusReturnType { pending: boolean; } +// This helper function is used in some projects like NextJS to check the active transaction status export function getActiveTransactionsStatus(): GetActiveTransactionsStatusReturnType { const state = getState(); const timedOutTransactions = timedOutTransactionsSelector(state); diff --git a/src/utils/transactions/index.ts b/src/utils/transactions/index.ts index 5fb1ead40..8d2ee89d6 100644 --- a/src/utils/transactions/index.ts +++ b/src/utils/transactions/index.ts @@ -1,5 +1,4 @@ export * from './explorerUrlBuilder'; -export * from './getActiveTransactionsStatus'; export * from './getDecodedDataField'; export * from './getHumanReadableTimeFormat'; export * from './getTransactionsHistory'; diff --git a/src/utils/validation/tests/maxDecimals.test.ts b/src/utils/validation/tests/maxDecimals.test.ts new file mode 100644 index 000000000..99b35ff36 --- /dev/null +++ b/src/utils/validation/tests/maxDecimals.test.ts @@ -0,0 +1,28 @@ +import { maxDecimals } from '../maxDecimals'; + +describe('maxDecimals tests', () => { + it('returns true when amount has no decimal part', () => { + expect(maxDecimals('100')).toBe(true); + }); + + it('returns true when decimal part length is within the limit', () => { + expect(maxDecimals('123.456', 6)).toBe(true); + }); + + it('returns true when decimal part length equals the limit', () => { + expect(maxDecimals('0.123456', 6)).toBe(true); + }); + + it('returns false when decimal part length exceeds the default limit of 18', () => { + expect(maxDecimals('1.1234567890123456789')).toBe(false); + }); + + it('returns false when decimal part length exceeds the provided limit', () => { + expect(maxDecimals('10.123', 2)).toBe(false); + }); + + it('returns true when amount is null or undefined', () => { + expect(maxDecimals(null as unknown as string)).toBe(true); + expect(maxDecimals(undefined as unknown as string)).toBe(true); + }); +});