diff --git a/package.json b/package.json index e38c92b88f8..042b2cb24bf 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "eth-rpc-errors": "^4.0.0", "eth-sig-util": "^3.0.0", "ethereumjs-tx": "^1.3.7", - "ethereumjs-util": "^6.1.0", + "ethereumjs-util": "^7.0.10", "ethereumjs-wallet": "^1.0.1", "ethjs-util": "^0.1.6", "human-standard-collectible-abi": "^1.0.2", diff --git a/src/assets/AssetsController.test.ts b/src/assets/AssetsController.test.ts index bf90f600128..380784abc73 100644 --- a/src/assets/AssetsController.test.ts +++ b/src/assets/AssetsController.test.ts @@ -38,7 +38,7 @@ describe('AssetsController', () => { }); nock(OPEN_SEA_HOST) - .get(`${OPEN_SEA_PATH}/asset_contract/0xfoO`) + .get(`${OPEN_SEA_PATH}/asset_contract/0x01`) .reply(200, { description: 'Description', image_url: 'url', @@ -46,7 +46,7 @@ describe('AssetsController', () => { symbol: 'FOO', total_supply: 0, }) - .get(`${OPEN_SEA_PATH}/asset_contract/0xFOu`) + .get(`${OPEN_SEA_PATH}/asset_contract/0x02`) .reply(200, { description: 'Description', image_url: 'url', @@ -54,7 +54,7 @@ describe('AssetsController', () => { symbol: 'FOU', total_supply: 10, }) - .get(`${OPEN_SEA_PATH}/asset/0xfoO/1`) + .get(`${OPEN_SEA_PATH}/asset/0x01/1`) .reply(200, { description: 'Description', image_original_url: 'url', @@ -115,16 +115,16 @@ describe('AssetsController', () => { }); it('should add token', async () => { - await assetsController.addToken('foo', 'bar', 2); + await assetsController.addToken('0x01', 'bar', 2); expect(assetsController.state.tokens[0]).toStrictEqual({ - address: '0xfoO', + address: '0x01', decimals: 2, image: undefined, symbol: 'bar', }); - await assetsController.addToken('foo', 'baz', 2); + await assetsController.addToken('0x01', 'baz', 2); expect(assetsController.state.tokens[0]).toStrictEqual({ - address: '0xfoO', + address: '0x01', decimals: 2, image: undefined, symbol: 'baz', @@ -133,33 +133,33 @@ describe('AssetsController', () => { it('should add tokens', async () => { await assetsController.addTokens([ - { address: 'addressA', symbol: 'barA', decimals: 2 }, - { address: 'addressB', symbol: 'barB', decimals: 2 }, + { address: '0x01', symbol: 'barA', decimals: 2 }, + { address: '0x02', symbol: 'barB', decimals: 2 }, ]); expect(assetsController.state.tokens[0]).toStrictEqual({ - address: '0xAdDRessA', + address: '0x01', decimals: 2, image: undefined, symbol: 'barA', }); expect(assetsController.state.tokens[1]).toStrictEqual({ - address: '0xAddReSSB', + address: '0x02', decimals: 2, image: undefined, symbol: 'barB', }); await assetsController.addTokens([ - { address: 'addressA', symbol: 'bazA', decimals: 2 }, - { address: 'addressB', symbol: 'bazB', decimals: 2 }, + { address: '0x01', symbol: 'bazA', decimals: 2 }, + { address: '0x02', symbol: 'bazB', decimals: 2 }, ]); expect(assetsController.state.tokens[0]).toStrictEqual({ - address: '0xAdDRessA', + address: '0x01', decimals: 2, image: undefined, symbol: 'bazA', }); expect(assetsController.state.tokens[1]).toStrictEqual({ - address: '0xAddReSSB', + address: '0x02', decimals: 2, image: undefined, symbol: 'bazB', @@ -171,12 +171,12 @@ describe('AssetsController', () => { const secondAddress = '0x321'; preferences.update({ selectedAddress: firstAddress }); - await assetsController.addToken('foo', 'bar', 2); + await assetsController.addToken('0x01', 'bar', 2); preferences.update({ selectedAddress: secondAddress }); expect(assetsController.state.tokens).toHaveLength(0); preferences.update({ selectedAddress: firstAddress }); expect(assetsController.state.tokens[0]).toStrictEqual({ - address: '0xfoO', + address: '0x01', decimals: 2, image: undefined, symbol: 'bar', @@ -192,7 +192,7 @@ describe('AssetsController', () => { chainId: NetworksChainId[firstNetworkType], }, }); - await assetsController.addToken('foo', 'bar', 2); + await assetsController.addToken('0x01', 'bar', 2); network.update({ provider: { type: secondNetworkType, @@ -207,7 +207,7 @@ describe('AssetsController', () => { }, }); expect(assetsController.state.tokens[0]).toStrictEqual({ - address: '0xfoO', + address: '0x01', decimals: 2, image: undefined, symbol: 'bar', @@ -215,8 +215,8 @@ describe('AssetsController', () => { }); it('should remove token', async () => { - await assetsController.addToken('foo', 'bar', 2); - assetsController.removeToken('0xfoO'); + await assetsController.addToken('0x01', 'bar', 2); + assetsController.removeToken('0x01'); expect(assetsController.state.tokens).toHaveLength(0); }); @@ -224,14 +224,14 @@ describe('AssetsController', () => { const firstAddress = '0x123'; const secondAddress = '0x321'; preferences.update({ selectedAddress: firstAddress }); - await assetsController.addToken('fou', 'baz', 2); + await assetsController.addToken('0x02', 'baz', 2); preferences.update({ selectedAddress: secondAddress }); - await assetsController.addToken('foo', 'bar', 2); - assetsController.removeToken('0xfoO'); + await assetsController.addToken('0x01', 'bar', 2); + assetsController.removeToken('0x01'); expect(assetsController.state.tokens).toHaveLength(0); preferences.update({ selectedAddress: firstAddress }); expect(assetsController.state.tokens[0]).toStrictEqual({ - address: '0xFOu', + address: '0x02', decimals: 2, image: undefined, symbol: 'baz', @@ -247,15 +247,15 @@ describe('AssetsController', () => { chainId: NetworksChainId[firstNetworkType], }, }); - await assetsController.addToken('fou', 'baz', 2); + await assetsController.addToken('0x02', 'baz', 2); network.update({ provider: { type: secondNetworkType, chainId: NetworksChainId[secondNetworkType], }, }); - await assetsController.addToken('foo', 'bar', 2); - assetsController.removeToken('0xfoO'); + await assetsController.addToken('0x01', 'bar', 2); + assetsController.removeToken('0x01'); expect(assetsController.state.tokens).toHaveLength(0); network.update({ provider: { @@ -264,7 +264,7 @@ describe('AssetsController', () => { }, }); expect(assetsController.state.tokens[0]).toStrictEqual({ - address: '0xFOu', + address: '0x02', decimals: 2, image: undefined, symbol: 'baz', @@ -272,20 +272,20 @@ describe('AssetsController', () => { }); it('should add collectible and collectible contract', async () => { - await assetsController.addCollectible('foo', 1, { + await assetsController.addCollectible('0x01', 1, { name: 'name', image: 'image', description: 'description', }); expect(assetsController.state.collectibles[0]).toStrictEqual({ - address: '0xfoO', + address: '0x01', description: 'description', image: 'image', name: 'name', tokenId: 1, }); expect(assetsController.state.collectibleContracts[0]).toStrictEqual({ - address: '0xfoO', + address: '0x01', description: 'Description', logo: 'url', name: 'Name', @@ -295,25 +295,25 @@ describe('AssetsController', () => { }); it('should update collectible if image is different', async () => { - await assetsController.addCollectible('foo', 1, { + await assetsController.addCollectible('0x01', 1, { name: 'name', image: 'image', description: 'description', }); expect(assetsController.state.collectibles[0]).toStrictEqual({ - address: '0xfoO', + address: '0x01', description: 'description', image: 'image', name: 'name', tokenId: 1, }); - await assetsController.addCollectible('foo', 1, { + await assetsController.addCollectible('0x01', 1, { name: 'name', image: 'image-updated', description: 'description', }); expect(assetsController.state.collectibles[0]).toStrictEqual({ - address: '0xfoO', + address: '0x01', description: 'description', image: 'image-updated', name: 'name', @@ -322,12 +322,12 @@ describe('AssetsController', () => { }); it('should not duplicate collectible nor collectible contract if already added', async () => { - await assetsController.addCollectible('foo', 1, { + await assetsController.addCollectible('0x01', 1, { name: 'name', image: 'image', description: 'description', }); - await assetsController.addCollectible('foo', 1, { + await assetsController.addCollectible('0x01', 1, { name: 'name', image: 'image', description: 'description', @@ -337,12 +337,12 @@ describe('AssetsController', () => { }); it('should not add collectible contract if collectible contract already exists', async () => { - await assetsController.addCollectible('foo', 1, { + await assetsController.addCollectible('0x01', 1, { name: 'name', image: 'image', description: 'description', }); - await assetsController.addCollectible('foo', 2, { + await assetsController.addCollectible('0x01', 2, { name: 'name', image: 'image', description: 'description', @@ -352,9 +352,9 @@ describe('AssetsController', () => { }); it('should add collectible and get information from OpenSea', async () => { - await assetsController.addCollectible('foo', 1); + await assetsController.addCollectible('0x01', 1); expect(assetsController.state.collectibles[0]).toStrictEqual({ - address: '0xfoO', + address: '0x01', description: 'Description', imageOriginal: 'url', name: 'Name', @@ -391,12 +391,12 @@ describe('AssetsController', () => { .stub(assetsController, 'getCollectibleInformation' as any) .returns({ name: 'name', image: 'url', description: 'description' }); preferences.update({ selectedAddress: firstAddress }); - await assetsController.addCollectible('foo', 1234); + await assetsController.addCollectible('0x01', 1234); preferences.update({ selectedAddress: secondAddress }); - await assetsController.addCollectible('fou', 4321); + await assetsController.addCollectible('0x02', 4321); preferences.update({ selectedAddress: firstAddress }); expect(assetsController.state.collectibles[0]).toStrictEqual({ - address: '0xfoO', + address: '0x01', description: 'description', image: 'url', name: 'name', @@ -416,7 +416,7 @@ describe('AssetsController', () => { chainId: NetworksChainId[firstNetworkType], }, }); - await assetsController.addCollectible('foo', 1234); + await assetsController.addCollectible('0x01', 1234); network.update({ provider: { type: secondNetworkType, @@ -431,7 +431,7 @@ describe('AssetsController', () => { }, }); expect(assetsController.state.collectibles[0]).toStrictEqual({ - address: '0xfoO', + address: '0x01', description: 'description', image: 'url', name: 'name', @@ -476,28 +476,28 @@ describe('AssetsController', () => { }); it('should remove collectible and collectible contract', async () => { - await assetsController.addCollectible('foo', 1, { + await assetsController.addCollectible('0x01', 1, { name: 'name', image: 'image', description: 'description', }); - assetsController.removeCollectible('0xfoO', 1); + assetsController.removeCollectible('0x01', 1); expect(assetsController.state.collectibles).toHaveLength(0); expect(assetsController.state.collectibleContracts).toHaveLength(0); }); it('should not remove collectible contract if collectible still exists', async () => { - await assetsController.addCollectible('foo', 1, { + await assetsController.addCollectible('0x01', 1, { name: 'name', image: 'image', description: 'description', }); - await assetsController.addCollectible('foo', 2, { + await assetsController.addCollectible('0x01', 2, { name: 'name', image: 'image', description: 'description', }); - assetsController.removeCollectible('0xfoO', 1); + assetsController.removeCollectible('0x01', 1); expect(assetsController.state.collectibles).toHaveLength(1); expect(assetsController.state.collectibleContracts).toHaveLength(1); }); @@ -509,14 +509,14 @@ describe('AssetsController', () => { const firstAddress = '0x123'; const secondAddress = '0x321'; preferences.update({ selectedAddress: firstAddress }); - await assetsController.addCollectible('fou', 4321); + await assetsController.addCollectible('0x02', 4321); preferences.update({ selectedAddress: secondAddress }); - await assetsController.addCollectible('foo', 1234); - assetsController.removeCollectible('0xfoO', 1234); + await assetsController.addCollectible('0x01', 1234); + assetsController.removeCollectible('0x01', 1234); expect(assetsController.state.collectibles).toHaveLength(0); preferences.update({ selectedAddress: firstAddress }); expect(assetsController.state.collectibles[0]).toStrictEqual({ - address: '0xFOu', + address: '0x02', description: 'description', image: 'url', name: 'name', @@ -536,16 +536,16 @@ describe('AssetsController', () => { chainId: NetworksChainId[firstNetworkType], }, }); - await assetsController.addCollectible('fou', 4321); + await assetsController.addCollectible('0x02', 4321); network.update({ provider: { type: secondNetworkType, chainId: NetworksChainId[secondNetworkType], }, }); - await assetsController.addCollectible('foo', 1234); - assetsController.removeToken('0xfoO'); - assetsController.removeCollectible('0xfoO', 1234); + await assetsController.addCollectible('0x01', 1234); + assetsController.removeToken('0x01'); + assetsController.removeCollectible('0x01', 1234); expect(assetsController.state.collectibles).toHaveLength(0); network.update({ provider: { @@ -554,7 +554,7 @@ describe('AssetsController', () => { }, }); expect(assetsController.state.collectibles[0]).toStrictEqual({ - address: '0xFOu', + address: '0x02', description: 'description', image: 'url', name: 'name', @@ -615,7 +615,7 @@ describe('AssetsController', () => { }, 'ERC20', ); - assetsController.rejectWatchAsset('foo'); + assetsController.rejectWatchAsset('0x01'); assetsController.rejectWatchAsset(suggestedAssetMeta.id); assetsController.hub.once(`${suggestedAssetMeta.id}:finished`, () => { expect(assetsController.state.suggestedAssets).toHaveLength(0); @@ -661,26 +661,26 @@ describe('AssetsController', () => { }); it('should not add duplicate tokens to the ignoredToken list', async () => { - await assetsController.addToken('0xfoO', 'bar', 2); + await assetsController.addToken('0x01', 'bar', 2); await assetsController.addToken('0xfAA', 'bar', 3); expect(assetsController.state.ignoredTokens).toHaveLength(0); expect(assetsController.state.tokens).toHaveLength(2); - assetsController.removeAndIgnoreToken('0xfoO'); + assetsController.removeAndIgnoreToken('0x01'); expect(assetsController.state.tokens).toHaveLength(1); expect(assetsController.state.ignoredTokens).toHaveLength(1); - await assetsController.addToken('0xfoO', 'bar', 2); + await assetsController.addToken('0x01', 'bar', 2); expect(assetsController.state.ignoredTokens).toHaveLength(1); - assetsController.removeAndIgnoreToken('0xfoO'); + assetsController.removeAndIgnoreToken('0x01'); expect(assetsController.state.ignoredTokens).toHaveLength(1); }); it('should not add duplicate collectibles to the ignoredCollectibles list', async () => { - await assetsController.addCollectible('foo', 1, { + await assetsController.addCollectible('0x01', 1, { name: 'name', image: 'image', description: 'description', }); - await assetsController.addCollectible('foo', 2, { + await assetsController.addCollectible('0x01', 2, { name: 'name', image: 'image', description: 'description', @@ -689,11 +689,11 @@ describe('AssetsController', () => { expect(assetsController.state.collectibles).toHaveLength(2); expect(assetsController.state.ignoredCollectibles).toHaveLength(0); - assetsController.removeAndIgnoreCollectible('0xfoO', 1); + assetsController.removeAndIgnoreCollectible('0x01', 1); expect(assetsController.state.collectibles).toHaveLength(1); expect(assetsController.state.ignoredCollectibles).toHaveLength(1); - await assetsController.addCollectible('foo', 1, { + await assetsController.addCollectible('0x01', 1, { name: 'name', image: 'image', description: 'description', @@ -701,15 +701,15 @@ describe('AssetsController', () => { expect(assetsController.state.collectibles).toHaveLength(2); expect(assetsController.state.ignoredCollectibles).toHaveLength(1); - assetsController.removeAndIgnoreCollectible('0xfoO', 1); + assetsController.removeAndIgnoreCollectible('0x01', 1); expect(assetsController.state.collectibles).toHaveLength(1); expect(assetsController.state.ignoredCollectibles).toHaveLength(1); }); it('should be able to clear the ignoredToken list', async () => { - await assetsController.addToken('0xfoO', 'bar', 2); + await assetsController.addToken('0x01', 'bar', 2); expect(assetsController.state.ignoredTokens).toHaveLength(0); - assetsController.removeAndIgnoreToken('0xfoO'); + assetsController.removeAndIgnoreToken('0x01'); expect(assetsController.state.tokens).toHaveLength(0); expect(assetsController.state.ignoredTokens).toHaveLength(1); assetsController.clearIgnoredTokens(); @@ -717,7 +717,7 @@ describe('AssetsController', () => { }); it('should be able to clear the ignoredCollectibles list', async () => { - await assetsController.addCollectible('0xfoO', 1, { + await assetsController.addCollectible('0x02', 1, { name: 'name', image: 'image', description: 'description', @@ -726,7 +726,7 @@ describe('AssetsController', () => { expect(assetsController.state.collectibles).toHaveLength(1); expect(assetsController.state.ignoredCollectibles).toHaveLength(0); - assetsController.removeAndIgnoreCollectible('0xfoO', 1); + assetsController.removeAndIgnoreCollectible('0x02', 1); expect(assetsController.state.collectibles).toHaveLength(0); expect(assetsController.state.ignoredCollectibles).toHaveLength(1); diff --git a/src/assets/AssetsController.ts b/src/assets/AssetsController.ts index 117a15de943..a6dea9febc2 100644 --- a/src/assets/AssetsController.ts +++ b/src/assets/AssetsController.ts @@ -1,11 +1,15 @@ import { EventEmitter } from 'events'; -import { toChecksumAddress } from 'ethereumjs-util'; import { v1 as random } from 'uuid'; import { Mutex } from 'async-mutex'; import BaseController, { BaseConfig, BaseState } from '../BaseController'; import type { PreferencesState } from '../user/PreferencesController'; import type { NetworkState, NetworkType } from '../network/NetworkController'; -import { safelyExecute, handleFetch, validateTokenToWatch } from '../util'; +import { + safelyExecute, + handleFetch, + validateTokenToWatch, + toChecksumHexAddress, +} from '../util'; import type { Token } from './TokenRatesController'; import type { ApiCollectible, @@ -452,7 +456,7 @@ export class AssetsController extends BaseController< ): Promise { const releaseLock = await this.mutex.acquire(); try { - address = toChecksumAddress(address); + address = toChecksumHexAddress(address); const { allCollectibles, collectibles } = this.state; const { chainId, selectedAddress } = this.config; const existingEntry: Collectible | undefined = collectibles.find( @@ -522,7 +526,7 @@ export class AssetsController extends BaseController< ): Promise { const releaseLock = await this.mutex.acquire(); try { - address = toChecksumAddress(address); + address = toChecksumHexAddress(address); const { allCollectibleContracts, collectibleContracts } = this.state; const { chainId, selectedAddress } = this.config; const existingEntry = collectibleContracts.find( @@ -599,7 +603,7 @@ export class AssetsController extends BaseController< address: string, tokenId: number, ) { - address = toChecksumAddress(address); + address = toChecksumHexAddress(address); const { allCollectibles, collectibles, ignoredCollectibles } = this.state; const { chainId, selectedAddress } = this.config; const newIgnoredCollectibles = [...ignoredCollectibles]; @@ -636,7 +640,7 @@ export class AssetsController extends BaseController< * @param tokenId - Token identifier of the collectible */ private removeIndividualCollectible(address: string, tokenId: number) { - address = toChecksumAddress(address); + address = toChecksumHexAddress(address); const { allCollectibles, collectibles } = this.state; const { chainId, selectedAddress } = this.config; const newCollectibles = collectibles.filter( @@ -665,7 +669,7 @@ export class AssetsController extends BaseController< * @returns - Promise resolving to the current collectible contracts list */ private removeCollectibleContract(address: string): CollectibleContract[] { - address = toChecksumAddress(address); + address = toChecksumHexAddress(address); const { allCollectibleContracts, collectibleContracts } = this.state; const { chainId, selectedAddress } = this.config; const newCollectibleContracts = collectibleContracts.filter( @@ -822,7 +826,7 @@ export class AssetsController extends BaseController< ): Promise { const releaseLock = await this.mutex.acquire(); try { - address = toChecksumAddress(address); + address = toChecksumHexAddress(address); const { allTokens, tokens } = this.state; const { chainId, selectedAddress } = this.config; const newEntry: Token = { address, symbol, decimals, image }; @@ -861,7 +865,7 @@ export class AssetsController extends BaseController< try { tokensToAdd.forEach((tokenToAdd) => { const { address, symbol, decimals, image } = tokenToAdd; - const checksumAddress = toChecksumAddress(address); + const checksumAddress = toChecksumHexAddress(address); const newEntry: Token = { address: checksumAddress, @@ -1025,7 +1029,7 @@ export class AssetsController extends BaseController< collectibleMetadata?: CollectibleMetadata, detection?: boolean, ) { - address = toChecksumAddress(address); + address = toChecksumHexAddress(address); const newCollectibleContracts = await this.addCollectibleContract( address, detection, @@ -1055,7 +1059,7 @@ export class AssetsController extends BaseController< * @param address - Hex address of the token contract */ removeAndIgnoreToken(address: string) { - address = toChecksumAddress(address); + address = toChecksumHexAddress(address); const { allTokens, tokens, ignoredTokens } = this.state; const { chainId, selectedAddress } = this.config; const newIgnoredTokens = [...ignoredTokens]; @@ -1088,7 +1092,7 @@ export class AssetsController extends BaseController< * @param address - Hex address of the token contract */ removeToken(address: string) { - address = toChecksumAddress(address); + address = toChecksumHexAddress(address); const { allTokens, tokens } = this.state; const { chainId, selectedAddress } = this.config; const newTokens = tokens.filter((token) => token.address !== address); @@ -1108,7 +1112,7 @@ export class AssetsController extends BaseController< * @param tokenId - Token identifier of the collectible */ removeCollectible(address: string, tokenId: number) { - address = toChecksumAddress(address); + address = toChecksumHexAddress(address); this.removeIndividualCollectible(address, tokenId); const { collectibles } = this.state; const remainingCollectible = collectibles.find( @@ -1126,7 +1130,7 @@ export class AssetsController extends BaseController< * @param tokenId - Token identifier of the collectible */ removeAndIgnoreCollectible(address: string, tokenId: number) { - address = toChecksumAddress(address); + address = toChecksumHexAddress(address); this.removeAndIgnoreIndividualCollectible(address, tokenId); const { collectibles } = this.state; const remainingCollectible = collectibles.find( diff --git a/src/assets/AssetsDetectionController.test.ts b/src/assets/AssetsDetectionController.test.ts index 1023cea302f..189e938a248 100644 --- a/src/assets/AssetsDetectionController.test.ts +++ b/src/assets/AssetsDetectionController.test.ts @@ -82,7 +82,7 @@ describe('AssetsDetectionController', () => { total_supply: 0, }) .get( - `${OPEN_SEA_PATH}/asset_contract/0x1D963688FE2209A98db35c67A041524822cf04Hh`, + `${OPEN_SEA_PATH}/asset_contract/0xebE4e5E773AFD2bAc25De0cFafa084CFb3cBf1eD`, ) .reply(200, { description: 'Description HH', @@ -92,11 +92,11 @@ describe('AssetsDetectionController', () => { total_supply: 10, }) .get( - `${OPEN_SEA_PATH}/asset_contract/0x1d963688FE2209A98db35c67A041524822CF04gg`, + `${OPEN_SEA_PATH}/asset_contract/0xCE7ec4B2DfB30eB6c0BB5656D33aAd6BFb4001Fc`, ) .replyWithError(new TypeError('Failed to fetch')) .get( - `${OPEN_SEA_PATH}/asset_contract/0x1D963688fe2209a98dB35c67a041524822Cf04ii`, + `${OPEN_SEA_PATH}/asset_contract/0x0B0fa4fF58D28A88d63235bd0756EDca69e49e6d`, ) .replyWithError(new TypeError('Failed to fetch')) .get(`${OPEN_SEA_PATH}/assets?owner=0x1&limit=300`) @@ -104,7 +104,7 @@ describe('AssetsDetectionController', () => { assets: [ { asset_contract: { - address: '0x1d963688FE2209A98db35c67A041524822CF04gg', + address: '0xCE7ec4B2DfB30eB6c0BB5656D33aAd6BFb4001Fc', }, description: 'Description 2577', image_url: 'image/2577.png', @@ -113,7 +113,7 @@ describe('AssetsDetectionController', () => { }, { asset_contract: { - address: '0x1d963688FE2209A98db35c67A041524822CF04ii', + address: '0x0B0fa4fF58D28A88d63235bd0756EDca69e49e6d', }, description: 'Description 2578', image_url: 'image/2578.png', @@ -122,7 +122,7 @@ describe('AssetsDetectionController', () => { }, { asset_contract: { - address: '0x1d963688FE2209A98db35c67A041524822CF04hh', + address: '0xebE4e5E773AFD2bAc25De0cFafa084CFb3cBf1eD', }, description: 'Description 2574', image_url: 'image/2574.png', @@ -231,7 +231,7 @@ describe('AssetsDetectionController', () => { await assetsDetection.detectCollectibles(); expect(assets.state.collectibles).toStrictEqual([ { - address: '0x1D963688FE2209A98db35c67A041524822cf04Hh', + address: '0xebE4e5E773AFD2bAc25De0cFafa084CFb3cBf1eD', description: 'Description 2574', image: 'image/2574.png', name: 'ID 2574', @@ -243,7 +243,7 @@ describe('AssetsDetectionController', () => { it('should detect, add collectibles and do nor remove not detected collectibles correctly', async () => { assetsDetection.configure({ networkType: MAINNET, selectedAddress: '0x1' }); await assets.addCollectible( - '0x1D963688FE2209A98db35c67A041524822cf04Hh', + '0xebE4e5E773AFD2bAc25De0cFafa084CFb3cBf1eD', 2573, { description: 'Description 2573', @@ -254,14 +254,14 @@ describe('AssetsDetectionController', () => { await assetsDetection.detectCollectibles(); expect(assets.state.collectibles).toStrictEqual([ { - address: '0x1D963688FE2209A98db35c67A041524822cf04Hh', + address: '0xebE4e5E773AFD2bAc25De0cFafa084CFb3cBf1eD', description: 'Description 2573', image: 'image/2573.png', name: 'ID 2573', tokenId: 2573, }, { - address: '0x1D963688FE2209A98db35c67A041524822cf04Hh', + address: '0xebE4e5E773AFD2bAc25De0cFafa084CFb3cBf1eD', description: 'Description 2574', image: 'image/2574.png', name: 'ID 2574', @@ -292,28 +292,28 @@ describe('AssetsDetectionController', () => { it('should not add collectible if collectible or collectible contract has no information to display', async () => { const collectibleHH2574 = { - address: '0x1D963688FE2209A98db35c67A041524822cf04Hh', + address: '0xebE4e5E773AFD2bAc25De0cFafa084CFb3cBf1eD', description: 'Description 2574', image: 'image/2574.png', name: 'ID 2574', tokenId: 2574, }; const collectibleGG2574 = { - address: '0x1d963688FE2209A98db35c67A041524822CF04gg', + address: '0xCE7ec4B2DfB30eB6c0BB5656D33aAd6BFb4001Fc', description: 'Description 2574', image: 'image/2574.png', name: 'ID 2574', tokenId: 2574, }; const collectibleII2577 = { - address: '0x1D963688fe2209a98dB35c67a041524822Cf04ii', + address: '0x0B0fa4fF58D28A88d63235bd0756EDca69e49e6d', description: 'Description 2577', image: 'image/2577.png', name: 'ID 2577', tokenId: 2577, }; const collectibleContractHH = { - address: '0x1D963688FE2209A98db35c67A041524822cf04Hh', + address: '0xebE4e5E773AFD2bAc25De0cFafa084CFb3cBf1eD', description: 'Description HH', logo: 'url HH', name: 'Name HH', @@ -321,7 +321,7 @@ describe('AssetsDetectionController', () => { totalSupply: 10, }; const collectibleContractGG = { - address: '0x1d963688FE2209A98db35c67A041524822CF04gg', + address: '0xCE7ec4B2DfB30eB6c0BB5656D33aAd6BFb4001Fc', description: 'Description GG', logo: 'url GG', name: 'Name GG', @@ -329,7 +329,7 @@ describe('AssetsDetectionController', () => { totalSupply: 10, }; const collectibleContractII = { - address: '0x1D963688fe2209a98dB35c67a041524822Cf04ii', + address: '0x0B0fa4fF58D28A88d63235bd0756EDca69e49e6d', description: 'Description II', logo: 'url II', name: 'Name II', @@ -347,7 +347,7 @@ describe('AssetsDetectionController', () => { nock(OPEN_SEA_HOST) .get( - `${OPEN_SEA_PATH}/asset_contract/0x1d963688FE2209A98db35c67A041524822CF04gg`, + `${OPEN_SEA_PATH}/asset_contract/0xCE7ec4B2DfB30eB6c0BB5656D33aAd6BFb4001Fc`, ) .reply(200, { description: 'Description GG', @@ -357,7 +357,7 @@ describe('AssetsDetectionController', () => { total_supply: 10, }) .get( - `${OPEN_SEA_PATH}/asset_contract/0x1D963688fe2209a98dB35c67a041524822Cf04ii`, + `${OPEN_SEA_PATH}/asset_contract/0x0B0fa4fF58D28A88d63235bd0756EDca69e49e6d`, ) .reply(200, { description: 'Description II', @@ -371,7 +371,7 @@ describe('AssetsDetectionController', () => { assets: [ { asset_contract: { - address: '0x1d963688FE2209A98db35c67A041524822CF04ii', + address: '0x0B0fa4fF58D28A88d63235bd0756EDca69e49e6d', }, description: 'Description 2577', image_url: 'image/2577.png', @@ -380,7 +380,7 @@ describe('AssetsDetectionController', () => { }, { asset_contract: { - address: '0x1D963688fe2209a98dB35c67a041524822Cf04gg', + address: '0xCE7ec4B2DfB30eB6c0BB5656D33aAd6BFb4001Fc', }, description: 'Description 2574', image_url: 'image/2574.png', @@ -389,7 +389,7 @@ describe('AssetsDetectionController', () => { }, { asset_contract: { - address: '0x1d963688FE2209A98db35c67A041524822CF04hh', + address: '0xebE4e5E773AFD2bAc25De0cFafa084CFb3cBf1eD', }, description: 'Description 2574', image_url: 'image/2574.png', diff --git a/src/assets/AssetsDetectionController.ts b/src/assets/AssetsDetectionController.ts index a3187e566a5..e2fbd7bbffb 100644 --- a/src/assets/AssetsDetectionController.ts +++ b/src/assets/AssetsDetectionController.ts @@ -1,9 +1,8 @@ -import { toChecksumAddress } from 'ethereumjs-util'; import contractMap from '@metamask/contract-metadata'; import BaseController, { BaseConfig, BaseState } from '../BaseController'; import type { NetworkState, NetworkType } from '../network/NetworkController'; import type { PreferencesState } from '../user/PreferencesController'; -import { safelyExecute, timeoutFetch } from '../util'; +import { safelyExecute, timeoutFetch, toChecksumHexAddress } from '../util'; import type { AssetsController, AssetsState, @@ -330,7 +329,7 @@ export class AssetsDetectionController extends BaseController< const { ignoredTokens } = this.getAssetsState(); if (ignoredTokens.length) { ignored = ignoredTokens.find( - (token) => token.address === toChecksumAddress(tokenAddress), + (token) => token.address === toChecksumHexAddress(tokenAddress), ); } if (!ignored) { @@ -390,7 +389,7 @@ export class AssetsDetectionController extends BaseController< ignored = ignoredCollectibles.find((c) => { /* istanbul ignore next */ return ( - c.address === toChecksumAddress(address) && + c.address === toChecksumHexAddress(address) && c.tokenId === Number(token_id) ); }); diff --git a/src/assets/TokenBalancesController.test.ts b/src/assets/TokenBalancesController.test.ts index 4676411ac81..3b77cccd875 100644 --- a/src/assets/TokenBalancesController.test.ts +++ b/src/assets/TokenBalancesController.test.ts @@ -218,9 +218,9 @@ describe('TokenBalancesController', () => { { interval: 1337 }, ); const updateBalances = sandbox.stub(tokenBalances, 'updateBalances'); - await assets.addToken('0xfoO', 'FOO', 18); + await assets.addToken('0x00', 'FOO', 18); const { tokens } = assets.state; - const found = tokens.filter((token: Token) => token.address === '0xfoO'); + const found = tokens.filter((token: Token) => token.address === '0x00'); expect(found.length > 0).toBe(true); expect(updateBalances.called).toBe(true); }); diff --git a/src/assets/TokenRatesController.test.ts b/src/assets/TokenRatesController.test.ts index 1bd1c6589e5..3b61c372fa2 100644 --- a/src/assets/TokenRatesController.test.ts +++ b/src/assets/TokenRatesController.test.ts @@ -8,21 +8,22 @@ import { AssetsContractController } from './AssetsContractController'; const COINGECKO_HOST = 'https://api.coingecko.com'; const COINGECKO_PATH = '/api/v3/simple/token_price/ethereum'; +const ADDRESS = '0x01'; describe('TokenRatesController', () => { beforeEach(() => { nock(COINGECKO_HOST) .get( - `${COINGECKO_PATH}?contract_addresses=0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359,0xfoO&vs_currencies=eth`, + `${COINGECKO_PATH}?contract_addresses=0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359,${ADDRESS}&vs_currencies=eth`, ) .reply(200, { '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359': { eth: 0.00561045 }, }) - .get(`${COINGECKO_PATH}?contract_addresses=0xfoO&vs_currencies=eth`) + .get(`${COINGECKO_PATH}?contract_addresses=${ADDRESS}&vs_currencies=eth`) .reply(200, {}) .get(`${COINGECKO_PATH}?contract_addresses=bar&vs_currencies=eth`) .reply(200, {}) - .get(`${COINGECKO_PATH}?contract_addresses=0xfoO&vs_currencies=gno`) + .get(`${COINGECKO_PATH}?contract_addresses=${ADDRESS}&vs_currencies=gno`) .reply(200, {}) .persist(); @@ -137,11 +138,10 @@ describe('TokenRatesController', () => { { interval: 10 }, ); const address = '0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359'; - const address2 = '0xfoO'; expect(controller.state.contractExchangeRates).toStrictEqual({}); controller.tokens = [ { address, decimals: 18, symbol: 'DAI' }, - { address: address2, decimals: 0, symbol: '' }, + { address: ADDRESS, decimals: 0, symbol: '' }, ]; await controller.updateExchangeRates(); expect(Object.keys(controller.state.contractExchangeRates)).toContain( @@ -149,9 +149,9 @@ describe('TokenRatesController', () => { ); expect(controller.state.contractExchangeRates[address]).toBeGreaterThan(0); expect(Object.keys(controller.state.contractExchangeRates)).toContain( - address2, + ADDRESS, ); - expect(controller.state.contractExchangeRates[address2]).toStrictEqual(0); + expect(controller.state.contractExchangeRates[ADDRESS]).toStrictEqual(0); }); it('should handle balance not found in API', async () => { diff --git a/src/assets/TokenRatesController.ts b/src/assets/TokenRatesController.ts index 06be7af4c2b..10ff79c3f2b 100644 --- a/src/assets/TokenRatesController.ts +++ b/src/assets/TokenRatesController.ts @@ -1,6 +1,5 @@ -import { toChecksumAddress } from 'ethereumjs-util'; import BaseController, { BaseConfig, BaseState } from '../BaseController'; -import { safelyExecute, handleFetch } from '../util'; +import { safelyExecute, handleFetch, toChecksumHexAddress } from '../util'; import type { AssetsState } from './AssetsController'; import type { CurrencyRateState } from './CurrencyRateController'; @@ -179,7 +178,7 @@ export class TokenRatesController extends BaseController< const query = `contract_addresses=${pairs}&vs_currencies=${nativeCurrency.toLowerCase()}`; const prices = await this.fetchExchangeRate(query); this.tokenList.forEach((token) => { - const address = toChecksumAddress(token.address); + const address = toChecksumHexAddress(token.address); const price = prices[token.address.toLowerCase()]; newContractExchangeRates[address] = price ? price[nativeCurrency.toLowerCase()] diff --git a/src/keyring/KeyringController.test.ts b/src/keyring/KeyringController.test.ts index 5fa548f7b3c..3bbe52f911a 100644 --- a/src/keyring/KeyringController.test.ts +++ b/src/keyring/KeyringController.test.ts @@ -1,4 +1,4 @@ -import * as ethUtil from 'ethereumjs-util'; +import { bufferToHex } from 'ethereumjs-util'; import { recoverPersonalSignature, recoverTypedSignature, @@ -152,7 +152,9 @@ describe('KeyringController', () => { error2 = e; } expect(error1.message).toBe('Cannot import an empty key.'); - expect(error2.message).toBe('Cannot import invalid private key.'); + expect(error2.message).toBe( + 'Expected private key to be an Uint8Array with length 32', + ); const address = '0x51253087e6f8358b5f10c0a94315d69db3357859'; const newKeyring = { accounts: [address], type: 'Simple Key Pair' }; const obj = await keyringController.importAccountWithStrategy( @@ -230,7 +232,7 @@ describe('KeyringController', () => { }); it('should sign personal message', async () => { - const data = ethUtil.bufferToHex(Buffer.from('Hello from test', 'utf8')); + const data = bufferToHex(Buffer.from('Hello from test', 'utf8')); const account = initialState.keyrings[0].accounts[0]; const signature = await keyringController.signPersonalMessage({ data, diff --git a/src/keyring/KeyringController.ts b/src/keyring/KeyringController.ts index 29ebcd8a80b..dc35bd69cf3 100644 --- a/src/keyring/KeyringController.ts +++ b/src/keyring/KeyringController.ts @@ -1,4 +1,9 @@ -import * as ethUtil from 'ethereumjs-util'; +import { + addHexPrefix, + bufferToHex, + isValidPrivate, + toBuffer, +} from 'ethereumjs-util'; import { stripHexPrefix } from 'ethjs-util'; import { normalize as normalizeAddress, @@ -17,6 +22,7 @@ import BaseController, { import PreferencesController from '../user/PreferencesController'; import { PersonalMessageParams } from '../message-manager/PersonalMessageManager'; import { TypedMessageParams } from '../message-manager/TypedMessageManager'; +import { toChecksumHexAddress } from '../util'; const privates = new WeakMap(); @@ -332,8 +338,9 @@ export class KeyringController extends BaseController< if (!importedKey) { throw new Error('Cannot import an empty key.'); } - const prefixed = ethUtil.addHexPrefix(importedKey); - if (!ethUtil.isValidPrivate(ethUtil.toBuffer(prefixed))) { + const prefixed = addHexPrefix(importedKey); + /* istanbul ignore if */ + if (!isValidPrivate(toBuffer(prefixed))) { throw new Error('Cannot import invalid private key.'); } privateKey = stripHexPrefix(prefixed); @@ -346,7 +353,7 @@ export class KeyringController extends BaseController< } catch (e) { wallet = wallet || (await Wallet.fromV3(input, password, true)); } - privateKey = ethUtil.bufferToHex(wallet.getPrivateKey()); + privateKey = bufferToHex(wallet.getPrivateKey()); break; default: throw new Error(`Unexpected import strategy: '${strategy}'`); @@ -418,9 +425,7 @@ export class KeyringController extends BaseController< const address = normalizeAddress(messageParams.from); const { password } = privates.get(this).keyring; const privateKey = await this.exportAccount(password, address); - const privateKeyBuffer = ethUtil.toBuffer( - ethUtil.addHexPrefix(privateKey), - ); + const privateKeyBuffer = toBuffer(addHexPrefix(privateKey)); switch (version) { case SignTypedDataVersion.V1: // signTypedDataLegacy will throw if the data is invalid. @@ -561,9 +566,7 @@ export class KeyringController extends BaseController< async (keyring: KeyringObject, index: number): Promise => { const keyringAccounts = await keyring.getAccounts(); const accounts = Array.isArray(keyringAccounts) - ? keyringAccounts.map((address) => - ethUtil.toChecksumAddress(address), - ) + ? keyringAccounts.map((address) => toChecksumHexAddress(address)) : /* istanbul ignore next */ []; return { accounts, diff --git a/src/third-party/EnsController.test.ts b/src/third-party/EnsController.test.ts index 9db9941a0c7..8e0fe84309d 100644 --- a/src/third-party/EnsController.test.ts +++ b/src/third-party/EnsController.test.ts @@ -1,5 +1,4 @@ -import { toChecksumAddress } from 'ethereumjs-util'; - +import { toChecksumHexAddress } from '../util'; import EnsController from './EnsController'; const address1 = '0x32Be343B94f860124dC4fEe278FDCBD38C102D88'; @@ -8,9 +7,9 @@ const address3 = '0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359'; const name1 = 'foobarb.eth'; const name2 = 'bazbarb.eth'; -const address1Checksum = toChecksumAddress(address1); -const address2Checksum = toChecksumAddress(address2); -const address3Checksum = toChecksumAddress(address3); +const address1Checksum = toChecksumHexAddress(address1); +const address2Checksum = toChecksumHexAddress(address2); +const address3Checksum = toChecksumHexAddress(address3); describe('EnsController', () => { it('should set default state', () => { diff --git a/src/third-party/EnsController.ts b/src/third-party/EnsController.ts index bc519e2879b..c8cf7fba3cf 100644 --- a/src/third-party/EnsController.ts +++ b/src/third-party/EnsController.ts @@ -1,6 +1,9 @@ -import { isValidAddress, toChecksumAddress } from 'ethereumjs-util'; import BaseController, { BaseConfig, BaseState } from '../BaseController'; -import { normalizeEnsName } from '../util'; +import { + normalizeEnsName, + isValidHexAddress, + toChecksumHexAddress, +} from '../util'; /** * @type EnsEntry @@ -122,7 +125,7 @@ export class EnsController extends BaseController { !Number.isInteger(Number.parseInt(chainId, 10)) || !ensName || typeof ensName !== 'string' || - (address && !isValidAddress(address)) + (address && !isValidHexAddress(address)) ) { throw new Error( `Invalid ENS entry: { chainId:${chainId}, ensName:${ensName}, address:${address}}`, @@ -134,7 +137,7 @@ export class EnsController extends BaseController { throw new Error(`Invalid ENS name: ${ensName}`); } - const normalizedAddress = address ? toChecksumAddress(address) : null; + const normalizedAddress = address ? toChecksumHexAddress(address) : null; const subState = this.state.ensEntries[chainId]; if ( diff --git a/src/user/AddressBookController.test.ts b/src/user/AddressBookController.test.ts index 83ce751e654..273989c1704 100644 --- a/src/user/AddressBookController.test.ts +++ b/src/user/AddressBookController.test.ts @@ -107,7 +107,7 @@ describe('AddressBookController', () => { it('should not add invalid contact entry', () => { const controller = new AddressBookController(); - controller.set('1337', 'foo'); + controller.set('0x01', 'foo'); expect(controller.state).toStrictEqual({ addressBook: {} }); }); @@ -205,7 +205,7 @@ describe('AddressBookController', () => { it('should return false to indicate an address book entry has NOT been added', () => { const controller = new AddressBookController(); - expect(controller.set('1337', 'foo')).toStrictEqual(false); + expect(controller.set('0x00', 'foo')).toStrictEqual(false); }); it('should return true to indicate an address book entry has been deleted', () => { @@ -218,8 +218,8 @@ describe('AddressBookController', () => { it('should return false to indicate an address book entry has NOT been deleted', () => { const controller = new AddressBookController(); - controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo'); - expect(controller.delete('1', 'bar')).toStrictEqual(false); + controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', '0x00'); + expect(controller.delete('1', '0x01')).toStrictEqual(false); }); it('should normalize addresses so adding and removing entries work across casings', () => { diff --git a/src/user/AddressBookController.ts b/src/user/AddressBookController.ts index 21783f0c4e2..38026a59fb0 100644 --- a/src/user/AddressBookController.ts +++ b/src/user/AddressBookController.ts @@ -1,5 +1,8 @@ -import { isValidAddress, toChecksumAddress } from 'ethereumjs-util'; -import { normalizeEnsName } from '../util'; +import { + normalizeEnsName, + isValidHexAddress, + toChecksumHexAddress, +} from '../util'; import BaseController, { BaseConfig, BaseState } from '../BaseController'; /** @@ -87,9 +90,9 @@ export class AddressBookController extends BaseController< * @param address - Recipient address to delete */ delete(chainId: string, address: string) { - address = toChecksumAddress(address); + address = toChecksumHexAddress(address); if ( - !isValidAddress(address) || + !isValidHexAddress(address) || !this.state.addressBook[chainId] || !this.state.addressBook[chainId][address] ) { @@ -117,8 +120,8 @@ export class AddressBookController extends BaseController< * @returns - Boolean indicating if the address was successfully set */ set(address: string, name: string, chainId = '1', memo = '') { - address = toChecksumAddress(address); - if (!isValidAddress(address)) { + address = toChecksumHexAddress(address); + if (!isValidHexAddress(address)) { return false; } diff --git a/src/user/PreferencesController.test.ts b/src/user/PreferencesController.test.ts index 9e473133cc3..a5626e60a04 100644 --- a/src/user/PreferencesController.test.ts +++ b/src/user/PreferencesController.test.ts @@ -15,78 +15,66 @@ describe('PreferencesController', () => { it('should add identities', () => { const controller = new PreferencesController(); - controller.addIdentities(['foo']); - controller.addIdentities(['foo']); - expect(controller.state.identities['0xfoO'].address).toStrictEqual('0xfoO'); - expect(controller.state.identities['0xfoO'].name).toStrictEqual( - 'Account 1', - ); - expect(controller.state.identities['0xfoO'].importTime).toBeLessThanOrEqual( + controller.addIdentities(['0x00']); + controller.addIdentities(['0x00']); + expect(controller.state.identities['0x00'].address).toStrictEqual('0x00'); + expect(controller.state.identities['0x00'].name).toStrictEqual('Account 1'); + expect(controller.state.identities['0x00'].importTime).toBeLessThanOrEqual( Date.now(), ); }); it('should remove identity', () => { const controller = new PreferencesController(); - controller.addIdentities(['foo', 'bar', 'baz']); - controller.update({ selectedAddress: '0xfoO' }); - controller.removeIdentity('foo'); - controller.removeIdentity('baz'); - controller.removeIdentity('foo'); - expect(typeof controller.state.identities['0xfoO']).toBe('undefined'); - expect(controller.state.selectedAddress).toBe('0xbar'); + controller.addIdentities(['0x00', '0x01', '0x02']); + controller.update({ selectedAddress: '0x00' }); + controller.removeIdentity('0x00'); + controller.removeIdentity('0x02'); + controller.removeIdentity('0x00'); + expect(typeof controller.state.identities['0x00']).toBe('undefined'); + expect(controller.state.selectedAddress).toBe('0x01'); }); it('should set identity label', () => { const controller = new PreferencesController(); - controller.addIdentities(['foo']); - controller.setAccountLabel('foo', 'bar'); - controller.setAccountLabel('baz', 'qux'); - expect(controller.state.identities['0xfoO'].name).toBe('bar'); - expect(controller.state.identities['0xBaZ'].name).toBe('qux'); + controller.addIdentities(['0x00']); + controller.setAccountLabel('0x00', 'bar'); + controller.setAccountLabel('0x01', 'qux'); + expect(controller.state.identities['0x00'].name).toBe('bar'); + expect(controller.state.identities['0x01'].name).toBe('qux'); }); it('should sync identities', () => { const controller = new PreferencesController(); - controller.addIdentities(['foo', 'bar']); - controller.syncIdentities(['foo', 'bar']); - expect(controller.state.identities['0xfoO'].address).toStrictEqual('0xfoO'); - expect(controller.state.identities['0xfoO'].name).toStrictEqual( - 'Account 1', - ); - expect(controller.state.identities['0xfoO'].importTime).toBeLessThanOrEqual( + controller.addIdentities(['0x00', '0x01']); + controller.syncIdentities(['0x00', '0x01']); + expect(controller.state.identities['0x00'].address).toStrictEqual('0x00'); + expect(controller.state.identities['0x00'].name).toStrictEqual('Account 1'); + expect(controller.state.identities['0x00'].importTime).toBeLessThanOrEqual( Date.now(), ); - expect(controller.state.identities['0xbar'].address).toStrictEqual('0xbar'); - expect(controller.state.identities['0xbar'].name).toStrictEqual( - 'Account 2', - ); - expect(controller.state.identities['0xbar'].importTime).toBeLessThanOrEqual( + expect(controller.state.identities['0x01'].address).toStrictEqual('0x01'); + expect(controller.state.identities['0x01'].name).toStrictEqual('Account 2'); + expect(controller.state.identities['0x01'].importTime).toBeLessThanOrEqual( Date.now(), ); - controller.syncIdentities(['foo']); - expect(controller.state.identities['0xfoO'].address).toStrictEqual('0xfoO'); - expect(controller.state.identities['0xfoO'].name).toStrictEqual( - 'Account 1', - ); - expect(controller.state.selectedAddress).toBe('0xfoO'); + controller.syncIdentities(['0x00']); + expect(controller.state.identities['0x00'].address).toStrictEqual('0x00'); + expect(controller.state.identities['0x00'].name).toStrictEqual('Account 1'); + expect(controller.state.selectedAddress).toBe('0x00'); }); it('should add new identities', () => { const controller = new PreferencesController(); - controller.updateIdentities(['foo', 'bar']); - expect(controller.state.identities['0xfoO'].address).toStrictEqual('0xfoO'); - expect(controller.state.identities['0xfoO'].name).toStrictEqual( - 'Account 1', - ); - expect(controller.state.identities['0xfoO'].importTime).toBeLessThanOrEqual( + controller.updateIdentities(['0x00', '0x01']); + expect(controller.state.identities['0x00'].address).toStrictEqual('0x00'); + expect(controller.state.identities['0x00'].name).toStrictEqual('Account 1'); + expect(controller.state.identities['0x00'].importTime).toBeLessThanOrEqual( Date.now(), ); - expect(controller.state.identities['0xbar'].address).toStrictEqual('0xbar'); - expect(controller.state.identities['0xbar'].name).toStrictEqual( - 'Account 2', - ); - expect(controller.state.identities['0xbar'].importTime).toBeLessThanOrEqual( + expect(controller.state.identities['0x01'].address).toStrictEqual('0x01'); + expect(controller.state.identities['0x01'].name).toStrictEqual('Account 2'); + expect(controller.state.identities['0x01'].importTime).toBeLessThanOrEqual( Date.now(), ); }); @@ -94,21 +82,19 @@ describe('PreferencesController', () => { it('should not update existing identities', () => { const controller = new PreferencesController( {}, - { identities: { '0xbar': { address: '0xbar', name: 'Custom name' } } }, - ); - controller.updateIdentities(['foo', 'bar']); - expect(controller.state.identities['0xfoO'].address).toStrictEqual('0xfoO'); - expect(controller.state.identities['0xfoO'].name).toStrictEqual( - 'Account 1', + { identities: { '0x01': { address: '0x01', name: 'Custom name' } } }, ); - expect(controller.state.identities['0xfoO'].importTime).toBeLessThanOrEqual( + controller.updateIdentities(['0x00', '0x01']); + expect(controller.state.identities['0x00'].address).toStrictEqual('0x00'); + expect(controller.state.identities['0x00'].name).toStrictEqual('Account 1'); + expect(controller.state.identities['0x00'].importTime).toBeLessThanOrEqual( Date.now(), ); - expect(controller.state.identities['0xbar'].address).toStrictEqual('0xbar'); - expect(controller.state.identities['0xbar'].name).toStrictEqual( + expect(controller.state.identities['0x01'].address).toStrictEqual('0x01'); + expect(controller.state.identities['0x01'].name).toStrictEqual( 'Custom name', ); - expect(controller.state.identities['0xbar'].importTime).toBeUndefined(); + expect(controller.state.identities['0x01'].importTime).toBeUndefined(); }); it('should remove identities', () => { @@ -116,14 +102,14 @@ describe('PreferencesController', () => { {}, { identities: { - '0xbar': { address: '0xbar', name: 'Account 2' }, - '0xfoO': { address: '0xfoO', name: 'Account 1' }, + '0x01': { address: '0x01', name: 'Account 2' }, + '0x00': { address: '0x00', name: 'Account 1' }, }, }, ); - controller.updateIdentities(['foo']); + controller.updateIdentities(['0x00']); expect(controller.state.identities).toStrictEqual({ - '0xfoO': { address: '0xfoO', name: 'Account 1' }, + '0x00': { address: '0x00', name: 'Account 1' }, }); }); @@ -132,14 +118,14 @@ describe('PreferencesController', () => { {}, { identities: { - '0xbar': { address: '0xbar', name: 'Account 2' }, - '0xfoO': { address: '0xfoO', name: 'Account 1' }, + '0x01': { address: '0x01', name: 'Account 2' }, + '0x00': { address: '0x00', name: 'Account 1' }, }, - selectedAddress: '0xbar', + selectedAddress: '0x01', }, ); - controller.updateIdentities(['foo', 'bar']); - expect(controller.state.selectedAddress).toStrictEqual('0xbar'); + controller.updateIdentities(['0x00', '0x01']); + expect(controller.state.selectedAddress).toStrictEqual('0x01'); }); it('should update selected address to first identity if it was removed from identities', () => { @@ -147,15 +133,15 @@ describe('PreferencesController', () => { {}, { identities: { - '0xbar': { address: '0xbar', name: 'Account 2' }, - '0xbaz': { address: '0xbaz', name: 'Account 3' }, - '0xfoO': { address: '0xfoO', name: 'Account 1' }, + '0x01': { address: '0x01', name: 'Account 2' }, + '0x02': { address: '0x02', name: 'Account 3' }, + '0x00': { address: '0x00', name: 'Account 1' }, }, - selectedAddress: '0xbaz', + selectedAddress: '0x02', }, ); - controller.updateIdentities(['foo', 'bar']); - expect(controller.state.selectedAddress).toStrictEqual('0xfoO'); + controller.updateIdentities(['0x00', '0x01']); + expect(controller.state.selectedAddress).toStrictEqual('0x00'); }); it('should add custom rpc url', () => { diff --git a/src/user/PreferencesController.ts b/src/user/PreferencesController.ts index df0b239e4d8..284121ba396 100644 --- a/src/user/PreferencesController.ts +++ b/src/user/PreferencesController.ts @@ -1,5 +1,5 @@ -import { toChecksumAddress } from 'ethereumjs-util'; import BaseController, { BaseConfig, BaseState } from '../BaseController'; +import { toChecksumHexAddress } from '../util'; import { ContactEntry } from './AddressBookController'; /** @@ -87,7 +87,7 @@ export class PreferencesController extends BaseController< addIdentities(addresses: string[]) { const { identities } = this.state; addresses.forEach((address) => { - address = toChecksumAddress(address); + address = toChecksumHexAddress(address); if (identities[address]) { return; } @@ -108,7 +108,7 @@ export class PreferencesController extends BaseController< * @param address - Address of the identity to remove */ removeIdentity(address: string) { - address = toChecksumAddress(address); + address = toChecksumHexAddress(address); const { identities } = this.state; if (!identities[address]) { return; @@ -127,7 +127,7 @@ export class PreferencesController extends BaseController< * @param label - New label to assign */ setAccountLabel(address: string, label: string) { - address = toChecksumAddress(address); + address = toChecksumHexAddress(address); const { identities } = this.state; identities[address] = identities[address] || {}; identities[address].name = label; @@ -153,7 +153,9 @@ export class PreferencesController extends BaseController< * @returns - Newly-selected address after syncing */ syncIdentities(addresses: string[]) { - addresses = addresses.map((address: string) => toChecksumAddress(address)); + addresses = addresses.map((address: string) => + toChecksumHexAddress(address), + ); const { identities, lostIdentities } = this.state; const newlyLost: { [address: string]: ContactEntry } = {}; @@ -191,7 +193,9 @@ export class PreferencesController extends BaseController< * @param addresses - List of addresses to use as a basis for each identity */ updateIdentities(addresses: string[]) { - addresses = addresses.map((address: string) => toChecksumAddress(address)); + addresses = addresses.map((address: string) => + toChecksumHexAddress(address), + ); const oldIdentities = this.state.identities; const identities = addresses.reduce( (ids: { [address: string]: ContactEntry }, address, index) => { @@ -268,7 +272,7 @@ export class PreferencesController extends BaseController< * @param selectedAddress - Ethereum address */ setSelectedAddress(selectedAddress: string) { - this.update({ selectedAddress: toChecksumAddress(selectedAddress) }); + this.update({ selectedAddress: toChecksumHexAddress(selectedAddress) }); } /** diff --git a/src/util.test.ts b/src/util.test.ts index 0fe0077dd32..c615a6b9642 100644 --- a/src/util.test.ts +++ b/src/util.test.ts @@ -5,6 +5,7 @@ import HttpProvider from 'ethjs-provider-http'; import EthQuery from 'eth-query'; import * as util from './util'; +const VALID = '4e1fF7229BDdAf0A73DF183a88d9c3a04cc975e0'; const SOME_API = 'https://someapi.com'; const SOME_FAILING_API = 'https://somefailingapi.com'; @@ -192,6 +193,30 @@ describe('util', () => { }); }); + describe('toChecksumHexAddress', () => { + const fullAddress = `0x${VALID}`; + it('should return address for valid address', () => { + expect(util.toChecksumHexAddress(fullAddress)).toBe(fullAddress); + }); + it('should return address for non prefix address', () => { + expect(util.toChecksumHexAddress(VALID)).toBe(fullAddress); + }); + }); + + describe('isValidHexAddress', () => { + it('should return true for valid address', () => { + expect(util.isValidHexAddress(VALID)).toBe(true); + }); + it('should return false for invalid address', () => { + expect(util.isValidHexAddress('0x00')).toBe(false); + }); + it('should allow allowNonPrefixed to be false', () => { + expect(util.isValidHexAddress('0x00', { allowNonPrefixed: false })).toBe( + false, + ); + }); + }); + describe('validateTransaction', () => { it('should throw if no from address', () => { expect(() => util.validateTransaction({} as any)).toThrow( @@ -314,11 +339,9 @@ describe('util', () => { expect(() => util.validateSignMessageData({ data: '0x879a05', - from: '3244e191f1b4903970224322180f1fbbc415696b', + from: '01', } as any), - ).toThrow( - 'Invalid "from" address: 3244e191f1b4903970224322180f1fbbc415696b must be a valid string.', - ); + ).toThrow('Invalid "from" address: 01 must be a valid string.'); }); it('should throw if invalid type from address', () => { @@ -363,7 +386,7 @@ describe('util', () => { data: [], from: '3244e191f1b4903970224322180f1fbbc415696b', } as any), - ).toThrow('Invalid "from" address:'); + ).toThrow('Expected EIP712 typed data.'); }); it('should throw if invalid type from address', () => { @@ -420,7 +443,7 @@ describe('util', () => { data: '0x879a05', from: '3244e191f1b4903970224322180f1fbbc415696b', } as any), - ).toThrow('Invalid "from" address:'); + ).toThrow('Data must be passed as a valid JSON string.'); }); it('should throw if invalid type from address', () => { diff --git a/src/util.ts b/src/util.ts index 7e25b1d8fac..2aea476b9a5 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,4 +1,11 @@ -import { addHexPrefix, isValidAddress, bufferToHex, BN } from 'ethereumjs-util'; +import { + addHexPrefix, + isValidAddress, + isHexString, + bufferToHex, + BN, + toChecksumAddress, +} from 'ethereumjs-util'; import { stripHexPrefix } from 'ethjs-util'; import { ethErrors } from 'eth-rpc-errors'; import ensNamehash from 'eth-ens-namehash'; @@ -271,6 +278,46 @@ export async function safelyExecuteWithTimeout( } } +export function toChecksumHexAddress(address: string) { + const hexPrefixed = addHexPrefix(address); + if (!isHexString(hexPrefixed)) { + // Version 5.1 of ethereumjs-utils would have returned '0xY' for input 'y' + // but we shouldn't waste effort trying to change case on a clearly invalid + // string. Instead just return the hex prefixed original string which most + // closely mimics the original behavior. + return hexPrefixed; + } + return toChecksumAddress(hexPrefixed); +} + +/** + * Validates that the input is a hex address. This utility method is a thin + * wrapper around ethereumjs-util.isValidAddress, with the exception that it + * does not throw an error when provided values that are not hex strings. In + * addition, and by default, this method will return true for hex strings that + * meet the length requirement of a hex address, but are not prefixed with `0x` + * Finally, if the mixedCaseUseChecksum flag is true and a mixed case string is + * provided this method will validate it has the proper checksum formatting. + * @param {string} possibleAddress - Input parameter to check against + * @param {Object} [options] - options bag + * @param {boolean} [options.allowNonPrefixed] - If true will first ensure '0x' + * is prepended to the string + * @returns {boolean} whether or not the input is a valid hex address + */ +export function isValidHexAddress( + possibleAddress: string, + { allowNonPrefixed = true } = {}, +) { + const addressToCheck = allowNonPrefixed + ? addHexPrefix(possibleAddress) + : possibleAddress; + if (!isHexString(addressToCheck)) { + return false; + } + + return isValidAddress(addressToCheck); +} + /** * Validates a Transaction object for required properties and throws in * the event of any validation error. @@ -281,7 +328,7 @@ export function validateTransaction(transaction: Transaction) { if ( !transaction.from || typeof transaction.from !== 'string' || - !isValidAddress(transaction.from) + !isValidHexAddress(transaction.from) ) { throw new Error( `Invalid "from" address: ${transaction.from} must be a valid string.`, @@ -295,7 +342,10 @@ export function validateTransaction(transaction: Transaction) { `Invalid "to" address: ${transaction.to} must be a valid string.`, ); } - } else if (transaction.to !== undefined && !isValidAddress(transaction.to)) { + } else if ( + transaction.to !== undefined && + !isValidHexAddress(transaction.to) + ) { throw new Error( `Invalid "to" address: ${transaction.to} must be a valid string.`, ); @@ -353,19 +403,12 @@ export function normalizeMessageData(data: string) { export function validateSignMessageData( messageData: PersonalMessageParams | MessageParams, ) { - if ( - !messageData.from || - typeof messageData.from !== 'string' || - !isValidAddress(messageData.from) - ) { - throw new Error( - `Invalid "from" address: ${messageData.from} must be a valid string.`, - ); + const { from, data } = messageData; + if (!from || typeof from !== 'string' || !isValidHexAddress(from)) { + throw new Error(`Invalid "from" address: ${from} must be a valid string.`); } - if (!messageData.data || typeof messageData.data !== 'string') { - throw new Error( - `Invalid message "data": ${messageData.data} must be a valid string.`, - ); + if (!data || typeof data !== 'string') { + throw new Error(`Invalid message "data": ${data} must be a valid string.`); } } @@ -382,7 +425,7 @@ export function validateTypedSignMessageDataV1( if ( !messageData.from || typeof messageData.from !== 'string' || - !isValidAddress(messageData.from) + !isValidHexAddress(messageData.from) ) { throw new Error( `Invalid "from" address: ${messageData.from} must be a valid string.`, @@ -413,7 +456,7 @@ export function validateTypedSignMessageDataV3( if ( !messageData.from || typeof messageData.from !== 'string' || - !isValidAddress(messageData.from) + !isValidHexAddress(messageData.from) ) { throw new Error( `Invalid "from" address: ${messageData.from} must be a valid string.`, @@ -464,7 +507,7 @@ export function validateTokenToWatch(token: Token) { `Invalid decimals "${decimals}": must be 0 <= 36.`, ); } - if (!isValidAddress(address)) { + if (!isValidHexAddress(address)) { throw ethErrors.rpc.invalidParams(`Invalid address "${address}".`); } } diff --git a/yarn.lock b/yarn.lock index 7d949c6b33a..71d0ce0328c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2772,7 +2772,7 @@ ethereumjs-util@^5.0.0, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2, ethereum safe-buffer "^5.1.1" secp256k1 "^3.0.1" -ethereumjs-util@^6.0.0, ethereumjs-util@^6.1.0: +ethereumjs-util@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz#e9c51e5549e8ebd757a339cc00f5380507e799c8" integrity sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q== @@ -2785,24 +2785,24 @@ ethereumjs-util@^6.0.0, ethereumjs-util@^6.1.0: safe-buffer "^5.1.1" secp256k1 "^3.0.1" -ethereumjs-util@^7.0.2: - version "7.0.8" - resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.0.8.tgz#5258762b7b17e3d828e41834948363ff0a703ffd" - integrity sha512-JJt7tDpCAmDPw/sGoFYeq0guOVqT3pTE9xlEbBmc/nlCij3JRCoS2c96SQ6kXVHOT3xWUNLDm5QCJLQaUnVAtQ== +ethereumjs-util@^7.0.10, ethereumjs-util@^7.0.9: + version "7.0.10" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.0.10.tgz#5fb7b69fa1fda0acc59634cf39d6b0291180fc1f" + integrity sha512-c/xThw6A+EAnej5Xk5kOzFzyoSnw0WX0tSlZ6pAsfGVvQj3TItaDg9b1+Fz1RJXA+y2YksKwQnuzgt1eY6LKzw== dependencies: - "@types/bn.js" "^4.11.3" + "@types/bn.js" "^5.1.0" bn.js "^5.1.2" create-hash "^1.1.2" ethereum-cryptography "^0.1.3" ethjs-util "0.1.6" rlp "^2.2.4" -ethereumjs-util@^7.0.9: - version "7.0.10" - resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.0.10.tgz#5fb7b69fa1fda0acc59634cf39d6b0291180fc1f" - integrity sha512-c/xThw6A+EAnej5Xk5kOzFzyoSnw0WX0tSlZ6pAsfGVvQj3TItaDg9b1+Fz1RJXA+y2YksKwQnuzgt1eY6LKzw== +ethereumjs-util@^7.0.2: + version "7.0.8" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.0.8.tgz#5258762b7b17e3d828e41834948363ff0a703ffd" + integrity sha512-JJt7tDpCAmDPw/sGoFYeq0guOVqT3pTE9xlEbBmc/nlCij3JRCoS2c96SQ6kXVHOT3xWUNLDm5QCJLQaUnVAtQ== dependencies: - "@types/bn.js" "^5.1.0" + "@types/bn.js" "^4.11.3" bn.js "^5.1.2" create-hash "^1.1.2" ethereum-cryptography "^0.1.3"