diff --git a/app/constants/network.js b/app/constants/network.js index 7e5a186e9a0..9ccc62d5749 100644 --- a/app/constants/network.js +++ b/app/constants/network.js @@ -1,10 +1,12 @@ +import { NetworkType } from '@metamask/controller-utils'; + export const MAINNET = 'mainnet'; export const HOMESTEAD = 'homestead'; export const GOERLI = 'goerli'; export const SEPOLIA = 'sepolia'; export const LINEA_GOERLI = 'linea-goerli'; export const LINEA_MAINNET = 'linea-mainnet'; -export const RPC = 'rpc'; +export const RPC = NetworkType.rpc; export const NO_RPC_BLOCK_EXPLORER = 'NO_BLOCK_EXPLORER'; export const PRIVATENETWORK = 'PRIVATENETWORK'; export const DEFAULT_MAINNET_CUSTOM_NAME = 'Ethereum Main Custom'; diff --git a/app/core/AppConstants.ts b/app/core/AppConstants.ts index dd642059a8f..16c2e0b086a 100644 --- a/app/core/AppConstants.ts +++ b/app/core/AppConstants.ts @@ -1,3 +1,4 @@ +import { CoreTypes } from '@walletconnect/types'; import Device from '../util/device'; const DEVELOPMENT = 'development'; @@ -61,7 +62,7 @@ export default { native: 'metamask://', universal: 'https://metamask.app.link/', }, - }, + } as CoreTypes.Metadata, }, SWAPS: { ACTIVE: true, diff --git a/app/core/BackupVault/backupVault.test.ts b/app/core/BackupVault/backupVault.test.ts index 57b0228dfe0..605af3b12b1 100644 --- a/app/core/BackupVault/backupVault.test.ts +++ b/app/core/BackupVault/backupVault.test.ts @@ -1,5 +1,6 @@ import { backupVault } from './backupVault'; import { VAULT_BACKUP_FAILED_UNDEFINED } from '../../constants/error'; +import { KeyringControllerState } from '@metamask/keyring-controller'; //TODO Mock the react-native-keychain module test the other functions inside backupVault /* @@ -12,9 +13,11 @@ import { VAULT_BACKUP_FAILED_UNDEFINED } from '../../constants/error'; */ describe('backupVault', () => { it('should return an error response when the vault is undefined', async () => { - const keyringState = { + const keyringState: KeyringControllerState = { vault: undefined, keyrings: [], + isUnlocked: false, + keyringTypes: [], }; const response = await backupVault(keyringState); expect(response.success).toBe(false); diff --git a/app/core/GasPolling/GasPolling.test.ts b/app/core/GasPolling/GasPolling.test.ts index 066e5d452c7..864970b7d26 100644 --- a/app/core/GasPolling/GasPolling.test.ts +++ b/app/core/GasPolling/GasPolling.test.ts @@ -3,9 +3,10 @@ import { startGasPolling, getEIP1559TransactionData, stopGasPolling, - // useDataStore, } from './GasPolling'; import { parseTransactionEIP1559 } from '../../util/transactions'; +import { GasFeeOptions, GetEIP1559TransactionDataProps } from './types'; +import AppConstants from '../AppConstants'; jest.mock('../../util/transactions'); const mockedParseTransactionEIP1559 = parseTransactionEIP1559 as jest.MockedFunction< @@ -30,39 +31,43 @@ jest.mock('react-redux', () => ({ })); const suggestedGasLimit = '0x123'; +const selectedOption = 'medium'; const gas = { maxWaitTimeEstimate: 45000, minWaitTimeEstimate: 15000, suggestedMaxFeePerGas: '1.500000018', suggestedMaxPriorityFeePerGas: '1.5', + selectedOption, }; -const selectedOption = 'medium'; -const gasFeeEstimates = { - baseFeeTrend: 'down', - estimatedBaseFee: '0.000000013', - high: { - maxWaitTimeEstimate: 60000, - minWaitTimeEstimate: 15000, - suggestedMaxFeePerGas: '2.450000023', - suggestedMaxPriorityFeePerGas: '2.45', - }, - historicalBaseFeeRange: ['0.000000009', '0.000000014'], - historicalPriorityFeeRange: ['1', '96'], - latestPriorityFeeRange: ['1.5', '2.999999783'], - low: { - maxWaitTimeEstimate: 30000, - minWaitTimeEstimate: 15000, - suggestedMaxFeePerGas: '1.410000013', - suggestedMaxPriorityFeePerGas: '1.41', - }, - medium: { - maxWaitTimeEstimate: 45000, - minWaitTimeEstimate: 15000, - suggestedMaxFeePerGas: '1.500000018', - suggestedMaxPriorityFeePerGas: '1.5', +const gasFeeEstimates: GasFeeOptions = { + estimatedBaseFee: null, + gasFeeEstimates: { + baseFeeTrend: 'down', + estimatedBaseFee: '0.000000013', + high: { + maxWaitTimeEstimate: 60000, + minWaitTimeEstimate: 15000, + suggestedMaxFeePerGas: '2.450000023', + suggestedMaxPriorityFeePerGas: '2.45', + }, + historicalBaseFeeRange: ['0.000000009', '0.000000014'], + historicalPriorityFeeRange: ['1', '96'], + latestPriorityFeeRange: ['1.5', '2.999999783'], + low: { + maxWaitTimeEstimate: 30000, + minWaitTimeEstimate: 15000, + suggestedMaxFeePerGas: '1.410000013', + suggestedMaxPriorityFeePerGas: '1.41', + }, + medium: { + maxWaitTimeEstimate: 45000, + minWaitTimeEstimate: 15000, + suggestedMaxFeePerGas: '1.500000018', + suggestedMaxPriorityFeePerGas: '1.5', + }, + networkCongestion: 0.4713, + priorityFeeTrend: 'level', }, - networkCongestion: 0.4713, - priorityFeeTrend: 'level', }; const contractExchangeRates = {}; const conversionRate = 1844.31; @@ -104,10 +109,9 @@ describe('GasPolling', () => { }); describe('GetEIP1559TransactionData', () => { - const transactionData = { + const transactionData: GetEIP1559TransactionDataProps = { suggestedGasLimit, gas, - selectedOption, gasFeeEstimates, transactionState, contractExchangeRates, @@ -117,10 +121,9 @@ describe('GetEIP1559TransactionData', () => { }; it('should fail when incomplete props is passed for ', async () => { - const incompleteTransactionData = { + const incompleteTransactionData: Partial = { suggestedGasLimit, gas, - selectedOption, gasFeeEstimates, transactionState, contractExchangeRates, @@ -169,7 +172,7 @@ describe('GetEIP1559TransactionData', () => { suggestedMaxPriorityFeePerGasHex: '59682f00', timeEstimate: 'Likely in < 30 seconds', timeEstimateColor: 'green', - timeEstimateId: 'likely', + timeEstimateId: AppConstants.GAS_TIMES.LIKELY, totalMaxConversion: '1844.4', totalMaxHex: 'de0e3e3ba6645f6', totalMaxNative: '1.00005', diff --git a/app/core/GasPolling/GasPolling.ts b/app/core/GasPolling/GasPolling.ts index 6734326bf31..bccd506f2d2 100644 --- a/app/core/GasPolling/GasPolling.ts +++ b/app/core/GasPolling/GasPolling.ts @@ -124,7 +124,13 @@ export const getEIP1559TransactionData = ({ conversionRate, currentCurrency, nativeCurrency, - transactionState, + transactionState: { + selectedAsset: transactionState.selectedAsset, + transaction: { + value: transactionState.transaction.value, + data: transactionState.transaction.data, + }, + }, gasFeeEstimates, swapsParams: undefined, selectedGasFee: { diff --git a/app/core/GasPolling/types.ts b/app/core/GasPolling/types.ts index dccdf355422..d35b65a2150 100644 --- a/app/core/GasPolling/types.ts +++ b/app/core/GasPolling/types.ts @@ -157,31 +157,30 @@ export interface TransactionSharedProps { * For UpdateEIP1559Transaction, the transactionState are undefined. */ transactionState: { - assetType: string | undefined; - ensRecipient: string | undefined; - id: string | undefined; - nonce: string | undefined; - paymentRequest: string | undefined; - proposedNonce: string | undefined; - readableValue: string | undefined; + assetType?: string; + ensRecipient?: string; + id?: string; + nonce?: string; + proposedNonce?: string; + readableValue?: string; selectedAsset: Record; - symbol: string | undefined; + symbol?: string; transaction: { - data: string | undefined; - from: string | undefined; - gas: string | undefined; - gasPrice: string | undefined; - maxFeePerGas: string | undefined; - maxPriorityFeePerGas: string | undefined; - to: string | undefined; - value: string | undefined; + data?: string; + from?: string; + gas?: string; + gasPrice?: string; + maxFeePerGas?: string; + maxPriorityFeePerGas?: string; + to?: string; + value?: string; }; - transactionFromName: string | undefined; - transactionTo: string | undefined; - transactionToName: string | undefined; - transactionValue: string | undefined; - type: string | undefined; - warningGasPriceHigh: string | undefined; + transactionFromName?: string; + transactionTo?: string; + transactionToName?: string; + transactionValue?: string; + type?: string; + warningGasPriceHigh?: string; }; contractExchangeRates: Record; } diff --git a/app/core/RPCMethods/RPCMethodMiddleware.test.ts b/app/core/RPCMethods/RPCMethodMiddleware.test.ts index c21febb67c2..2d954f58395 100644 --- a/app/core/RPCMethods/RPCMethodMiddleware.test.ts +++ b/app/core/RPCMethods/RPCMethodMiddleware.test.ts @@ -17,6 +17,9 @@ import { getRpcMethodMiddleware } from './RPCMethodMiddleware'; import AppConstants from '../AppConstants'; import { PermissionConstraint } from '@metamask/permission-controller'; import PPOMUtil from '../../lib/ppom/ppom-util'; +import initialBackgroundState from '../../util/test/initial-background-state.json'; +import { Store } from 'redux'; +import { RootState } from 'app/reducers'; jest.mock('../Engine', () => ({ context: { @@ -55,8 +58,6 @@ jest.mock('../../store', () => ({ }, })); -const mockStore = store as { getState: jest.Mock }; - jest.mock('../Permissions', () => ({ getPermittedAccounts: jest.fn(), })); @@ -212,21 +213,25 @@ function setupGlobalState({ providerConfig?: ProviderConfig; selectedAddress?: string; }) { - mockStore.getState.mockImplementation(() => ({ - browser: activeTab - ? { - activeTab, - } - : {}, - engine: { - backgroundState: { - NetworkController: { - providerConfig: providerConfig || {}, + // TODO: Remove any cast once PermissionController type is fixed. Currently, the state shows never. + jest + .spyOn(store as Store, any>, 'getState') + .mockImplementation(() => ({ + browser: activeTab + ? { + activeTab, + } + : {}, + engine: { + backgroundState: { + ...initialBackgroundState, + NetworkController: { + providerConfig: providerConfig || {}, + }, + PreferencesController: selectedAddress ? { selectedAddress } : {}, }, - PreferencesController: selectedAddress ? { selectedAddress } : {}, - }, - }, - })); + } as any, + })); if (addTransactionResult) { MockEngine.context.TransactionController.addTransaction.mockImplementation( async () => ({ result: addTransactionResult }), diff --git a/app/core/RPCMethods/eth_sendTransaction.test.ts b/app/core/RPCMethods/eth_sendTransaction.test.ts index 1c2f6d50562..6235abdc623 100644 --- a/app/core/RPCMethods/eth_sendTransaction.test.ts +++ b/app/core/RPCMethods/eth_sendTransaction.test.ts @@ -86,7 +86,7 @@ function getMockAddTransaction({ ) => { expect(deviceConfirmedOn).toBe('metamask_mobile'); if (expectedOrigin) { - expect(origin).toBe(expectedOrigin); + expect(origin).toBe(expectedOrigin.origin); } if (expectedTransaction) { expect(transaction).toBe(expectedTransaction); @@ -120,7 +120,7 @@ describe('eth_sendTransaction', () => { res: pendingResult, sendTransaction: getMockAddTransaction({ expectedTransaction: mockTransactionParameters, - expectedOrigin: 'example.metamask.io', + expectedOrigin: { origin: 'example.metamask.io' }, returnValue: expectedResult, }), validateAccountAndChainId: jest.fn(), @@ -205,7 +205,7 @@ describe('eth_sendTransaction', () => { res: constructPendingJsonRpcResponse(), sendTransaction: getMockAddTransaction({ expectedTransaction: mockTransactionParameters, - expectedOrigin: 'example.metamask.io', + expectedOrigin: { origin: 'example.metamask.io' }, addTransactionError: new Error('Failed to add transaction'), }), validateAccountAndChainId: jest.fn(), @@ -225,7 +225,7 @@ describe('eth_sendTransaction', () => { res: constructPendingJsonRpcResponse(), sendTransaction: getMockAddTransaction({ expectedTransaction: mockTransactionParameters, - expectedOrigin: 'example.metamask.io', + expectedOrigin: { origin: 'example.metamask.io' }, processTransactionError: new Error('User rejected the transaction'), }), validateAccountAndChainId: jest.fn(), @@ -246,7 +246,7 @@ describe('eth_sendTransaction', () => { res: pendingResult, sendTransaction: getMockAddTransaction({ expectedTransaction: mockTransactionParameters, - expectedOrigin: 'example.metamask.io', + expectedOrigin: { origin: 'example.metamask.io' }, returnValue: expectedResult, }), validateAccountAndChainId: jest.fn(), diff --git a/app/core/SDKConnect/SDKConnect.ts b/app/core/SDKConnect/SDKConnect.ts index a1f91e63c99..51898b68587 100644 --- a/app/core/SDKConnect/SDKConnect.ts +++ b/app/core/SDKConnect/SDKConnect.ts @@ -61,7 +61,7 @@ export interface ConnectionProps { initialConnection?: boolean; originatorInfo?: OriginatorInfo; validUntil: number; - lastAuthorized: number; // timestamp of last received activity + lastAuthorized?: number; // timestamp of last received activity } export interface ConnectedSessions { [id: string]: Connection; @@ -133,7 +133,7 @@ export class Connection extends EventEmitter2 { /* * Timestamp of last activity, used to check if channel is still active and to prevent showing OTP approval modal too often. */ - lastAuthorized: number; + lastAuthorized?: number; /** * Prevent double sending 'authorized' message. diff --git a/app/core/WalletConnect/WalletConnect.test.ts b/app/core/WalletConnect/WalletConnect.test.ts index ca7d542b407..faf4845edc4 100644 --- a/app/core/WalletConnect/WalletConnect.test.ts +++ b/app/core/WalletConnect/WalletConnect.test.ts @@ -41,10 +41,12 @@ describe('WalletConnect', () => { }); const walletConnectorRejectSessionMock = jest.fn(); - RNWalletConnect.mockImplementation(() => ({ - on: walletConnectorSessionRequestCallbackMock, - rejectSession: walletConnectorRejectSessionMock, - })); + jest + .spyOn(RNWalletConnect.prototype, 'on') + .mockImplementation(walletConnectorSessionRequestCallbackMock); + jest + .spyOn(RNWalletConnect.prototype, 'rejectSession') + .mockImplementation(walletConnectorRejectSessionMock); afterEach(() => { // Reset WalletConnect diff --git a/app/declarations.d.ts b/app/declarations.d.ts index 7111ebc0a9d..42434fa9c1b 100644 --- a/app/declarations.d.ts +++ b/app/declarations.d.ts @@ -32,6 +32,8 @@ declare module '*.png' { export default content; } +// TODO: eth-json-rpc-errors does not contain types. May want to create our own types. +declare module 'eth-json-rpc-errors'; declare module '@react-native-community/checkbox' { import CheckBoxOriginal from '@react-native-community/checkbox'; diff --git a/app/util/general/index.js b/app/util/general/index.js index bb3bf699244..7eea47f10d0 100644 --- a/app/util/general/index.js +++ b/app/util/general/index.js @@ -103,7 +103,7 @@ export const getURLProtocol = (url) => { * ipfs:// -> true * ipfs://ipfs/ -> true * https:// -> false - * @param {string | null} uri - string representing the source uri to the file + * @param {string | null | undefined} uri - string representing the source uri to the file * @returns true if it's an ipfs url */ export const isIPFSUri = (uri) => { diff --git a/app/util/transactions/index.js b/app/util/transactions/index.js index 79abd4b9350..a06a928861d 100644 --- a/app/util/transactions/index.js +++ b/app/util/transactions/index.js @@ -659,7 +659,7 @@ export const calculateEIP1559Times = ({ }) => { let timeEstimate = strings('times_eip1559.unknown'); let timeEstimateColor = 'grey'; - let timeEstimateId = AppConstants.GAS_TIMES.UNKNOWN; + let timeEstimateId; const LOW = AppConstants.GAS_OPTIONS.LOW; const MEDIUM = AppConstants.GAS_OPTIONS.MEDIUM; @@ -800,6 +800,9 @@ export const calculateEIP1559Times = ({ } catch (error) { Logger.log('ERROR ESTIMATING TIME', error); } + if (!timeEstimateId) { + timeEstimate = AppConstants.GAS_TIMES.UNKNOWN; + } return { timeEstimate, timeEstimateColor, timeEstimateId }; }; diff --git a/locales/languages/en.json b/locales/languages/en.json index a23a73cc4ae..36ea8da088a 100644 --- a/locales/languages/en.json +++ b/locales/languages/en.json @@ -791,7 +791,6 @@ "incoming_transactions_title": "Show incoming transactions", "incoming_transactions_content": "This relies on the network you select which will expose your Ethereum address and your IP address.", "display_nft_media_desc_new": "Displaying NFT media and data exposes your IP address to OpenSea or other third parties. NFT autodetection relies on this feature, and won't be available when turned off. If NFT media is fully located on IPFS, it can still be displayed even when this feature is turned off." - }, "sdk": { "disconnect_title": "Disconnect all sites?", diff --git a/tsconfig.lint.json b/tsconfig.lint.json index 420a95252c8..fbca45116d8 100644 --- a/tsconfig.lint.json +++ b/tsconfig.lint.json @@ -10,7 +10,7 @@ "app/component-library/hooks/**/*", // "app/components/**/*", "app/constants/**/*", - // "app/core/**/*", + "app/core/**/*", "app/core/*.ts", "app/images/**/*", "app/lib/**/*",