diff --git a/packages/assets-controllers/CHANGELOG.md b/packages/assets-controllers/CHANGELOG.md index 4a77d29c5ac..21ce9683509 100644 --- a/packages/assets-controllers/CHANGELOG.md +++ b/packages/assets-controllers/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Changed +- Update TokenListController to fetch prefiltered set of tokens from the API, reducing response data and removing the need for filtering logic ([#2054](https://github.com/MetaMask/core/pull/2054)) ## [20.0.0] ### Added diff --git a/packages/assets-controllers/jest.config.js b/packages/assets-controllers/jest.config.js index 91018b6767f..13bec0c96f3 100644 --- a/packages/assets-controllers/jest.config.js +++ b/packages/assets-controllers/jest.config.js @@ -18,7 +18,7 @@ module.exports = merge(baseConfig, { coverageThreshold: { global: { branches: 88.2, - functions: 96, + functions: 95.95, lines: 96.25, statements: 96.5, }, diff --git a/packages/assets-controllers/src/TokenDetectionController.test.ts b/packages/assets-controllers/src/TokenDetectionController.test.ts index 6eba6e1c9d3..87f0680873c 100644 --- a/packages/assets-controllers/src/TokenDetectionController.test.ts +++ b/packages/assets-controllers/src/TokenDetectionController.test.ts @@ -13,6 +13,7 @@ import type { ProviderConfig, } from '@metamask/network-controller'; import { PreferencesController } from '@metamask/preferences-controller'; +import type { Hex } from '@metamask/utils'; import { BN } from 'ethereumjs-util'; import nock from 'nock'; import * as sinon from 'sinon'; @@ -149,7 +150,7 @@ describe('TokenDetectionController', () => { beforeEach(async () => { nock(TOKEN_END_POINT_API) - .get(`/tokens/${convertHexToDecimal(ChainId.mainnet)}`) + .get(getTokensPath(ChainId.mainnet)) .reply(200, sampleTokenList) .get( `/token/${convertHexToDecimal(ChainId.mainnet)}?address=${ @@ -437,7 +438,7 @@ describe('TokenDetectionController', () => { it('should not call getBalancesInSingleCall after stopping polling, and then switching between networks that support token detection', async () => { const polygonDecimalChainId = '137'; nock(TOKEN_END_POINT_API) - .get(`/tokens/${polygonDecimalChainId}`) + .get(getTokensPath(toHex(polygonDecimalChainId))) .reply(200, sampleTokenList); const stub = sinon.stub(); @@ -657,3 +658,15 @@ describe('TokenDetectionController', () => { }); }); }); + +/** + * Construct the path used to fetch tokens that we can pass to `nock`. + * + * @param chainId - The chain ID. + * @returns The constructed path. + */ +function getTokensPath(chainId: Hex) { + return `/tokens/${convertHexToDecimal( + chainId, + )}?occurrenceFloor=3&includeNativeAssets=false&includeDuplicateSymbolAssets=false&includeTokenFees=false&includeAssetType=false`; +} diff --git a/packages/assets-controllers/src/TokenListController.test.ts b/packages/assets-controllers/src/TokenListController.test.ts index 8cfb70add48..8b75473455e 100644 --- a/packages/assets-controllers/src/TokenListController.test.ts +++ b/packages/assets-controllers/src/TokenListController.test.ts @@ -13,6 +13,7 @@ import type { ProviderConfig, } from '@metamask/network-controller'; import { NetworkStatus } from '@metamask/network-controller'; +import type { Hex } from '@metamask/utils'; import nock from 'nock'; import * as sinon from 'sinon'; @@ -105,91 +106,6 @@ const sampleMainnetTokensChainsCache = sampleMainnetTokenList.reduce( {} as TokenListMap, ); -const sampleWithDuplicateSymbols = [ - { - address: '0x1f573d6fb3f13d689ff844b4ce37794d79a7ff1c', - symbol: 'BNT', - decimals: 18, - occurrences: 11, - name: 'Bancor', - iconUrl: - 'https://static.metafi.codefi.network/api/v1/tokenIcons/1/0x1f573d6fb3f13d689ff844b4ce37794d79a7ff1c.png', - aggregators: [ - 'Bancor', - 'CMC', - 'CoinGecko', - '1inch', - 'Paraswap', - 'PMM', - 'Zapper', - 'Zerion', - '0x', - ], - }, -]; - -const sampleWithDuplicateSymbolsTokensChainsCache = - sampleWithDuplicateSymbols.reduce((output, current) => { - output[current.address] = current; - return output; - }, {} as TokenListMap); - -const sampleWithLessThan3OccurencesResponse = [ - { - address: '0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f', - symbol: 'SNX', - decimals: 18, - occurrences: 2, - name: 'Synthetix', - iconUrl: - 'https://static.metafi.codefi.network/api/v1/tokenIcons/1/0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f.png', - aggregators: [ - 'Aave', - 'Bancor', - 'CMC', - 'Crypto.com', - 'CoinGecko', - '1inch', - 'Paraswap', - 'PMM', - 'Synthetix', - 'Zapper', - 'Zerion', - '0x', - ], - }, - { - address: '0x514910771af9ca656af840dff83e8264ecf986ca', - symbol: 'LINK', - decimals: 18, - occurrences: 11, - name: 'Chainlink', - iconUrl: - 'https://static.metafi.codefi.network/api/v1/tokenIcons/1/0x514910771af9ca656af840dff83e8264ecf986ca.png', - aggregators: [ - 'Aave', - 'Bancor', - 'CMC', - 'Crypto.com', - 'CoinGecko', - '1inch', - 'Paraswap', - 'PMM', - 'Zapper', - 'Zerion', - '0x', - ], - }, -]; - -const sampleWith3OrMoreOccurrences = - sampleWithLessThan3OccurencesResponse.reduce((output, token) => { - if (token.occurrences >= 3) { - output[token.address] = token; - } - return output; - }, {} as TokenListMap); - const sampleBinanceTokenList = [ { address: '0x7083609fce4d1d8dc0c979aab8c869ea2c873402', @@ -734,7 +650,7 @@ describe('TokenListController', () => { it('should update tokenList state when network updates are passed via onNetworkStateChange callback', async () => { nock(tokenService.TOKEN_END_POINT_API) - .get(`/tokens/${convertHexToDecimal(ChainId.mainnet)}`) + .get(getTokensPath(ChainId.mainnet)) .reply(200, sampleMainnetTokenList) .persist(); @@ -897,7 +813,7 @@ describe('TokenListController', () => { it('should update token list from api', async () => { nock(tokenService.TOKEN_END_POINT_API) - .get(`/tokens/${convertHexToDecimal(ChainId.mainnet)}`) + .get(getTokensPath(ChainId.mainnet)) .reply(200, sampleMainnetTokenList) .persist(); @@ -935,12 +851,12 @@ describe('TokenListController', () => { it('should update the cache before threshold time if the current data is undefined', async () => { nock(tokenService.TOKEN_END_POINT_API) - .get(`/tokens/${convertHexToDecimal(ChainId.mainnet)}`) + .get(getTokensPath(ChainId.mainnet)) .once() .reply(200, undefined); nock(tokenService.TOKEN_END_POINT_API) - .get(`/tokens/${convertHexToDecimal(ChainId.mainnet)}`) + .get(getTokensPath(ChainId.mainnet)) .reply(200, sampleMainnetTokenList) .persist(); @@ -988,76 +904,9 @@ describe('TokenListController', () => { controller.destroy(); }); - it('should update token list after removing data with duplicate symbols', async () => { - nock(tokenService.TOKEN_END_POINT_API) - .get(`/tokens/${convertHexToDecimal(ChainId.mainnet)}`) - .reply(200, sampleWithDuplicateSymbols) - .persist(); - - const controllerMessenger = getControllerMessenger(); - const messenger = getRestrictedMessenger(controllerMessenger); - const controller = new TokenListController({ - chainId: ChainId.mainnet, - preventPollingOnNetworkRestart: false, - messenger, - }); - await controller.start(); - expect(controller.state.tokenList).toStrictEqual({ - '0x1f573d6fb3f13d689ff844b4ce37794d79a7ff1c': { - address: '0x1f573d6fb3f13d689ff844b4ce37794d79a7ff1c', - symbol: 'BNT', - decimals: 18, - occurrences: 11, - name: 'Bancor', - iconUrl: - 'https://static.metafi.codefi.network/api/v1/tokenIcons/1/0x1f573d6fb3f13d689ff844b4ce37794d79a7ff1c.png', - aggregators: [ - 'Bancor', - 'CMC', - 'CoinGecko', - '1inch', - 'Paraswap', - 'PMM', - 'Zapper', - 'Zerion', - '0x', - ], - }, - }); - - expect( - controller.state.tokensChainsCache[ChainId.mainnet].data, - ).toStrictEqual(sampleWithDuplicateSymbolsTokensChainsCache); - controller.destroy(); - }); - - it('should update token list after removing data less than 3 occurrences', async () => { - nock(tokenService.TOKEN_END_POINT_API) - .get(`/tokens/${convertHexToDecimal(ChainId.mainnet)}`) - .reply(200, sampleWithLessThan3OccurencesResponse) - .persist(); - - const controllerMessenger = getControllerMessenger(); - const messenger = getRestrictedMessenger(controllerMessenger); - const controller = new TokenListController({ - chainId: ChainId.mainnet, - preventPollingOnNetworkRestart: false, - messenger, - }); - await controller.start(); - expect(controller.state.tokenList).toStrictEqual( - sampleWith3OrMoreOccurrences, - ); - - expect( - controller.state.tokensChainsCache[ChainId.mainnet].data, - ).toStrictEqual(sampleWith3OrMoreOccurrences); - controller.destroy(); - }); - it('should update token list when the token property changes', async () => { nock(tokenService.TOKEN_END_POINT_API) - .get(`/tokens/${convertHexToDecimal(ChainId.mainnet)}`) + .get(getTokensPath(ChainId.mainnet)) .reply(200, sampleMainnetTokenList) .persist(); @@ -1085,7 +934,7 @@ describe('TokenListController', () => { it('should update the cache when the timestamp expires', async () => { nock(tokenService.TOKEN_END_POINT_API) - .get(`/tokens/${convertHexToDecimal(ChainId.mainnet)}`) + .get(getTokensPath(ChainId.mainnet)) .reply(200, sampleMainnetTokenList) .persist(); @@ -1115,11 +964,11 @@ describe('TokenListController', () => { it('should update token list when the chainId change', async () => { nock(tokenService.TOKEN_END_POINT_API) - .get(`/tokens/${convertHexToDecimal(ChainId.mainnet)}`) + .get(getTokensPath(ChainId.mainnet)) .reply(200, sampleMainnetTokenList) - .get(`/tokens/${convertHexToDecimal(ChainId.goerli)}`) + .get(getTokensPath(ChainId.goerli)) .reply(200, { error: 'ChainId 5 is not supported' }) - .get(`/tokens/56`) + .get(getTokensPath(toHex(56))) .reply(200, sampleBinanceTokenList) .persist(); @@ -1212,11 +1061,11 @@ describe('TokenListController', () => { it('should update preventPollingOnNetworkRestart and restart the polling on network restart', async () => { nock(tokenService.TOKEN_END_POINT_API) - .get(`/tokens/${convertHexToDecimal(ChainId.mainnet)}`) + .get(getTokensPath(ChainId.mainnet)) .reply(200, sampleMainnetTokenList) - .get(`/tokens/${convertHexToDecimal(ChainId.goerli)}`) + .get(getTokensPath(ChainId.goerli)) .reply(200, { error: 'ChainId 5 is not supported' }) - .get(`/tokens/56`) + .get(getTokensPath(toHex(56))) .reply(200, sampleBinanceTokenList) .persist(); @@ -1301,7 +1150,7 @@ describe('TokenListController', () => { it('should call fetchTokenListByChainId with the correct chainId', async () => { nock(tokenService.TOKEN_END_POINT_API) - .get(`/tokens/${convertHexToDecimal(ChainId.sepolia)}`) + .get(getTokensPath(ChainId.sepolia)) .reply(200, sampleSepoliaTokenList) .persist(); @@ -1482,3 +1331,15 @@ describe('TokenListController', () => { }); }); }); + +/** + * Construct the path used to fetch tokens that we can pass to `nock`. + * + * @param chainId - The chain ID. + * @returns The constructed path. + */ +function getTokensPath(chainId: Hex) { + return `/tokens/${convertHexToDecimal( + chainId, + )}?occurrenceFloor=3&includeNativeAssets=false&includeDuplicateSymbolAssets=false&includeTokenFees=false&includeAssetType=false`; +} diff --git a/packages/assets-controllers/src/TokenListController.ts b/packages/assets-controllers/src/TokenListController.ts index 7c613f03e5f..6cf489d965e 100644 --- a/packages/assets-controllers/src/TokenListController.ts +++ b/packages/assets-controllers/src/TokenListController.ts @@ -293,26 +293,7 @@ export class TokenListController extends PollingController< }); return; } - // Filtering out tokens with less than 3 occurrences and native tokens - const filteredTokenList = tokensFromAPI.filter( - (token) => - token.occurrences && - token.occurrences >= 3 && - token.address !== '0x0000000000000000000000000000000000000000', - ); - // Removing the tokens with symbol conflicts - const symbolsList = filteredTokenList.map((token) => token.symbol); - const duplicateSymbols = [ - ...new Set( - symbolsList.filter( - (symbol, index) => symbolsList.indexOf(symbol) !== index, - ), - ), - ]; - const uniqueTokenList = filteredTokenList.filter( - (token) => !duplicateSymbols.includes(token.symbol), - ); - for (const token of uniqueTokenList) { + for (const token of tokensFromAPI) { const formattedToken: TokenListToken = { ...token, aggregators: formatAggregatorNames(token.aggregators), diff --git a/packages/assets-controllers/src/token-service.test.ts b/packages/assets-controllers/src/token-service.test.ts index 0a88265ae78..023a34b18e5 100644 --- a/packages/assets-controllers/src/token-service.test.ts +++ b/packages/assets-controllers/src/token-service.test.ts @@ -141,7 +141,9 @@ describe('Token service', () => { it('should call the tokens api and return the list of tokens', async () => { const { signal } = new AbortController(); nock(TOKEN_END_POINT_API) - .get(`/tokens/${sampleDecimalChainId}`) + .get( + `/tokens/${sampleDecimalChainId}?occurrenceFloor=3&includeNativeAssets=false&includeDuplicateSymbolAssets=false&includeTokenFees=false&includeAssetType=false`, + ) .reply(200, sampleTokenList) .persist(); @@ -153,7 +155,9 @@ describe('Token service', () => { it('should return undefined if the fetch is aborted', async () => { const abortController = new AbortController(); nock(TOKEN_END_POINT_API) - .get(`/tokens/${sampleDecimalChainId}`) + .get( + `/tokens/${sampleDecimalChainId}?occurrenceFloor=3&includeNativeAssets=false&includeDuplicateSymbolAssets=false&includeTokenFees=false&includeAssetType=false`, + ) // well beyond time it will take to abort .delay(ONE_SECOND_IN_MILLISECONDS) .reply(200, sampleTokenList) @@ -171,7 +175,9 @@ describe('Token service', () => { it('should return undefined if the fetch fails with a network error', async () => { const { signal } = new AbortController(); nock(TOKEN_END_POINT_API) - .get(`/tokens/${sampleDecimalChainId}`) + .get( + `/tokens/${sampleDecimalChainId}?occurrenceFloor=3&includeNativeAssets=false&includeDuplicateSymbolAssets=false&includeTokenFees=false&includeAssetType=false`, + ) .replyWithError('Example network error') .persist(); @@ -183,7 +189,9 @@ describe('Token service', () => { it('should return undefined if the fetch fails with an unsuccessful status code', async () => { const { signal } = new AbortController(); nock(TOKEN_END_POINT_API) - .get(`/tokens/${sampleDecimalChainId}`) + .get( + `/tokens/${sampleDecimalChainId}?occurrenceFloor=3&includeNativeAssets=false&includeDuplicateSymbolAssets=false&includeTokenFees=false&includeAssetType=false`, + ) .reply(500) .persist(); @@ -195,7 +203,9 @@ describe('Token service', () => { it('should return undefined if the fetch fails with a timeout', async () => { const { signal } = new AbortController(); nock(TOKEN_END_POINT_API) - .get(`/tokens/${sampleDecimalChainId}`) + .get( + `/tokens/${sampleDecimalChainId}?occurrenceFloor=3&includeNativeAssets=false&includeDuplicateSymbolAssets=false&includeTokenFees=false&includeAssetType=false`, + ) // well beyond timeout .delay(ONE_SECOND_IN_MILLISECONDS) .reply(200, sampleTokenList) @@ -309,16 +319,4 @@ describe('Token service', () => { ).rejects.toThrow(TOKEN_METADATA_NO_SUPPORT_ERROR); }); }); - - it('should call the tokens api and return undefined', async () => { - const { signal } = new AbortController(); - nock(TOKEN_END_POINT_API) - .get(`/tokens/${sampleDecimalChainId}`) - .reply(404, undefined) - .persist(); - - const tokens = await fetchTokenListByChainId(sampleChainId, signal); - - expect(tokens).toBeUndefined(); - }); }); diff --git a/packages/assets-controllers/src/token-service.ts b/packages/assets-controllers/src/token-service.ts index e0654597416..c315af3d21d 100644 --- a/packages/assets-controllers/src/token-service.ts +++ b/packages/assets-controllers/src/token-service.ts @@ -14,7 +14,9 @@ export const TOKEN_METADATA_NO_SUPPORT_ERROR = * @returns The tokens URL. */ function getTokensURL(chainId: Hex) { - return `${TOKEN_END_POINT_API}/tokens/${convertHexToDecimal(chainId)}`; + return `${TOKEN_END_POINT_API}/tokens/${convertHexToDecimal( + chainId, + )}?occurrenceFloor=3&includeNativeAssets=false&includeDuplicateSymbolAssets=false&includeTokenFees=false&includeAssetType=false`; } /** diff --git a/packages/keyring-controller/package.json b/packages/keyring-controller/package.json index 05a3cd86b54..0492184c12e 100644 --- a/packages/keyring-controller/package.json +++ b/packages/keyring-controller/package.json @@ -46,7 +46,7 @@ "@ethereumjs/tx": "^4.2.0", "@keystonehq/bc-ur-registry-eth": "^0.9.0", "@metamask/auto-changelog": "^3.4.3", - "@metamask/eth-sig-util": "^7.0.0", + "@metamask/eth-sig-util": "^7.0.1", "@metamask/scure-bip39": "^2.1.1", "@types/jest": "^27.4.1", "deepmerge": "^4.2.2", diff --git a/packages/message-manager/package.json b/packages/message-manager/package.json index 2c1ee148307..35faae64284 100644 --- a/packages/message-manager/package.json +++ b/packages/message-manager/package.json @@ -32,7 +32,7 @@ "dependencies": { "@metamask/base-controller": "^4.0.0", "@metamask/controller-utils": "^6.1.0", - "@metamask/eth-sig-util": "^7.0.0", + "@metamask/eth-sig-util": "^7.0.1", "@metamask/utils": "^8.2.0", "@types/uuid": "^8.3.0", "ethereumjs-util": "^7.0.10", diff --git a/packages/transaction-controller/jest.config.js b/packages/transaction-controller/jest.config.js index 50d284eb7ef..7ee4a9bdfe5 100644 --- a/packages/transaction-controller/jest.config.js +++ b/packages/transaction-controller/jest.config.js @@ -17,10 +17,10 @@ module.exports = merge(baseConfig, { // An object that configures minimum threshold enforcement for coverage results coverageThreshold: { global: { - branches: 89.23, - functions: 94.23, - lines: 98.16, - statements: 98.12, + branches: 89.03, + functions: 94.16, + lines: 97.96, + statements: 97.93, }, }, diff --git a/packages/transaction-controller/src/utils/transaction-type.ts b/packages/transaction-controller/src/utils/transaction-type.ts index 29ffdec60c8..94a635fe237 100644 --- a/packages/transaction-controller/src/utils/transaction-type.ts +++ b/packages/transaction-controller/src/utils/transaction-type.ts @@ -27,20 +27,34 @@ export async function determineTransactionType( ethQuery: EthQuery, ): Promise { const { data, to } = txParams; - const name = parseStandardTokenTransactionData(data)?.name; if (data && !to) { return { type: TransactionType.deployContract, getCodeResponse: undefined }; } - const { contractCode: resultCode, isContractAddress } = + const { contractCode: getCodeResponse, isContractAddress } = await readAddressAsContract(ethQuery, to); if (!isContractAddress) { - return { type: TransactionType.simpleSend, getCodeResponse: resultCode }; + return { type: TransactionType.simpleSend, getCodeResponse }; + } + + const hasValue = Number(txParams.value ?? '0') !== 0; + + const contractInteractionResult = { + type: TransactionType.contractInteraction, + getCodeResponse, + }; + + if (!data || hasValue) { + return contractInteractionResult; } - const hasValue = txParams.value && Number(txParams.value) !== 0; + const name = parseStandardTokenTransactionData(data)?.name; + + if (!name) { + return contractInteractionResult; + } const tokenMethodName = [ TransactionType.tokenMethodApprove, @@ -48,16 +62,13 @@ export async function determineTransactionType( TransactionType.tokenMethodTransfer, TransactionType.tokenMethodTransferFrom, TransactionType.tokenMethodSafeTransferFrom, - ].find((methodName) => methodName.toLowerCase() === name?.toLowerCase()); + ].find((methodName) => methodName.toLowerCase() === name.toLowerCase()); - if (data && tokenMethodName && !hasValue) { - return { type: tokenMethodName, getCodeResponse: resultCode }; + if (tokenMethodName) { + return { type: tokenMethodName, getCodeResponse }; } - return { - type: TransactionType.contractInteraction, - getCodeResponse: resultCode, - }; + return contractInteractionResult; } /** diff --git a/yarn.lock b/yarn.lock index 2c6bd2f5035..87ab9da5ec3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1883,18 +1883,17 @@ __metadata: languageName: node linkType: hard -"@metamask/eth-sig-util@npm:^7.0.0": - version: 7.0.0 - resolution: "@metamask/eth-sig-util@npm:7.0.0" +"@metamask/eth-sig-util@npm:^7.0.0, @metamask/eth-sig-util@npm:^7.0.1": + version: 7.0.1 + resolution: "@metamask/eth-sig-util@npm:7.0.1" dependencies: "@ethereumjs/util": ^8.1.0 "@metamask/abi-utils": ^2.0.2 "@metamask/utils": ^8.1.0 ethereum-cryptography: ^2.1.2 - ethjs-util: ^0.1.6 tweetnacl: ^1.0.3 tweetnacl-util: ^0.15.1 - checksum: bcb6bd23333e0b4dcb49f8772483dcb4c27e75405a2b111f1eafe0b341b221cf86ba4843e91c567d8836e80b6049d8e2f89c6766c62bbd256533e0f256f6d846 + checksum: 98d056bd83aeb2d29ec3de09cd18e67d97ea295a59d405a9ce3fe274badd2d4f18da1fe530a266b4c777650855ed75ecd3577decd607a561e938dd7a808c5839 languageName: node linkType: hard @@ -2061,7 +2060,7 @@ __metadata: "@metamask/auto-changelog": ^3.4.3 "@metamask/base-controller": ^4.0.0 "@metamask/eth-keyring-controller": ^15.0.0 - "@metamask/eth-sig-util": ^7.0.0 + "@metamask/eth-sig-util": ^7.0.1 "@metamask/message-manager": ^7.3.6 "@metamask/preferences-controller": ^5.0.0 "@metamask/scure-bip39": ^2.1.1 @@ -2110,7 +2109,7 @@ __metadata: "@metamask/auto-changelog": ^3.4.3 "@metamask/base-controller": ^4.0.0 "@metamask/controller-utils": ^6.1.0 - "@metamask/eth-sig-util": ^7.0.0 + "@metamask/eth-sig-util": ^7.0.1 "@metamask/utils": ^8.2.0 "@types/jest": ^27.4.1 "@types/uuid": ^8.3.0 @@ -3159,20 +3158,13 @@ __metadata: languageName: node linkType: hard -"@types/json-schema@npm:*": +"@types/json-schema@npm:*, @types/json-schema@npm:^7.0.9": version: 7.0.15 resolution: "@types/json-schema@npm:7.0.15" checksum: 97ed0cb44d4070aecea772b7b2e2ed971e10c81ec87dd4ecc160322ffa55ff330dace1793489540e3e318d90942064bb697cc0f8989391797792d919737b3b98 languageName: node linkType: hard -"@types/json-schema@npm:^7.0.9": - version: 7.0.12 - resolution: "@types/json-schema@npm:7.0.12" - checksum: 00239e97234eeb5ceefb0c1875d98ade6e922bfec39dd365ec6bd360b5c2f825e612ac4f6e5f1d13601b8b30f378f15e6faa805a3a732f4a1bbe61915163d293 - languageName: node - linkType: hard - "@types/json5@npm:^0.0.29": version: 0.0.29 resolution: "@types/json5@npm:0.0.29" @@ -5749,16 +5741,6 @@ __metadata: languageName: node linkType: hard -"ethjs-util@npm:^0.1.6": - version: 0.1.6 - resolution: "ethjs-util@npm:0.1.6" - dependencies: - is-hex-prefixed: 1.0.0 - strip-hex-prefix: 1.0.0 - checksum: 1f42959e78ec6f49889c49c8a98639e06f52a15966387dd39faf2930db48663d026efb7db2702dcffe7f2a99c4a0144b7ce784efdbf733f4077aae95de76d65f - languageName: node - linkType: hard - "ethjs@npm:^0.3.0": version: 0.3.9 resolution: "ethjs@npm:0.3.9"