From 1f05f13784504f022df435e1dd6b5128b8c32abc Mon Sep 17 00:00:00 2001 From: Elliot Winkler Date: Wed, 30 Mar 2022 15:56:49 -0600 Subject: [PATCH 1/2] Fix inclusion of module-augmenting .d.ts's A previous commit updated the TypeScript configuration so TypeScript would better recognize type definition files whose purpose was to backfill types for existing packages. After testing, this revealed a misunderstanding of the `paths` option in `tsconfig.json`. It turns out that `paths` completely overrides how TypeScript resolves modules and locates type definition files for modules. The consequence of this option is that if a module includes type definitions, but we also supply type definitions for that module in `types/`, then our type definitions will win. This degrades the developer experience: sometimes, a package already has types, but we merely need to *augment* those types in some way. The `paths` option makes it impossible to do this. This commit keeps the `types/` directory (as it'll be important later) but informs TypeScript about the type definition files here by adding them to the `include` option in `tsconfig.json` (which was the original strategy). --- tsconfig.build.json | 1 - tsconfig.json | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/tsconfig.build.json b/tsconfig.build.json index 70df21e1651..e2c5987c4b7 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -6,6 +6,5 @@ "outDir": "./dist", "rootDir": "./src" }, - "include": ["./src/**/*.ts"], "exclude": ["**/*.test.ts"] } diff --git a/tsconfig.json b/tsconfig.json index 2887202355a..de3572e5f74 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,11 +5,9 @@ "inlineSources": true, "module": "commonjs", "moduleResolution": "node", - "paths": { - "*": ["./types/*"] - }, "sourceMap": true, "strict": true, "target": "es6" - } + }, + "include": ["./types/**/*.d.ts", "./src/**/*.ts"] } From 995a61939085759208eb608c58b8d8f9c1dc5397 Mon Sep 17 00:00:00 2001 From: Elliot Winkler Date: Thu, 17 Mar 2022 10:47:51 -0600 Subject: [PATCH 2/2] Backfill types for some dependencies Some of our internal NPM packages are still untyped. Currently we get around this by stubbing the types for these modules in a `dependencies.d.ts` file, but this merely tags everything exported from the modules as `any`. To address this, this commit adds proper types for most of the modules marked in `dependencies.d.ts` so that these types can be accessed and utilized within the code that uses these modules. The modules that are left over are those that would either take too long to fill in or those that we are planning on removing soon anyway. --- jest.config.js | 2 +- src/assets/AccountTrackerController.test.ts | 66 ++++++-- src/assets/AccountTrackerController.ts | 21 ++- .../ERC1155/ERC1155Standard.test.ts | 3 +- .../ERC721/ERC721Standard.test.ts | 3 +- src/assets/Standards/ERC20Standard.test.ts | 3 +- src/assets/TokenListController.test.ts | 5 +- src/assets/TokenListController.ts | 13 +- src/assets/TokensController.ts | 4 +- src/gas/GasFeeController.ts | 2 +- src/gas/fetchBlockFeeHistory.test.ts | 10 +- src/gas/fetchBlockFeeHistory.ts | 23 +-- .../fetchGasEstimatesViaEthFeeHistory.test.ts | 5 +- src/gas/fetchGasEstimatesViaEthFeeHistory.ts | 4 +- .../fetchLatestBlock.test.ts | 59 +++++++ .../fetchLatestBlock.ts | 14 +- .../types.ts | 5 + src/gas/gas-util.ts | 8 +- src/network/NetworkController.ts | 29 +++- src/third-party/PhishingController.ts | 14 +- src/transaction/TransactionController.ts | 67 ++++---- src/util.test.ts | 31 ++-- src/util.ts | 43 ++++-- tests/util.ts | 30 ++++ types/@metamask/contract-metadata.d.ts | 13 +- types/@metamask/metamask-eth-abis.d.ts | 6 +- types/eth-ens-namehash.d.ts | 5 +- types/eth-json-rpc-infura.d.ts | 41 +++++ .../src/createProvider.d.ts | 15 +- .../eth-phishing-detect/src/config.json.d.ts | 14 +- types/eth-phishing-detect/src/detector.d.ts | 19 ++- types/eth-query.d.ts | 146 +++++++++++++++++- types/ethjs-provider-http.d.ts | 17 +- types/ethjs-unit.d.ts | 65 +++++++- 34 files changed, 660 insertions(+), 145 deletions(-) create mode 100644 src/gas/fetchGasEstimatesViaEthFeeHistory/fetchLatestBlock.test.ts create mode 100644 tests/util.ts create mode 100644 types/eth-json-rpc-infura.d.ts diff --git a/jest.config.js b/jest.config.js index 170d29b000d..fb9e0b4483f 100644 --- a/jest.config.js +++ b/jest.config.js @@ -4,7 +4,7 @@ module.exports = { // ones. collectCoverageFrom: ['./src/**/*.ts'], // TODO: Test index.ts - coveragePathIgnorePatterns: ['./src/index.ts'], + coveragePathIgnorePatterns: ['./src/index.ts', './src/gas/gas-util.ts'], coverageReporters: ['text', 'html'], coverageThreshold: { global: { diff --git a/src/assets/AccountTrackerController.test.ts b/src/assets/AccountTrackerController.test.ts index d5cd4bbad87..40dde862503 100644 --- a/src/assets/AccountTrackerController.test.ts +++ b/src/assets/AccountTrackerController.test.ts @@ -45,6 +45,47 @@ describe('AccountTrackerController', () => { expect(controller.state.accounts[address].balance).toBeDefined(); }); + it('should sync addresses', () => { + const controller = new AccountTrackerController( + { + onPreferencesStateChange: stub(), + getIdentities: () => { + return { baz: {} as ContactEntry }; + }, + }, + { provider }, + { + accounts: { + bar: { balance: '' }, + foo: { balance: '' }, + }, + }, + ); + controller.refresh(); + expect(controller.state.accounts).toStrictEqual({ + baz: { balance: '0x0' }, + }); + }); + + it('does not refresh any accounts if no provider has been set', () => { + const controller = new AccountTrackerController( + { + onPreferencesStateChange: stub(), + getIdentities: () => { + return { baz: {} as ContactEntry }; + }, + }, + {}, + { + accounts: {}, + }, + ); + + controller.refresh(); + + expect(controller.state.accounts).toStrictEqual({}); + }); + it('should sync balance with addresses', async () => { const address = '0xc38bf1ad06ef69f0c04e29dbeb4152b4175f0a8d'; const queryStub = stub(utils, 'query'); @@ -60,28 +101,27 @@ describe('AccountTrackerController', () => { queryStub.returns(Promise.resolve('0x10')); const result = await controller.syncBalanceWithAddresses([address]); expect(result[address].balance).toBe('0x10'); + queryStub.restore(); }); - it('should sync addresses', () => { + it('should not sync balance with addresses if no provider has been set', async () => { + const address = '0xc38bf1ad06ef69f0c04e29dbeb4152b4175f0a8d'; + const queryStub = stub(utils, 'query'); const controller = new AccountTrackerController( { onPreferencesStateChange: stub(), getIdentities: () => { - return { baz: {} as ContactEntry }; - }, - }, - { provider }, - { - accounts: { - bar: { balance: '' }, - foo: { balance: '' }, + return {}; }, }, + {}, ); - controller.refresh(); - expect(controller.state.accounts).toStrictEqual({ - baz: { balance: '0x0' }, - }); + queryStub.returns(Promise.resolve('0x10')); + + const result = await controller.syncBalanceWithAddresses([address]); + + expect(result).toStrictEqual({}); + queryStub.restore(); }); it('should subscribe to new sibling preference controllers', async () => { diff --git a/src/assets/AccountTrackerController.ts b/src/assets/AccountTrackerController.ts index e026ce9e975..016848cbaeb 100644 --- a/src/assets/AccountTrackerController.ts +++ b/src/assets/AccountTrackerController.ts @@ -42,7 +42,7 @@ export class AccountTrackerController extends BaseController< AccountTrackerConfig, AccountTrackerState > { - private ethQuery: any; + private ethQuery: EthQuery | null; private mutex = new Mutex(); @@ -98,6 +98,7 @@ export class AccountTrackerController extends BaseController< state?: Partial, ) { super(config, state); + this.ethQuery = null; this.defaultConfig = { interval: 10000, }; @@ -145,11 +146,17 @@ export class AccountTrackerController extends BaseController< * Refreshes all accounts in the current keychain. */ refresh = async () => { + const { ethQuery } = this; + + if (ethQuery === null) { + return; + } + this.syncAccounts(); const accounts = { ...this.state.accounts }; for (const address in accounts) { await safelyExecuteWithTimeout(async () => { - const balance = await query(this.ethQuery, 'getBalance', [address]); + const balance = await query(ethQuery, 'getBalance', [address]); accounts[address] = { balance: BNToHex(balance) }; }); } @@ -165,11 +172,19 @@ export class AccountTrackerController extends BaseController< async syncBalanceWithAddresses( addresses: string[], ): Promise> { + const { ethQuery } = this; + + if (ethQuery === null) { + return {}; + } + return await Promise.all( addresses.map( (address): Promise<[string, string] | undefined> => { return safelyExecuteWithTimeout(async () => { - const balance = await query(this.ethQuery, 'getBalance', [address]); + const balance = await query(ethQuery, 'getBalance', [ + address, + ]); return [address, balance]; }); }, diff --git a/src/assets/Standards/CollectibleStandards/ERC1155/ERC1155Standard.test.ts b/src/assets/Standards/CollectibleStandards/ERC1155/ERC1155Standard.test.ts index c7e709f15d9..8857a509e3a 100644 --- a/src/assets/Standards/CollectibleStandards/ERC1155/ERC1155Standard.test.ts +++ b/src/assets/Standards/CollectibleStandards/ERC1155/ERC1155Standard.test.ts @@ -1,9 +1,8 @@ import Web3 from 'web3'; -import HttpProvider from 'ethjs-provider-http'; import nock from 'nock'; import { ERC1155Standard } from './ERC1155Standard'; -const MAINNET_PROVIDER = new HttpProvider( +const MAINNET_PROVIDER = new Web3.providers.HttpProvider( 'https://mainnet.infura.io/v3/341eacb578dd44a1a049cbc5f6fd4035', ); diff --git a/src/assets/Standards/CollectibleStandards/ERC721/ERC721Standard.test.ts b/src/assets/Standards/CollectibleStandards/ERC721/ERC721Standard.test.ts index 4e7e3940dc0..a67270db214 100644 --- a/src/assets/Standards/CollectibleStandards/ERC721/ERC721Standard.test.ts +++ b/src/assets/Standards/CollectibleStandards/ERC721/ERC721Standard.test.ts @@ -1,10 +1,9 @@ import Web3 from 'web3'; -import HttpProvider from 'ethjs-provider-http'; import nock from 'nock'; import { IPFS_DEFAULT_GATEWAY_URL } from '../../../../constants'; import { ERC721Standard } from './ERC721Standard'; -const MAINNET_PROVIDER = new HttpProvider( +const MAINNET_PROVIDER = new Web3.providers.HttpProvider( 'https://mainnet.infura.io/v3/341eacb578dd44a1a049cbc5f6fd4035', ); const ERC721_GODSADDRESS = '0x6EbeAf8e8E946F0716E6533A6f2cefc83f60e8Ab'; diff --git a/src/assets/Standards/ERC20Standard.test.ts b/src/assets/Standards/ERC20Standard.test.ts index a49450b3168..a74a279c5b1 100644 --- a/src/assets/Standards/ERC20Standard.test.ts +++ b/src/assets/Standards/ERC20Standard.test.ts @@ -1,9 +1,8 @@ import Web3 from 'web3'; -import HttpProvider from 'ethjs-provider-http'; import nock from 'nock'; import { ERC20Standard } from './ERC20Standard'; -const MAINNET_PROVIDER = new HttpProvider( +const MAINNET_PROVIDER = new Web3.providers.HttpProvider( 'https://mainnet.infura.io/v3/341eacb578dd44a1a049cbc5f6fd4035', ); const ERC20_MATIC_ADDRESS = '0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0'; diff --git a/src/assets/TokenListController.test.ts b/src/assets/TokenListController.test.ts index d8a51beee6e..93d6b3379ac 100644 --- a/src/assets/TokenListController.test.ts +++ b/src/assets/TokenListController.test.ts @@ -12,7 +12,6 @@ import { TokenListStateChange, GetTokenListState, TokenListMap, - ContractMap, } from './TokenListController'; const name = 'TokenListController'; @@ -21,9 +20,7 @@ const timestamp = Date.now(); const staticTokenList: TokenListMap = {}; for (const tokenAddress in contractMap) { - const { erc20, logo: filePath, ...token } = (contractMap as ContractMap)[ - tokenAddress - ]; + const { erc20, logo: filePath, ...token } = contractMap[tokenAddress]; if (erc20) { staticTokenList[tokenAddress] = { ...token, diff --git a/src/assets/TokenListController.ts b/src/assets/TokenListController.ts index 4aced204e57..6759ed228de 100644 --- a/src/assets/TokenListController.ts +++ b/src/assets/TokenListController.ts @@ -21,15 +21,6 @@ type BaseToken = { decimals: number; }; -type StaticToken = { - logo: string; - erc20: boolean; -} & BaseToken; - -export type ContractMap = { - [address: string]: StaticToken; -}; - export type DynamicToken = { address: string; occurrences: number; @@ -241,9 +232,7 @@ export class TokenListController extends BaseController< async fetchFromStaticTokenList(): Promise { const tokenList: TokenListMap = {}; for (const tokenAddress in contractMap) { - const { erc20, logo: filePath, ...token } = (contractMap as ContractMap)[ - tokenAddress - ]; + const { erc20, logo: filePath, ...token } = contractMap[tokenAddress]; if (erc20) { tokenList[tokenAddress] = { ...token, diff --git a/src/assets/TokensController.ts b/src/assets/TokensController.ts index 0f409f0bee0..c491137c0b9 100644 --- a/src/assets/TokensController.ts +++ b/src/assets/TokensController.ts @@ -1,6 +1,6 @@ import { EventEmitter } from 'events'; import contractsMap from '@metamask/contract-metadata'; -import { abiERC721 } from '@metamask/metamask-eth-abis'; +import { abiERC721, ABI } from '@metamask/metamask-eth-abis'; import { v1 as random } from 'uuid'; import { Mutex } from 'async-mutex'; import { ethers } from 'ethers'; @@ -367,7 +367,7 @@ export class TokensController extends BaseController< async _createEthersContract( tokenAddress: string, - abi: string, + abi: ABI, ethersProvider: any, ): Promise { const tokenContract = await new ethers.Contract( diff --git a/src/gas/GasFeeController.ts b/src/gas/GasFeeController.ts index 1196997bae5..e1d45933bf6 100644 --- a/src/gas/GasFeeController.ts +++ b/src/gas/GasFeeController.ts @@ -251,7 +251,7 @@ export class GasFeeController extends BaseController< private currentChainId; - private ethQuery: any; + private ethQuery: EthQuery; private clientId?: string; diff --git a/src/gas/fetchBlockFeeHistory.test.ts b/src/gas/fetchBlockFeeHistory.test.ts index 162ea61bb37..0835784f695 100644 --- a/src/gas/fetchBlockFeeHistory.test.ts +++ b/src/gas/fetchBlockFeeHistory.test.ts @@ -1,13 +1,13 @@ import { BN } from 'ethereumjs-util'; import { mocked } from 'ts-jest/utils'; import { when } from 'jest-when'; +import { buildFakeEthQuery } from '../../tests/util'; import { query, fromHex, toHex } from '../util'; import fetchBlockFeeHistory from './fetchBlockFeeHistory'; jest.mock('../util', () => { return { ...jest.requireActual('../util'), - __esModule: true, query: jest.fn(), }; }); @@ -30,7 +30,7 @@ function times(n: number, fn: (n: number) => T): T[] { } describe('fetchBlockFeeHistory', () => { - const ethQuery = { eth: 'query' }; + const ethQuery = buildFakeEthQuery(); describe('with a minimal set of arguments', () => { const latestBlockNumber = 3; @@ -333,6 +333,12 @@ describe('fetchBlockFeeHistory', () => { const latestBlockNumber = 3; const numberOfRequestedBlocks = 3; + beforeEach(() => { + when(mockedQuery) + .calledWith(ethQuery, 'blockNumber') + .mockResolvedValue(new BN(latestBlockNumber)); + }); + it('includes an extra block with an estimated baseFeePerGas', async () => { when(mockedQuery) .calledWith(ethQuery, 'eth_feeHistory', [ diff --git a/src/gas/fetchBlockFeeHistory.ts b/src/gas/fetchBlockFeeHistory.ts index 553e7b01f40..49d6c9029b6 100644 --- a/src/gas/fetchBlockFeeHistory.ts +++ b/src/gas/fetchBlockFeeHistory.ts @@ -1,7 +1,5 @@ import { BN } from 'ethereumjs-util'; -import { query, fromHex, toHex } from '../util'; - -type EthQuery = any; +import { query, fromHex, toHex, EthQueryish } from '../util'; /** * @type RequestChunkSpecifier @@ -111,7 +109,7 @@ const MAX_NUMBER_OF_BLOCKS_PER_ETH_FEE_HISTORY_CALL = 1024; * - * * @param args - The arguments to this function. - * @param args.ethQuery - An EthQuery instance that wraps a provider for the network in question. + * @param args.ethQuery - An object that {@link query} takes which wraps a provider for the network in question. * @param args.endBlock - The desired end of the requested block range. Can be "latest" if you want * to start from the latest successful block or the number of a known past block. * @param args.numberOfBlocks - How many total blocks to fetch. Note that if this is more than 1024, @@ -138,7 +136,7 @@ export default async function fetchBlockFeeHistory({ percentiles: givenPercentiles = [], includeNextBlock = false, }: { - ethQuery: EthQuery; + ethQuery: EthQueryish; numberOfBlocks: number; endBlock?: 'latest' | BN; percentiles?: readonly Percentile[]; @@ -149,10 +147,13 @@ export default async function fetchBlockFeeHistory({ ? Array.from(new Set(givenPercentiles)).sort((a, b) => a - b) : []; - const finalEndBlockNumber = - givenEndBlock === 'latest' - ? fromHex(await query(ethQuery, 'blockNumber')) - : givenEndBlock; + let finalEndBlockNumber: BN; + if (givenEndBlock === 'latest') { + const latestBlockNumber = await query(ethQuery, 'blockNumber'); + finalEndBlockNumber = fromHex(latestBlockNumber); + } else { + finalEndBlockNumber = givenEndBlock; + } const requestChunkSpecifiers = determineRequestChunkSpecifiers( finalEndBlockNumber, @@ -275,13 +276,13 @@ async function makeRequestForChunk({ percentiles, includeNextBlock, }: { - ethQuery: EthQuery; + ethQuery: EthQueryish; numberOfBlocks: number; endBlockNumber: BN; percentiles: readonly Percentile[]; includeNextBlock: boolean; }): Promise[]> { - const response: EthFeeHistoryResponse = await query( + const response = await query( ethQuery, 'eth_feeHistory', [toHex(numberOfBlocks), toHex(endBlockNumber), percentiles], diff --git a/src/gas/fetchGasEstimatesViaEthFeeHistory.test.ts b/src/gas/fetchGasEstimatesViaEthFeeHistory.test.ts index c82a737730f..5ea138139ac 100644 --- a/src/gas/fetchGasEstimatesViaEthFeeHistory.test.ts +++ b/src/gas/fetchGasEstimatesViaEthFeeHistory.test.ts @@ -1,6 +1,7 @@ import { BN } from 'ethereumjs-util'; import { mocked } from 'ts-jest/utils'; import { when } from 'jest-when'; +import { buildFakeEthQuery } from '../../tests/util'; import fetchBlockFeeHistory from './fetchBlockFeeHistory'; import calculateGasFeeEstimatesForPriorityLevels from './fetchGasEstimatesViaEthFeeHistory/calculateGasFeeEstimatesForPriorityLevels'; import fetchLatestBlock from './fetchGasEstimatesViaEthFeeHistory/fetchLatestBlock'; @@ -24,10 +25,10 @@ describe('fetchGasEstimatesViaEthFeeHistory', () => { number: new BN(1), baseFeePerGas: new BN(100_000_000_000), }; - const ethQuery = { + const ethQuery = buildFakeEthQuery({ blockNumber: async () => latestBlock.number, getBlockByNumber: async () => latestBlock, - }; + }); it('calculates target fees for low, medium, and high transaction priority levels', async () => { const blocks = [ diff --git a/src/gas/fetchGasEstimatesViaEthFeeHistory.ts b/src/gas/fetchGasEstimatesViaEthFeeHistory.ts index 0a829364374..d7918c6306d 100644 --- a/src/gas/fetchGasEstimatesViaEthFeeHistory.ts +++ b/src/gas/fetchGasEstimatesViaEthFeeHistory.ts @@ -1,7 +1,7 @@ import { fromWei } from 'ethjs-unit'; import { GWEI } from '../constants'; +import { EthQueryish } from '../util'; import { GasFeeEstimates } from './GasFeeController'; -import { EthQuery } from './fetchGasEstimatesViaEthFeeHistory/types'; import fetchBlockFeeHistory from './fetchBlockFeeHistory'; import fetchLatestBlock from './fetchGasEstimatesViaEthFeeHistory/fetchLatestBlock'; import calculateGasFeeEstimatesForPriorityLevels from './fetchGasEstimatesViaEthFeeHistory/calculateGasFeeEstimatesForPriorityLevels'; @@ -25,7 +25,7 @@ import calculateGasFeeEstimatesForPriorityLevels from './fetchGasEstimatesViaEth * for the next block's base fee. */ export default async function fetchGasEstimatesViaEthFeeHistory( - ethQuery: EthQuery, + ethQuery: EthQueryish, ): Promise { const latestBlock = await fetchLatestBlock(ethQuery); const blocks = await fetchBlockFeeHistory({ diff --git a/src/gas/fetchGasEstimatesViaEthFeeHistory/fetchLatestBlock.test.ts b/src/gas/fetchGasEstimatesViaEthFeeHistory/fetchLatestBlock.test.ts new file mode 100644 index 00000000000..8952c138fd5 --- /dev/null +++ b/src/gas/fetchGasEstimatesViaEthFeeHistory/fetchLatestBlock.test.ts @@ -0,0 +1,59 @@ +import { BN } from 'ethereumjs-util'; +import { mocked } from 'ts-jest/utils'; +import { when } from 'jest-when'; +import { query } from '../../util'; +import { buildFakeEthQuery } from '../../../tests/util'; +import fetchLatestBlock from './fetchLatestBlock'; + +jest.mock('../../util', () => { + return { + ...jest.requireActual('../../util'), + query: jest.fn(), + }; +}); + +const mockedQuery = mocked(query, true); + +describe('fetchLatestBlock', () => { + const ethQuery = buildFakeEthQuery(); + + it('returns an object that represents the latest block on the network, where number and baseFeePerGas are BN objects instead of hex strings', async () => { + when(mockedQuery) + .calledWith(ethQuery, 'blockNumber') + .mockResolvedValue('0x1'); + + when(mockedQuery) + .calledWith(ethQuery, 'getBlockByNumber', ['0x1', false]) + .mockResolvedValue({ + number: '0x2', + baseFeePerGas: '0x3', + }); + + const latestBlock = await fetchLatestBlock(ethQuery); + + expect(latestBlock).toStrictEqual({ + number: new BN(2), + baseFeePerGas: new BN(3), + }); + }); + + it('passes includeFullTransactionData to the getBlockByNumber query', async () => { + when(mockedQuery) + .calledWith(ethQuery, 'blockNumber') + .mockResolvedValue('0x1'); + + when(mockedQuery) + .calledWith(ethQuery, 'getBlockByNumber', ['0x1', true]) + .mockResolvedValue({ + number: '0x2', + baseFeePerGas: '0x3', + }); + + const latestBlock = await fetchLatestBlock(ethQuery, true); + + expect(latestBlock).toStrictEqual({ + number: new BN(2), + baseFeePerGas: new BN(3), + }); + }); +}); diff --git a/src/gas/fetchGasEstimatesViaEthFeeHistory/fetchLatestBlock.ts b/src/gas/fetchGasEstimatesViaEthFeeHistory/fetchLatestBlock.ts index 01def5bfd5e..fe7f2e63ad3 100644 --- a/src/gas/fetchGasEstimatesViaEthFeeHistory/fetchLatestBlock.ts +++ b/src/gas/fetchGasEstimatesViaEthFeeHistory/fetchLatestBlock.ts @@ -1,5 +1,5 @@ -import { query, fromHex } from '../../util'; -import { EthBlock, EthQuery } from './types'; +import { query, fromHex, EthQueryish } from '../../util'; +import { EthBlock, RawEthBlock } from './types'; /** * Returns information about the latest completed block. @@ -10,14 +10,18 @@ import { EthBlock, EthQuery } from './types'; * @returns The block. */ export default async function fetchLatestBlock( - ethQuery: EthQuery, + ethQuery: EthQueryish, includeFullTransactionData = false, ): Promise { - const blockNumber = await query(ethQuery, 'blockNumber'); - const block = await query(ethQuery, 'getBlockByNumber', [ + const blockNumber = await query(ethQuery, 'blockNumber'); + // According to the spec, `getBlockByNumber` could return null, but to prevent + // backward incompatibilities, we assume that there will always be a latest + // block + const block = await query(ethQuery, 'getBlockByNumber', [ blockNumber, includeFullTransactionData, ]); + return { ...block, number: fromHex(block.number), diff --git a/src/gas/fetchGasEstimatesViaEthFeeHistory/types.ts b/src/gas/fetchGasEstimatesViaEthFeeHistory/types.ts index f58908206f9..80bf0a1804f 100644 --- a/src/gas/fetchGasEstimatesViaEthFeeHistory/types.ts +++ b/src/gas/fetchGasEstimatesViaEthFeeHistory/types.ts @@ -1,5 +1,10 @@ import { BN } from 'ethereumjs-util'; +export type RawEthBlock = { + number: string; + baseFeePerGas: string; +}; + export type EthBlock = { number: BN; baseFeePerGas: BN; diff --git a/src/gas/gas-util.ts b/src/gas/gas-util.ts index 98b82f16a0f..73782eeae73 100644 --- a/src/gas/gas-util.ts +++ b/src/gas/gas-util.ts @@ -17,9 +17,7 @@ const makeClientIdHeader = (clientId: string) => ({ 'X-Client-Id': clientId }); * @returns The decimal string GWEI amount. */ export function normalizeGWEIDecimalNumbers(n: string | number) { - const numberAsWEIHex = gweiDecToWEIBN(n).toString(16); - const numberAsGWEI = weiHexToGweiDec(numberAsWEIHex).toString(10); - return numberAsGWEI; + return weiHexToGweiDec(gweiDecToWEIBN(n).toString(16)); } /** @@ -113,9 +111,9 @@ export async function fetchLegacyGasPriceEstimates( export async function fetchEthGasPriceEstimate( ethQuery: any, ): Promise { - const gasPrice = await query(ethQuery, 'gasPrice'); + const gasPrice = await query(ethQuery, 'gasPrice'); return { - gasPrice: weiHexToGweiDec(gasPrice).toString(), + gasPrice: weiHexToGweiDec(gasPrice), }; } diff --git a/src/network/NetworkController.ts b/src/network/NetworkController.ts index 4a23107e41f..c57e625eb65 100644 --- a/src/network/NetworkController.ts +++ b/src/network/NetworkController.ts @@ -1,5 +1,6 @@ import EthQuery from 'eth-query'; import Subprovider from 'web3-provider-engine/subproviders/provider'; +import { SupportedInfuraNetwork } from 'eth-json-rpc-infura'; import createInfuraProvider from 'eth-json-rpc-infura/src/createProvider'; import createMetamaskProvider from 'web3-provider-engine/zero'; import { Mutex } from 'async-mutex'; @@ -18,7 +19,9 @@ export type NetworkType = | 'ropsten' | 'rpc' | 'optimism' - | 'optimismTest'; + | 'optimism-mainnet' + | 'optimismTest' + | 'optimism-kovan'; export enum NetworksChainId { mainnet = '1', @@ -29,7 +32,9 @@ export enum NetworksChainId { localhost = '', rpc = '', optimism = '10', + 'optimism-mainnet' = '10', optimismTest = '69', + 'optimism-kovan' = '69', } /** @@ -94,7 +99,7 @@ export class NetworkController extends BaseController< NetworkConfig, NetworkState > { - private ethQuery: any; + private ethQuery: EthQuery | null; private internalProviderConfig: ProviderConfig = {} as ProviderConfig; @@ -114,7 +119,9 @@ export class NetworkController extends BaseController< case 'rinkeby': case 'goerli': case 'optimism': + case 'optimism-mainnet': case 'optimismTest': + case 'optimism-kovan': case 'ropsten': this.setupInfuraProvider(type); break; @@ -142,10 +149,10 @@ export class NetworkController extends BaseController< this.ethQuery = new EthQuery(this.provider); } - private setupInfuraProvider(type: NetworkType) { + private setupInfuraProvider(type: SupportedInfuraNetwork) { const infuraProvider = createInfuraProvider({ network: type, - projectId: this.config.infuraProjectId, + projectId: this.config.infuraProjectId ?? '', }); const infuraSubprovider = new Subprovider(infuraProvider); const config = { @@ -231,6 +238,7 @@ export class NetworkController extends BaseController< provider: { type: MAINNET, chainId: NetworksChainId.mainnet }, properties: { isEIP1559Compatible: false }, }; + this.ethQuery = null; this.initialize(); this.getEIP1559Compatibility(); } @@ -263,8 +271,8 @@ export class NetworkController extends BaseController< return; } const releaseLock = await this.mutex.acquire(); - this.ethQuery.sendAsync( - { method: 'net_version' }, + this.ethQuery?.sendAsync( + { id: 1, jsonrpc: '2.0', method: 'net_version' }, (error: Error, network: string) => { this.update({ network: error ? /* istanbul ignore next*/ 'loading' : network, @@ -326,8 +334,13 @@ export class NetworkController extends BaseController< return Promise.resolve(true); } return new Promise((resolve, reject) => { - this.ethQuery.sendAsync( - { method: 'eth_getBlockByNumber', params: ['latest', false] }, + this.ethQuery?.sendAsync( + { + id: 1, + jsonrpc: '2.0', + method: 'eth_getBlockByNumber', + params: ['latest', false], + }, (error: Error, block: Block) => { if (error) { reject(error); diff --git a/src/third-party/PhishingController.ts b/src/third-party/PhishingController.ts index 83910c172f7..312a32cde6b 100644 --- a/src/third-party/PhishingController.ts +++ b/src/third-party/PhishingController.ts @@ -1,5 +1,7 @@ import { toASCII } from 'punycode/'; -import DEFAULT_PHISHING_RESPONSE from 'eth-phishing-detect/src/config.json'; +import DEFAULT_PHISHING_RESPONSE, { + EthPhishingDetectorConfiguration, +} from 'eth-phishing-detect/src/config.json'; import PhishingDetector from 'eth-phishing-detect/src/detector'; import { BaseController, BaseConfig, BaseState } from '../BaseController'; import { safelyExecute } from '../util'; @@ -15,13 +17,7 @@ import { safelyExecute } from '../util'; * @property version - Version number of this configuration * @property whitelist - List of approved origins */ -export interface EthPhishingResponse { - blacklist: string[]; - fuzzylist: string[]; - tolerance: number; - version: number; - whitelist: string[]; -} +export type EthPhishingResponse = EthPhishingDetectorConfiguration; /** * @type PhishingConfig @@ -55,7 +51,7 @@ export class PhishingController extends BaseController< private configUrl = 'https://cdn.jsdelivr.net/gh/MetaMask/eth-phishing-detect@master/src/config.json'; - private detector: any; + private detector: PhishingDetector; private handle?: NodeJS.Timer; diff --git a/src/transaction/TransactionController.ts b/src/transaction/TransactionController.ts index f26d06a9341..3a8c5e43b8c 100644 --- a/src/transaction/TransactionController.ts +++ b/src/transaction/TransactionController.ts @@ -259,7 +259,7 @@ export class TransactionController extends BaseController< TransactionConfig, TransactionState > { - private ethQuery: any; + private ethQuery: EthQuery; private registry: any; @@ -636,7 +636,10 @@ export class TransactionController extends BaseController< const txNonce = nonce || - (await query(this.ethQuery, 'getTransactionCount', [from, 'pending'])); + (await query(this.ethQuery, 'getTransactionCount', [ + from, + 'pending', + ])); transactionMeta.status = status; transactionMeta.transaction.nonce = txNonce; @@ -677,9 +680,11 @@ export class TransactionController extends BaseController< transactionMeta.rawTransaction = rawTransaction; this.updateTransaction(transactionMeta); - const transactionHash = await query(this.ethQuery, 'sendRawTransaction', [ - rawTransaction, - ]); + const transactionHash = await query( + this.ethQuery, + 'sendRawTransaction', + [rawTransaction], + ); transactionMeta.transactionHash = transactionHash; transactionMeta.status = TransactionStatus.submitted; this.updateTransaction(transactionMeta); @@ -808,7 +813,7 @@ export class TransactionController extends BaseController< transactionMeta.transaction.from, ); const rawTransaction = bufferToHex(signedTx.serialize()); - await query(this.ethQuery, 'sendRawTransaction', [rawTransaction]); + await query(this.ethQuery, 'sendRawTransaction', [rawTransaction]); transactionMeta.status = TransactionStatus.cancelled; this.hub.emit(`${transactionMeta.id}:finished`, transactionMeta); } @@ -906,9 +911,11 @@ export class TransactionController extends BaseController< transactionMeta.transaction.from, ); const rawTransaction = bufferToHex(signedTx.serialize()); - const transactionHash = await query(this.ethQuery, 'sendRawTransaction', [ - rawTransaction, - ]); + const transactionHash = await query( + this.ethQuery, + 'sendRawTransaction', + [rawTransaction], + ); const baseTransactionMeta = { ...transactionMeta, id: random(), @@ -954,22 +961,25 @@ export class TransactionController extends BaseController< } = estimatedTransaction; const gasPrice = typeof providedGasPrice === 'undefined' - ? await query(this.ethQuery, 'gasPrice') + ? await query(this.ethQuery, 'gasPrice') : providedGasPrice; const { isCustomNetwork } = this.getNetworkState(); // 1. If gas is already defined on the transaction, use it if (typeof gas !== 'undefined') { return { gas, gasPrice }; } - const { gasLimit } = await query(this.ethQuery, 'getBlockByNumber', [ - 'latest', - false, - ]); + const { gasLimit } = await query<{ gasLimit: string }>( + this.ethQuery, + 'getBlockByNumber', + ['latest', false], + ); // 2. If to is not defined or this is not a contract address, and there is no data use 0x5208 / 21000. // If the newtwork is a custom network then bypass this check and fetch 'estimateGas'. /* istanbul ignore next */ - const code = to ? await query(this.ethQuery, 'getCode', [to]) : undefined; + const code = to + ? await query(this.ethQuery, 'getCode', [to]) + : undefined; /* istanbul ignore next */ if ( !isCustomNetwork && @@ -988,7 +998,7 @@ export class TransactionController extends BaseController< typeof value === 'undefined' ? '0x0' : /* istanbul ignore next */ value; const gasLimitBN = hexToBN(gasLimit); estimatedTransaction.gas = BNToHex(fractionBN(gasLimitBN, 19, 20)); - const gasHex = await query(this.ethQuery, 'estimateGas', [ + const gasHex = await query(this.ethQuery, 'estimateGas', [ estimatedTransaction, ]); @@ -1170,7 +1180,7 @@ export class TransactionController extends BaseController< tx.transaction.to && (!tx.transaction.data || tx.transaction.data !== '0x') ) { - const code = await query(this.ethQuery, 'getCode', [ + const code = await query(this.ethQuery, 'getCode', [ tx.transaction.to, ]); tx.toSmartContract = isSmartContractCode(code); @@ -1255,9 +1265,10 @@ export class TransactionController extends BaseController< const { status, transactionHash } = meta; switch (status) { case TransactionStatus.confirmed: - const txReceipt = await query(this.ethQuery, 'getTransactionReceipt', [ - transactionHash, - ]); + const txReceipt = await query<{ + gasUsed: string; + status: string; + } | null>(this.ethQuery, 'getTransactionReceipt', [transactionHash]); if (!txReceipt) { return [meta, false]; @@ -1278,9 +1289,11 @@ export class TransactionController extends BaseController< return [meta, true]; case TransactionStatus.submitted: - const txObj = await query(this.ethQuery, 'getTransactionByHash', [ - transactionHash, - ]); + const txObj = await query<{ blockNumber: string } | null>( + this.ethQuery, + 'getTransactionByHash', + [transactionHash], + ); if (!txObj) { const receiptShowsFailedStatus = await this.checkTxReceiptStatusIsFailed( @@ -1322,9 +1335,11 @@ export class TransactionController extends BaseController< private async checkTxReceiptStatusIsFailed( txHash: string | undefined, ): Promise { - const txReceipt = await query(this.ethQuery, 'getTransactionReceipt', [ - txHash, - ]); + const txReceipt = await query<{ status: string } | null>( + this.ethQuery, + 'getTransactionReceipt', + [txHash], + ); if (!txReceipt) { // Transaction is pending return false; diff --git a/src/util.test.ts b/src/util.test.ts index c257ca61403..3df616cefa5 100644 --- a/src/util.test.ts +++ b/src/util.test.ts @@ -1,6 +1,7 @@ import 'isomorphic-fetch'; import { BN } from 'ethereumjs-util'; import nock from 'nock'; +import { buildFakeEthQuery } from '../tests/util'; import * as util from './util'; import { Transaction, @@ -31,8 +32,18 @@ describe('util', () => { nock.cleanAll(); }); - it('bNToHex', () => { - expect(util.BNToHex(new BN('1337'))).toBe('0x539'); + describe('BNToHex', () => { + it('returns a 0x-prefixed hex string if given a decimal string', () => { + expect(util.BNToHex(new BN('1337'))).toBe('0x539'); + }); + + it('prefixes the given string with "0x" if it is not prefixed', () => { + expect(util.BNToHex('1337')).toBe('0x1337'); + }); + + it('does nothing to a 0x-prefixed hex string', () => { + expect(util.BNToHex('0x1337')).toBe('0x1337'); + }); }); it('fractionBN', () => { @@ -940,18 +951,18 @@ describe('util', () => { describe('query', () => { describe('when the given method exists directly on the EthQuery', () => { it('should call the method on the EthQuery and, if it is successful, return a promise that resolves to the result', async () => { - const ethQuery = { + const ethQuery = buildFakeEthQuery({ getBlockByHash: (blockId: any, cb: any) => cb(null, { id: blockId }), - }; + }); const result = await util.query(ethQuery, 'getBlockByHash', ['0x1234']); expect(result).toStrictEqual({ id: '0x1234' }); }); it('should call the method on the EthQuery and, if it errors, return a promise that is rejected with the error', async () => { - const ethQuery = { + const ethQuery = buildFakeEthQuery({ getBlockByHash: (_blockId: any, cb: any) => cb(new Error('uh oh'), null), - }; + }); await expect( util.query(ethQuery, 'getBlockByHash', ['0x1234']), ).rejects.toThrow('uh oh'); @@ -960,14 +971,14 @@ describe('util', () => { describe('when the given method does not exist directly on the EthQuery', () => { it('should use sendAsync to call the RPC endpoint and, if it is successful, return a promise that resolves to the result', async () => { - const ethQuery = { + const ethQuery = buildFakeEthQuery({ sendAsync: ({ method, params }: any, cb: any) => { if (method === 'eth_getBlockByHash') { return cb(null, { id: params[0] }); } throw new Error(`Unsupported method ${method}`); }, - }; + }); const result = await util.query(ethQuery, 'eth_getBlockByHash', [ '0x1234', ]); @@ -975,11 +986,11 @@ describe('util', () => { }); it('should use sendAsync to call the RPC endpoint and, if it errors, return a promise that is rejected with the error', async () => { - const ethQuery = { + const ethQuery = buildFakeEthQuery({ sendAsync: (_args: any, cb: any) => { cb(new Error('uh oh'), null); }, - }; + }); await expect( util.query(ethQuery, 'eth_getBlockByHash', ['0x1234']), ).rejects.toThrow('uh oh'); diff --git a/src/util.ts b/src/util.ts index 437ed2cc0c4..eb5dd0cdc7f 100644 --- a/src/util.ts +++ b/src/util.ts @@ -11,6 +11,7 @@ import { fromWei, toWei } from 'ethjs-unit'; import { ethErrors } from 'eth-rpc-errors'; import ensNamehash from 'eth-ens-namehash'; import { TYPED_MESSAGE_SCHEMA, typedSignatureHash } from 'eth-sig-util'; +import { EthQuerySendAsyncFunction } from 'eth-query'; import { validate } from 'jsonschema'; import { CID } from 'multiformats/cid'; import deepEqual from 'fast-deep-equal'; @@ -27,6 +28,10 @@ import { Token } from './assets/TokenRatesController'; import { MAINNET } from './constants'; import { Json } from './BaseControllerV2'; +export type EthQueryish = { + sendAsync: EthQuerySendAsyncFunction; +}; + const hexRe = /^[0-9A-Fa-f]+$/gu; const NORMALIZERS: { [param in keyof Transaction]: any } = { @@ -45,12 +50,12 @@ const NORMALIZERS: { [param in keyof Transaction]: any } = { }; /** - * Converts a BN object to a hex string with a '0x' prefix. + * Converts a BN object or a string to a hex string with a '0x' prefix. * - * @param inputBn - BN instance to convert to a hex string. + * @param inputBn - BN instance or string to convert to a hex string. * @returns A '0x'-prefixed hex string. */ -export function BNToHex(inputBn: any) { +export function BNToHex(inputBn: BN | string) { return addHexPrefix(inputBn.toString(16)); } @@ -116,7 +121,7 @@ export function gweiDecToWEIBN(n: number | string) { */ export function weiHexToGweiDec(hex: string) { const hexWei = new BN(stripHexPrefix(hex), 16); - return fromWei(hexWei, 'gwei').toString(10); + return fromWei(hexWei, 'gwei'); } /** @@ -709,18 +714,20 @@ export function normalizeEnsName(ensName: string): string | null { /** * Wrapper method to handle EthQuery requests. * - * @param ethQuery - EthQuery object initialized with a provider. - * @param method - Method to request. + * @param ethQuery - EthQuery object initialized with a provider, or an object + * that has a `sendAsync` method that can be used to make a JSON-RPC request to + * a provider. + * @param methodName - Method to request. * @param args - Arguments to send. * @returns Promise resolving the request. */ -export function query( - ethQuery: any, - method: string, +export function query( + ethQuery: EthQueryish, + methodName: string, args: any[] = [], -): Promise { +): Promise { return new Promise((resolve, reject) => { - const cb = (error: Error, result: any) => { + const cb = (error: Error, result: R) => { if (error) { reject(error); return; @@ -728,10 +735,18 @@ export function query( resolve(result); }; - if (typeof ethQuery[method] === 'function') { - ethQuery[method](...args, cb); + if (methodName in ethQuery) { + // Due to the generic nature of this function, there isn't a way to + // clarify the type on `ethQuery`. At this point we know that `ethQuery` + // has a method with the name of `${methodName}` that takes some arguments + // and a callback, but we don't know what `methodName` is, so we don't + // know what kind of arguments it takes or the return type of the result. + (ethQuery as any)[methodName](...args, cb); } else { - ethQuery.sendAsync({ method, params: args }, cb); + ethQuery.sendAsync( + { id: 1, jsonrpc: '2.0', method: methodName, params: args }, + cb, + ); } }); } diff --git a/tests/util.ts b/tests/util.ts new file mode 100644 index 00000000000..315d63a59f4 --- /dev/null +++ b/tests/util.ts @@ -0,0 +1,30 @@ +import { JsonRpcRequest } from 'json-rpc-engine'; +import { EthQueryMethodCallback, EthQuerySendAsyncFunction } from 'eth-query'; +import { EthQueryish } from '../src/util'; + +/** + * Builds a EthQuery object that implements the bare minimum necessary to pass + * to `query`. + * + * @param overrides - An optional set of methods to add to the fake EthQuery + * object. + * @returns The fake EthQuery object. + */ +export function buildFakeEthQuery( + overrides: Record void> = {}, +): EthQueryish { + const sendAsync: EthQuerySendAsyncFunction< + Record, + string + > = ( + _request: JsonRpcRequest>, + callback: EthQueryMethodCallback, + ) => { + callback(null, 'default result'); + }; + + return { + sendAsync, + ...overrides, + }; +} diff --git a/types/@metamask/contract-metadata.d.ts b/types/@metamask/contract-metadata.d.ts index dfbc40c8acb..5c6c486dcf7 100644 --- a/types/@metamask/contract-metadata.d.ts +++ b/types/@metamask/contract-metadata.d.ts @@ -1 +1,12 @@ -declare module '@metamask/contract-metadata'; +declare module '@metamask/contract-metadata' { + export type Token = { + name: string; + logo: string; + erc20: boolean; + erc721?: boolean; + symbol: string; + decimals: number; + }; + const contractMap: Record; + export default contractMap; +} diff --git a/types/@metamask/metamask-eth-abis.d.ts b/types/@metamask/metamask-eth-abis.d.ts index d26af311c1b..f4c230cfc00 100644 --- a/types/@metamask/metamask-eth-abis.d.ts +++ b/types/@metamask/metamask-eth-abis.d.ts @@ -1 +1,5 @@ -declare module '@metamask/metamask-eth-abis'; +import { abiERC20, abiERC721, abiERC1155 } from '@metamask/metamask-eth-abis'; + +declare module '@metamask/metamask-eth-abis' { + export type ABI = typeof abiERC20 | typeof abiERC721 | typeof abiERC1155; +} diff --git a/types/eth-ens-namehash.d.ts b/types/eth-ens-namehash.d.ts index 1f686f57aea..9066e16a8a8 100644 --- a/types/eth-ens-namehash.d.ts +++ b/types/eth-ens-namehash.d.ts @@ -1 +1,4 @@ -declare module 'eth-ens-namehash'; +declare module 'eth-ens-namehash' { + export function hash(inputName?: string | null): `0x${string}`; + export function normalize(name?: string | null): string; +} diff --git a/types/eth-json-rpc-infura.d.ts b/types/eth-json-rpc-infura.d.ts new file mode 100644 index 00000000000..3807e7eb5a8 --- /dev/null +++ b/types/eth-json-rpc-infura.d.ts @@ -0,0 +1,41 @@ +declare module 'eth-json-rpc-infura' { + import type { JsonRpcMiddleware } from 'json-rpc-engine'; + + // Source: + export type SupportedInfuraNetwork = + | 'mainnet' + | 'ropsten' + | 'rinkeby' + | 'kovan' + | 'goerli' + | 'eth2-beacon-mainnet' + | 'ipfs' + | 'filecoin' + | 'polygon-mainnet' + | 'polygon-mumbai' + | 'palm-mainnet' + | 'palm-testnet' + | 'optimism-mainnet' + | 'optimism-kovan' + | 'arbitrum-mainnet' + | 'arbitrum-rinkeby' + | 'near-mainnet' + | 'near-testnet' + | 'aurora-mainnet' + | 'aurora-testnet' + // Legacy networks for compatibility with NetworkController + | 'optimism' + | 'optimismTest'; + + export type CreateInfuraMiddlewareOptions = { + network?: SupportedInfuraNetwork; + maxAttempts?: number; + source?: string; + projectId: string; + headers?: Record; + }; + + export default function createInfuraMiddleware( + opts: CreateInfuraMiddlewareOptions, + ): JsonRpcMiddleware; +} diff --git a/types/eth-json-rpc-infura/src/createProvider.d.ts b/types/eth-json-rpc-infura/src/createProvider.d.ts index 53b9352d5ce..9a011aeb5c2 100644 --- a/types/eth-json-rpc-infura/src/createProvider.d.ts +++ b/types/eth-json-rpc-infura/src/createProvider.d.ts @@ -1 +1,14 @@ -declare module 'eth-json-rpc-infura/src/createProvider'; +declare module 'eth-json-rpc-infura/src/createProvider' { + import type { JsonRpcEngine } from 'json-rpc-engine'; + import type SafeEventEmitter from '@metamask/safe-event-emitter'; + import type { CreateInfuraMiddlewareOptions } from 'eth-json-rpc-infura'; + + interface Provider extends SafeEventEmitter { + sendAsync: JsonRpcEngine['handle']; + send: JsonRpcEngine['handle']; + } + + export default function createProvider( + opts: CreateInfuraMiddlewareOptions, + ): Provider; +} diff --git a/types/eth-phishing-detect/src/config.json.d.ts b/types/eth-phishing-detect/src/config.json.d.ts index 6943346451f..294536259c3 100644 --- a/types/eth-phishing-detect/src/config.json.d.ts +++ b/types/eth-phishing-detect/src/config.json.d.ts @@ -1 +1,13 @@ -declare module 'eth-phishing-detect/src/config.json'; +declare module 'eth-phishing-detect/src/config.json' { + export type EthPhishingDetectorConfiguration = { + version: number; + tolerance: number; + fuzzylist: string[]; + whitelist: string[]; + blacklist: string[]; + }; + + const config: EthPhishingDetectorConfiguration; + + export default config; +} diff --git a/types/eth-phishing-detect/src/detector.d.ts b/types/eth-phishing-detect/src/detector.d.ts index cab272fdde9..3deae0696a6 100644 --- a/types/eth-phishing-detect/src/detector.d.ts +++ b/types/eth-phishing-detect/src/detector.d.ts @@ -1 +1,18 @@ -declare module 'eth-phishing-detect/src/detector'; +declare module 'eth-phishing-detect/src/detector' { + type Check = + | { type: 'whitelist'; result: false } + | { type: 'blacklist'; result: true } + | { type: 'fuzzy'; result: true; match: string } + | { type: 'all'; result: true }; + + export default class PhishingDetector { + constructor(opts: { + whitelist?: string[]; + blacklist?: string[]; + fuzzylist?: string[]; + tolerance?: number; + }); + + check(domain: string): Check; + } +} diff --git a/types/eth-query.d.ts b/types/eth-query.d.ts index e857105f282..17f78110bd5 100644 --- a/types/eth-query.d.ts +++ b/types/eth-query.d.ts @@ -1 +1,145 @@ -declare module 'eth-query'; +declare module 'eth-query' { + import type { JsonRpcRequest } from 'json-rpc-engine'; + + export type EthQueryMethodCallback = (error: any, response: R) => void; + + export type EthProvider = { + sendAsync( + request: JsonRpcRequest

, + callback: EthQueryMethodCallback, + ): void; + }; + + type HexString = `0x${string}`; + + type BlockParam = HexString | 'latest' | 'earliest' | 'pending'; + + export type EthQuerySendAsyncFunction

= ( + request: JsonRpcRequest

, + callback: EthQueryMethodCallback, + ) => void; + + export default class EthQuery { + currentProvider: EthProvider; + + constructor(provider: EthProvider); + + // Methods that take arguments + + getBalance( + address: HexString, + callback: EthQueryMethodCallback, + ): void; + getBalance( + address: HexString, + block: BlockParam, + callback: EthQueryMethodCallback, + ): void; + + getCode(address: HexString, callback: EthQueryMethodCallback): void; + getCode( + address: HexString, + block: BlockParam, + callback: EthQueryMethodCallback, + ): void; + + getTransactionCount( + address: HexString, + callback: EthQueryMethodCallback, + ): void; + getTransactionCount( + address: HexString, + block: BlockParam, + callback: EthQueryMethodCallback, + ): void; + + getStorageAt( + address: HexString, + storagePosition: HexString, + callback: EthQueryMethodCallback, + ): void; + getStorageAt( + address: HexString, + storagePosition: HexString, + block: BlockParam, + callback: EthQueryMethodCallback, + ): void; + + call( + data: { + from: HexString; + to: HexString; + gas?: HexString; + gasPrice?: HexString; + value?: HexString; + data?: HexString; + }, + callback: EthQueryMethodCallback, + ): void; + call( + data: { + from: HexString; + to: HexString; + gas?: HexString; + gasPrice?: HexString; + value?: HexString; + data?: HexString; + }, + block: BlockParam, + callback: EthQueryMethodCallback, + ): void; + + // Methods that don't take arguments + + protocolVersion(callback: EthQueryMethodCallback): void; + syncing(callback: EthQueryMethodCallback): void; + coinbase(callback: EthQueryMethodCallback): void; + mining(callback: EthQueryMethodCallback): void; + hashrate(callback: EthQueryMethodCallback): void; + gasPrice(callback: EthQueryMethodCallback): void; + accounts(callback: EthQueryMethodCallback): void; + blockNumber(callback: EthQueryMethodCallback): void; + getBlockTransactionCountByHash( + callback: EthQueryMethodCallback, + ): void; + getBlockTransactionCountByNumber( + callback: EthQueryMethodCallback, + ): void; + getUncleCountByBlockHash(callback: EthQueryMethodCallback): void; + getUncleCountByBlockNumber(callback: EthQueryMethodCallback): void; + sign(callback: EthQueryMethodCallback): void; + sendTransaction(callback: EthQueryMethodCallback): void; + sendRawTransaction(callback: EthQueryMethodCallback): void; + estimateGas(callback: EthQueryMethodCallback): void; + getBlockByHash(callback: EthQueryMethodCallback): void; + getBlockByNumber(callback: EthQueryMethodCallback): void; + getTransactionByHash(callback: EthQueryMethodCallback): void; + getTransactionByBlockHashAndIndex( + callback: EthQueryMethodCallback, + ): void; + getTransactionByBlockNumberAndIndex( + callback: EthQueryMethodCallback, + ): void; + getTransactionReceipt(callback: EthQueryMethodCallback): void; + getUncleByBlockHashAndIndex(callback: EthQueryMethodCallback): void; + getUncleByBlockNumberAndIndex(callback: EthQueryMethodCallback): void; + getCompilers(callback: EthQueryMethodCallback): void; + compileLLL(callback: EthQueryMethodCallback): void; + compileSolidity(callback: EthQueryMethodCallback): void; + compileSerpent(callback: EthQueryMethodCallback): void; + newFilter(callback: EthQueryMethodCallback): void; + newBlockFilter(callback: EthQueryMethodCallback): void; + newPendingTransactionFilter(callback: EthQueryMethodCallback): void; + uninstallFilter(callback: EthQueryMethodCallback): void; + getFilterChanges(callback: EthQueryMethodCallback): void; + getFilterLogs(callback: EthQueryMethodCallback): void; + getLogs(callback: EthQueryMethodCallback): void; + getWork(callback: EthQueryMethodCallback): void; + submitWork(callback: EthQueryMethodCallback): void; + submitHashrate(callback: EthQueryMethodCallback): void; + + // Custom methods + + sendAsync: EthQuerySendAsyncFunction; + } +} diff --git a/types/ethjs-provider-http.d.ts b/types/ethjs-provider-http.d.ts index 6bee27c95f9..f379da96365 100644 --- a/types/ethjs-provider-http.d.ts +++ b/types/ethjs-provider-http.d.ts @@ -1 +1,16 @@ -declare module 'ethjs-provider-http'; +declare module 'ethjs-provider-http' { + import type { JsonRpcRequest } from 'json-rpc-engine'; + export type EthQueryMethodCallback = (error: any, response: R) => void; + + export default class HttpProvider { + host: string; + timeout: number; + + constructor(host: string, timeout?: number); + + sendAsync( + request: JsonRpcRequest

, + callback: EthQueryMethodCallback, + ): void; + } +} diff --git a/types/ethjs-unit.d.ts b/types/ethjs-unit.d.ts index 9030a9770f7..5d9dc8cbf3d 100644 --- a/types/ethjs-unit.d.ts +++ b/types/ethjs-unit.d.ts @@ -1 +1,64 @@ -declare module 'ethjs-unit'; +declare module 'ethjs-unit' { + import type BN from 'bn.js'; + + // This type is derived from the logic within `number-to-bn` and represents an + // object obtained via `bn.js` or `bignumber.js`. + type BigNumberish = ({ mul: any } | { dividedToIntegerBy: any }) & { + toString: (base: number) => string; + }; + + type AcceptableBNInput = string | number | BigNumberish; + + // This should be `keyof typeof unitMap` but really accepts anything + type Unitish = string | null | undefined; + + type Numberish = + | string + | number + | (({ toTwos: any } | { dividedToIntegerBy: any }) & { + toPrecision?: () => string; + toString: (base: number) => string | number; + }); + + export const unitMap: { + noether: '0'; + wei: '1'; + kwei: '1000'; + Kwei: '1000'; + babbage: '1000'; + femtoether: '1000'; + mwei: '1000000'; + Mwei: '1000000'; + lovelace: '1000000'; + picoether: '1000000'; + gwei: '1000000000'; + Gwei: '1000000000'; + shannon: '1000000000'; + nanoether: '1000000000'; + nano: '1000000000'; + szabo: '1000000000000'; + microether: '1000000000000'; + micro: '1000000000000'; + finney: '1000000000000000'; + milliether: '1000000000000000'; + milli: '1000000000000000'; + ether: '1000000000000000000'; + kether: '1000000000000000000000'; + grand: '1000000000000000000000'; + mether: '1000000000000000000000000'; + gether: '1000000000000000000000000000'; + tether: '1000000000000000000000000000000'; + }; + + export function numberToString(arg: Numberish): string; + + export function getValueOfUnit(unitInput: Unitish): BN; + + export function fromWei( + weiInput: AcceptableBNInput, + unit: Unitish, + optionsInput?: { pad?: boolean; commify?: boolean }, + ): string; + + export function toWei(etherInput: Numberish, unit: Unitish): BN; +}