diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cb28e93bc..65d0a1f87b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +## [6.0.0] - 2021-01-19 + +### Changed + +- Remove default approval controller type ([#321](https://github.com/MetaMask/controllers/pull/321)) + +### Fixed + +- Enforce the usage of `chainId` instead of `networkId` in `NetworkController` ([#324](https://github.com/MetaMask/controllers/pull/324)) + ## [5.1.0] - 2020-12-02 ### Changed @@ -151,7 +161,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Remove shapeshift controller (#209) -[Unreleased]:https://github.com/MetaMask/controllers/compare/v5.1.0...HEAD +[Unreleased]:https://github.com/MetaMask/controllers/compare/v6.0.0...HEAD +[6.0.0]:https://github.com/MetaMask/controllers/compare/v5.1.0...v6.0.0 [5.1.0]:https://github.com/MetaMask/controllers/compare/v5.0.0...v5.1.0 [5.0.0]:https://github.com/MetaMask/controllers/compare/v4.2.0...v5.0.0 [4.2.0]:https://github.com/MetaMask/controllers/compare/v4.1.0...v4.2.0 diff --git a/package.json b/package.json index dfc50a9e71..13a9ea9d54 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@metamask/controllers", - "version": "5.1.0", + "version": "6.0.0", "description": "Collection of platform-agnostic modules for creating secure data models for cryptocurrency wallets", "license": "MIT", "files": [ diff --git a/src/approval/ApprovalController.ts b/src/approval/ApprovalController.ts index 9d4775736b..a3c5a45397 100644 --- a/src/approval/ApprovalController.ts +++ b/src/approval/ApprovalController.ts @@ -44,7 +44,6 @@ export interface Approval { } export interface ApprovalConfig extends BaseConfig { - defaultApprovalType: string; showApprovalRequest: () => void; } @@ -70,8 +69,6 @@ const defaultState: ApprovalState = { [APPROVALS_STORE_KEY]: {}, [APPROVAL_COUNT */ export class ApprovalController extends BaseController { - public readonly defaultApprovalType: string; - private _approvals: Map; private _origins: Map>; @@ -80,22 +77,17 @@ export class ApprovalController extends BaseController { const promise = this._add(opts.origin, opts.type, opts.id, opts.requestData); @@ -141,8 +132,7 @@ export class ApprovalController extends BaseController { return this._add(opts.origin, opts.type, opts.id, opts.requestData); @@ -315,7 +305,7 @@ export class ApprovalController extends BaseController { @@ -357,7 +347,7 @@ export class ApprovalController extends BaseController transactionID === id); const transactionMeta = transactions[index]; const { from } = transactionMeta.transaction; @@ -480,12 +483,15 @@ export class TransactionController extends BaseController Promise.all( @@ -726,7 +732,7 @@ export class TransactionController extends BaseController networkID !== currentNetworkID); this.update({ transactions: newTransactions }); } diff --git a/tests/ApprovalController.test.js b/tests/ApprovalController.test.js index 2e5318c592..2b92d1b046 100644 --- a/tests/ApprovalController.test.js +++ b/tests/ApprovalController.test.js @@ -2,7 +2,6 @@ const { errorCodes } = require('eth-rpc-errors'); const ApprovalController = require('../dist/approval/ApprovalController').default; const defaultConfig = { - defaultApprovalType: 'DEFAULT_TYPE', showApprovalRequest: () => undefined, }; @@ -13,16 +12,8 @@ const STORE_KEY = 'pendingApprovals'; describe('ApprovalController: Input Validation', () => { describe('constructor', () => { it('throws on invalid input', () => { - expect(() => new ApprovalController({})).toThrow(getInvalidDefaultTypeError()); - expect(() => new ApprovalController({ showApprovalRequest: () => undefined })).toThrow( - getInvalidDefaultTypeError(), - ); - expect(() => new ApprovalController({ defaultApprovalType: 2 })).toThrow(getInvalidDefaultTypeError()); - - expect(() => new ApprovalController({ defaultApprovalType: 'foo' })).toThrow( - getInvalidShowApprovalRequestError(), - ); - expect(() => new ApprovalController({ defaultApprovalType: 'foo', showApprovalRequest: 'bar' })).toThrow( + expect(() => new ApprovalController({})).toThrow(getInvalidShowApprovalRequestError()); + expect(() => new ApprovalController({ showApprovalRequest: 'bar' })).toThrow( getInvalidShowApprovalRequestError(), ); }); @@ -50,6 +41,7 @@ describe('ApprovalController: Input Validation', () => { approvalController.add({ id: 'foo', origin: 'bar.baz', + type: 'type', requestData: 'foo', }), ).toThrow(getInvalidRequestDataError()); @@ -60,7 +52,7 @@ describe('ApprovalController: Input Validation', () => { it('returns undefined for non-existing entry', () => { const approvalController = getApprovalController(); - approvalController.add({ id: 'foo', origin: 'bar.baz' }); + approvalController.add({ id: 'foo', origin: 'bar.baz', type: 'type' }); expect(approvalController.get('fizz')).toBeUndefined(); @@ -103,25 +95,26 @@ describe('ApprovalController: Input Validation', () => { }); it('deletes entry', () => { - approvalController.add({ id: 'foo', origin: 'bar.baz' }); + approvalController.add({ id: 'foo', origin: 'bar.baz', type: 'type' }); approvalController._delete('foo'); expect( !approvalController.has({ id: 'foo' }) && + !approvalController.has({ type: 'type' }) && !approvalController.has({ origin: 'bar.baz' }) && !approvalController.state[STORE_KEY].foo, ).toEqual(true); }); it('deletes one entry out of many without side-effects', () => { - approvalController.add({ id: 'foo', origin: 'bar.baz' }); - approvalController.add({ id: 'fizz', origin: 'bar.baz', type: 'myType' }); + approvalController.add({ id: 'foo', origin: 'bar.baz', type: 'type1' }); + approvalController.add({ id: 'fizz', origin: 'bar.baz', type: 'type2' }); approvalController._delete('fizz'); expect( - !approvalController.has({ id: 'fizz' }) && !approvalController.has({ origin: 'bar.baz', type: 'myType' }), + !approvalController.has({ id: 'fizz' }) && !approvalController.has({ origin: 'bar.baz', type: 'type2' }), ).toEqual(true); expect(approvalController.has({ id: 'foo' }) && approvalController.has({ origin: 'bar.baz' })).toEqual(true); @@ -138,10 +131,6 @@ describe('ApprovalController: Input Validation', () => { // helpers -function getInvalidDefaultTypeError() { - return getError('Must specify non-empty string defaultApprovalType.'); -} - function getInvalidShowApprovalRequestError() { return getError('Must specify function showApprovalRequest.'); } @@ -171,7 +160,7 @@ function getInvalidRequestDataError() { } function getInvalidTypeError(code) { - return getError('May not specify empty or non-string type.', code); + return getError('Must specify non-empty string type.', code); } function getInvalidHasParamsError() { diff --git a/tests/ApprovalController.test.ts b/tests/ApprovalController.test.ts index daddab3802..4f55c698aa 100644 --- a/tests/ApprovalController.test.ts +++ b/tests/ApprovalController.test.ts @@ -5,10 +5,9 @@ const sinon = require('sinon'); const STORE_KEY = 'pendingApprovals'; -const DEFAULT_TYPE = 'DEFAULT_TYPE'; +const TYPE = 'TYPE'; const defaultConfig = { - defaultApprovalType: DEFAULT_TYPE, showApprovalRequest: () => undefined, }; @@ -24,58 +23,45 @@ describe('approval controller', () => { }); it('adds correctly specified entry', () => { - expect(() => approvalController.add({ id: 'foo', origin: 'bar.baz' })).not.toThrow(); + expect(() => approvalController.add({ id: 'foo', origin: 'bar.baz', type: TYPE })).not.toThrow(); expect(approvalController.has({ id: 'foo' })).toEqual(true); + expect(approvalController.has({ origin: 'bar.baz', type: TYPE })).toEqual(true); expect(approvalController.state[STORE_KEY]).toEqual({ - foo: { id: 'foo', origin: 'bar.baz', time: 1, type: DEFAULT_TYPE }, + foo: { id: 'foo', origin: 'bar.baz', time: 1, type: TYPE }, }); }); it('adds id if non provided', () => { - expect(() => approvalController.add({ id: undefined, origin: 'bar.baz' })).not.toThrow(); + expect(() => approvalController.add({ id: undefined, origin: 'bar.baz', type: TYPE })).not.toThrow(); const id = Object.keys(approvalController.state[STORE_KEY])[0]; expect(id && typeof id === 'string').toBeTruthy(); }); - it('adds correctly specified entry with custom type', () => { - expect(() => approvalController.add({ id: 'foo', origin: 'bar.baz', type: 'myType' })).not.toThrow(); - - expect(approvalController.has({ id: 'foo' })).toEqual(true); - expect(approvalController.has({ origin: 'bar.baz', type: 'myType' })).toEqual(true); - expect(approvalController.state[STORE_KEY]).toEqual({ - foo: { id: 'foo', origin: 'bar.baz', type: 'myType', time: 1 }, - }); - }); - it('adds correctly specified entry with request data', () => { expect(() => approvalController.add({ id: 'foo', origin: 'bar.baz', - type: undefined, + type: 'myType', requestData: { foo: 'bar' }, }), ).not.toThrow(); expect(approvalController.has({ id: 'foo' })).toEqual(true); expect(approvalController.has({ origin: 'bar.baz' })).toEqual(true); + expect(approvalController.has({ type: 'myType' })).toEqual(true); expect(approvalController.state[STORE_KEY].foo.requestData).toEqual({ foo: 'bar' }); }); it('adds multiple entries for same origin with different types and ids', () => { const ORIGIN = 'bar.baz'; - expect(() => approvalController.add({ id: 'foo1', origin: ORIGIN })).not.toThrow(); - expect(() => approvalController.add({ id: 'foo2', origin: ORIGIN, type: 'myType1' })).not.toThrow(); - expect(() => approvalController.add({ id: 'foo3', origin: ORIGIN, type: 'myType2' })).not.toThrow(); + expect(() => approvalController.add({ id: 'foo1', origin: ORIGIN, type: 'myType1' })).not.toThrow(); + expect(() => approvalController.add({ id: 'foo2', origin: ORIGIN, type: 'myType2' })).not.toThrow(); - expect( - approvalController.has({ id: 'foo1' }) && - approvalController.has({ id: 'foo3' }) && - approvalController.has({ id: 'foo3' }), - ).toEqual(true); + expect(approvalController.has({ id: 'foo1' }) && approvalController.has({ id: 'foo2' })).toEqual(true); expect( approvalController.has({ origin: ORIGIN }) && approvalController.has({ origin: ORIGIN, type: 'myType1' }) && @@ -84,20 +70,14 @@ describe('approval controller', () => { }); it('throws on id collision', () => { - expect(() => approvalController.add({ id: 'foo', origin: 'bar.baz' })).not.toThrow(); - - expect(() => approvalController.add({ id: 'foo', origin: 'fizz.buzz' })).toThrow(getIdCollisionError('foo')); - }); + expect(() => approvalController.add({ id: 'foo', origin: 'bar.baz', type: TYPE })).not.toThrow(); - it('throws on origin and default type collision', () => { - expect(() => approvalController.add({ id: 'foo', origin: 'bar.baz' })).not.toThrow(); - - expect(() => approvalController.add({ id: 'foo1', origin: 'bar.baz' })).toThrow( - getOriginTypeCollisionError('bar.baz'), + expect(() => approvalController.add({ id: 'foo', origin: 'fizz.buzz', type: TYPE })).toThrow( + getIdCollisionError('foo'), ); }); - it('throws on origin and custom type collision', () => { + it('throws on origin and type collision', () => { expect(() => approvalController.add({ id: 'foo', origin: 'bar.baz', type: 'myType' })).not.toThrow(); expect(() => approvalController.add({ id: 'foo1', origin: 'bar.baz', type: 'myType' })).toThrow( @@ -127,21 +107,9 @@ describe('approval controller', () => { }); describe('get', () => { - let approvalController: ApprovalController; - - beforeEach(() => { - approvalController = new ApprovalController({ ...defaultConfig }); - }); - - it('gets entry with default type', () => { - approvalController.add({ id: 'foo', origin: 'bar.baz' }); - - expect(approvalController.get('foo')).toEqual({ id: 'foo', origin: 'bar.baz', time: 1, type: DEFAULT_TYPE }); - }); - - it('gets entry with custom type', () => { + it('gets entry', () => { + const approvalController = new ApprovalController({ ...defaultConfig }); approvalController.add({ id: 'foo', origin: 'bar.baz', type: 'myType' }); - expect(approvalController.get('foo')).toEqual({ id: 'foo', origin: 'bar.baz', type: 'myType', time: 1 }); }); }); @@ -156,25 +124,25 @@ describe('approval controller', () => { }); it('gets the count when specifying origin and type', () => { - addWithCatch({ id: '1', origin: 'origin1' }); + addWithCatch({ id: '1', origin: 'origin1', type: TYPE }); addWithCatch({ id: '2', origin: 'origin1', type: 'type1' }); addWithCatch({ id: '3', origin: 'origin2', type: 'type1' }); - expect(approvalController.getApprovalCount({ origin: 'origin1', type: DEFAULT_TYPE })).toEqual(1); + expect(approvalController.getApprovalCount({ origin: 'origin1', type: TYPE })).toEqual(1); expect(approvalController.getApprovalCount({ origin: 'origin1', type: 'type1' })).toEqual(1); expect(approvalController.getApprovalCount({ origin: 'origin1', type: 'type2' })).toEqual(0); - expect(approvalController.getApprovalCount({ origin: 'origin2', type: DEFAULT_TYPE })).toEqual(0); + expect(approvalController.getApprovalCount({ origin: 'origin2', type: TYPE })).toEqual(0); expect(approvalController.getApprovalCount({ origin: 'origin2', type: 'type1' })).toEqual(1); expect(approvalController.getApprovalCount({ origin: 'origin2', type: 'type2' })).toEqual(0); - expect(approvalController.getApprovalCount({ origin: 'origin3', type: DEFAULT_TYPE })).toEqual(0); + expect(approvalController.getApprovalCount({ origin: 'origin3', type: TYPE })).toEqual(0); expect(approvalController.getApprovalCount({ origin: 'origin3', type: 'type1' })).toEqual(0); expect(approvalController.getApprovalCount({ origin: 'origin3', type: 'type2' })).toEqual(0); }); it('gets the count when specifying origin only', () => { - addWithCatch({ id: '1', origin: 'origin1' }); + addWithCatch({ id: '1', origin: 'origin1', type: 'type0' }); addWithCatch({ id: '2', origin: 'origin1', type: 'type1' }); addWithCatch({ id: '3', origin: 'origin2', type: 'type1' }); @@ -186,13 +154,10 @@ describe('approval controller', () => { }); it('gets the count when specifying type only', () => { - addWithCatch({ id: '1', origin: 'origin1' }); addWithCatch({ id: '2', origin: 'origin1', type: 'type1' }); addWithCatch({ id: '3', origin: 'origin2', type: 'type1' }); addWithCatch({ id: '4', origin: 'origin2', type: 'type2' }); - expect(approvalController.getApprovalCount({ type: DEFAULT_TYPE })).toEqual(1); - expect(approvalController.getApprovalCount({ type: 'type1' })).toEqual(2); expect(approvalController.getApprovalCount({ type: 'type2' })).toEqual(1); @@ -208,7 +173,7 @@ describe('approval controller', () => { const addWithCatch = (args: any) => approvalController.add(args).catch(() => undefined); - addWithCatch({ id: '1', origin: 'origin1' }); + addWithCatch({ id: '1', origin: 'origin1', type: 'type0' }); expect(approvalController.getTotalApprovalCount()).toEqual(1); addWithCatch({ id: '2', origin: 'origin1', type: 'type1' }); @@ -233,49 +198,43 @@ describe('approval controller', () => { }); it('returns true for existing entry by id', () => { - approvalController.add({ id: 'foo', origin: 'bar.baz' }); + approvalController.add({ id: 'foo', origin: 'bar.baz', type: TYPE }); expect(approvalController.has({ id: 'foo' })).toEqual(true); }); it('returns true for existing entry by origin', () => { - approvalController.add({ id: 'foo', origin: 'bar.baz' }); + approvalController.add({ id: 'foo', origin: 'bar.baz', type: TYPE }); expect(approvalController.has({ origin: 'bar.baz' })).toEqual(true); }); - it('returns true for existing entry by origin and custom type', () => { + it('returns true for existing entry by origin and type', () => { approvalController.add({ id: 'foo', origin: 'bar.baz', type: 'myType' }); expect(approvalController.has({ origin: 'bar.baz', type: 'myType' })).toEqual(true); }); - it('returns true for existing default type', () => { - approvalController.add({ id: 'foo', origin: 'bar.baz' }); - - expect(approvalController.has({ type: approvalController.defaultApprovalType })).toEqual(true); - }); - - it('returns true for existing custom type', () => { + it('returns true for existing type', () => { approvalController.add({ id: 'foo', origin: 'bar.baz', type: 'myType' }); expect(approvalController.has({ type: 'myType' })).toEqual(true); }); it('returns false for non-existing entry by id', () => { - approvalController.add({ id: 'foo', origin: 'bar.baz' }); + approvalController.add({ id: 'foo', origin: 'bar.baz', type: TYPE }); expect(approvalController.has({ id: 'fizz' })).toEqual(false); }); it('returns false for non-existing entry by origin', () => { - approvalController.add({ id: 'foo', origin: 'bar.baz' }); + approvalController.add({ id: 'foo', origin: 'bar.baz', type: TYPE }); expect(approvalController.has({ origin: 'fizz.buzz' })).toEqual(false); }); it('returns false for non-existing entry by existing origin and non-existing type', () => { - approvalController.add({ id: 'foo', origin: 'bar.baz' }); + approvalController.add({ id: 'foo', origin: 'bar.baz', type: TYPE }); expect(approvalController.has({ origin: 'bar.baz', type: 'myType' })).toEqual(false); }); @@ -286,16 +245,10 @@ describe('approval controller', () => { expect(approvalController.has({ origin: 'fizz.buzz', type: 'myType' })).toEqual(false); }); - it('returns false for non-existing entry by default type', () => { - approvalController.add({ id: 'foo', origin: 'bar.baz', type: 'myType' }); - - expect(approvalController.has({ type: approvalController.defaultApprovalType })).toEqual(false); - }); - - it('returns false for non-existing entry by custom type', () => { - approvalController.add({ id: 'foo', origin: 'bar.baz' }); + it('returns false for non-existing entry by type', () => { + approvalController.add({ id: 'foo', origin: 'bar.baz', type: 'myType1' }); - expect(approvalController.has({ type: 'myType' })).toEqual(false); + expect(approvalController.has({ type: 'myType2' })).toEqual(false); }); }); @@ -313,7 +266,7 @@ describe('approval controller', () => { it('resolves approval promise', async () => { numDeletions = 1; - const approvalPromise = approvalController.add({ id: 'foo', origin: 'bar.baz' }); + const approvalPromise = approvalController.add({ id: 'foo', origin: 'bar.baz', type: 'myType' }); approvalController.resolve('foo', 'success'); const result = await approvalPromise; @@ -324,7 +277,7 @@ describe('approval controller', () => { it('resolves multiple approval promises out of order', async () => { numDeletions = 2; - const approvalPromise1 = approvalController.add({ id: 'foo1', origin: 'bar.baz' }); + const approvalPromise1 = approvalController.add({ id: 'foo1', origin: 'bar.baz', type: 'myType1' }); const approvalPromise2 = approvalController.add({ id: 'foo2', origin: 'bar.baz', type: 'myType2' }); approvalController.resolve('foo2', 'success2'); @@ -359,7 +312,7 @@ describe('approval controller', () => { it('rejects approval promise', async () => { numDeletions = 1; - const approvalPromise = approvalController.add({ id: 'foo', origin: 'bar.baz' }).catch((error) => { + const approvalPromise = approvalController.add({ id: 'foo', origin: 'bar.baz', type: TYPE }).catch((error) => { expect(error).toMatchObject(getError('failure')); }); @@ -371,7 +324,7 @@ describe('approval controller', () => { it('rejects multiple approval promises out of order', async () => { numDeletions = 2; - const rejectionPromise1 = approvalController.add({ id: 'foo1', origin: 'bar.baz' }).catch((error) => { + const rejectionPromise1 = approvalController.add({ id: 'foo1', origin: 'bar.baz', type: TYPE }).catch((error) => { expect(error).toMatchObject(getError('failure1')); }); const rejectionPromise2 = approvalController @@ -398,9 +351,9 @@ describe('approval controller', () => { it('resolves and rejects multiple approval promises out of order', async () => { const approvalController = new ApprovalController({ ...defaultConfig }); - const promise1 = approvalController.add({ id: 'foo1', origin: 'bar.baz' }); + const promise1 = approvalController.add({ id: 'foo1', origin: 'bar.baz', type: TYPE }); const promise2 = approvalController.add({ id: 'foo2', origin: 'bar.baz', type: 'myType2' }); - const promise3 = approvalController.add({ id: 'foo3', origin: 'fizz.buzz' }).catch((error) => { + const promise3 = approvalController.add({ id: 'foo3', origin: 'fizz.buzz', type: TYPE }).catch((error) => { expect(error).toMatchObject(getError('failure3')); }); const promise4 = approvalController.add({ id: 'foo4', origin: 'bar.baz', type: 'myType4' }).catch((error) => { @@ -444,14 +397,13 @@ describe('approval controller', () => { it('deletes existing entries', async () => { const rejectSpy = sinon.spy(approvalController, 'reject'); - approvalController.add({ id: 'foo1', origin: 'bar.baz' }).catch((_error) => undefined); approvalController.add({ id: 'foo2', origin: 'bar.baz', type: 'myType' }).catch((_error) => undefined); approvalController.add({ id: 'foo3', origin: 'fizz.buzz', type: 'myType' }).catch((_error) => undefined); approvalController.clear(); expect(approvalController.state[STORE_KEY]).toEqual({}); - expect(rejectSpy.callCount).toEqual(3); + expect(rejectSpy.callCount).toEqual(2); }); }); }); @@ -462,7 +414,7 @@ function getIdCollisionError(id: string) { return getError(`Approval with id '${id}' already exists.`, errorCodes.rpc.internal); } -function getOriginTypeCollisionError(origin: string, type = DEFAULT_TYPE) { +function getOriginTypeCollisionError(origin: string, type = TYPE) { const message = `Request of type '${type}' already pending for origin ${origin}. Please wait.`; return getError(message, errorCodes.rpc.resourceUnavailable); } diff --git a/tests/AssetsController.test.ts b/tests/AssetsController.test.ts index cf2be59675..852960f8bb 100644 --- a/tests/AssetsController.test.ts +++ b/tests/AssetsController.test.ts @@ -3,7 +3,7 @@ import { getOnce } from 'fetch-mock'; import AssetsController from '../src/assets/AssetsController'; import ComposableController from '../src/ComposableController'; import PreferencesController from '../src/user/PreferencesController'; -import { NetworkController } from '../src/network/NetworkController'; +import { NetworkController, NetworksChainId } from '../src/network/NetworkController'; import { AssetsContractController } from '../src/assets/AssetsContractController'; const HttpProvider = require('ethjs-provider-http'); @@ -166,11 +166,11 @@ describe('AssetsController', () => { it('should add token by provider type', async () => { const firstNetworkType = 'rinkeby'; const secondNetworkType = 'ropsten'; - network.update({ provider: { type: firstNetworkType } }); + network.update({ provider: { type: firstNetworkType, chainId: NetworksChainId[firstNetworkType] } }); await assetsController.addToken('foo', 'bar', 2); - network.update({ provider: { type: secondNetworkType } }); + network.update({ provider: { type: secondNetworkType, chainId: NetworksChainId[secondNetworkType] } }); expect(assetsController.state.tokens).toHaveLength(0); - network.update({ provider: { type: firstNetworkType } }); + network.update({ provider: { type: firstNetworkType, chainId: NetworksChainId[firstNetworkType] } }); expect(assetsController.state.tokens[0]).toEqual({ address: '0xfoO', decimals: 2, @@ -204,13 +204,13 @@ describe('AssetsController', () => { it('should remove token by provider type', async () => { const firstNetworkType = 'rinkeby'; const secondNetworkType = 'ropsten'; - network.update({ provider: { type: firstNetworkType } }); + network.update({ provider: { type: firstNetworkType, chainId: NetworksChainId[firstNetworkType] } }); await assetsController.addToken('fou', 'baz', 2); - network.update({ provider: { type: secondNetworkType } }); + network.update({ provider: { type: secondNetworkType, chainId: NetworksChainId[secondNetworkType] } }); await assetsController.addToken('foo', 'bar', 2); assetsController.removeToken('0xfoO'); expect(assetsController.state.tokens).toHaveLength(0); - network.update({ provider: { type: firstNetworkType } }); + network.update({ provider: { type: firstNetworkType, chainId: NetworksChainId[firstNetworkType] } }); expect(assetsController.state.tokens[0]).toEqual({ address: '0xFOu', decimals: 2, @@ -310,11 +310,11 @@ describe('AssetsController', () => { sandbox .stub(assetsController, 'getCollectibleInformation' as any) .returns({ name: 'name', image: 'url', description: 'description' }); - network.update({ provider: { type: firstNetworkType } }); + network.update({ provider: { type: firstNetworkType, chainId: NetworksChainId[firstNetworkType] } }); await assetsController.addCollectible('foo', 1234); - network.update({ provider: { type: secondNetworkType } }); + network.update({ provider: { type: secondNetworkType, chainId: NetworksChainId[secondNetworkType] } }); expect(assetsController.state.collectibles).toHaveLength(0); - network.update({ provider: { type: firstNetworkType } }); + network.update({ provider: { type: firstNetworkType, chainId: NetworksChainId[firstNetworkType] } }); expect(assetsController.state.collectibles[0]).toEqual({ address: '0xfoO', description: 'description', @@ -393,14 +393,14 @@ describe('AssetsController', () => { .returns({ name: 'name', image: 'url', description: 'description' }); const firstNetworkType = 'rinkeby'; const secondNetworkType = 'ropsten'; - network.update({ provider: { type: firstNetworkType } }); + network.update({ provider: { type: firstNetworkType, chainId: NetworksChainId[firstNetworkType] } }); await assetsController.addCollectible('fou', 4321); - network.update({ provider: { type: secondNetworkType } }); + network.update({ provider: { type: secondNetworkType, chainId: NetworksChainId[secondNetworkType] } }); await assetsController.addCollectible('foo', 1234); assetsController.removeToken('0xfoO'); assetsController.removeCollectible('0xfoO', 1234); expect(assetsController.state.collectibles).toHaveLength(0); - network.update({ provider: { type: firstNetworkType } }); + network.update({ provider: { type: firstNetworkType, chainId: NetworksChainId[firstNetworkType] } }); expect(assetsController.state.collectibles[0]).toEqual({ address: '0xFOu', description: 'description', @@ -415,7 +415,7 @@ describe('AssetsController', () => { const address = '0x123'; preferences.update({ selectedAddress: address }); expect(assetsController.context.PreferencesController.state.selectedAddress).toEqual(address); - network.update({ provider: { type: networkType } }); + network.update({ provider: { type: networkType, chainId: NetworksChainId[networkType] } }); expect(assetsController.context.NetworkController.state.provider.type).toEqual(networkType); }); @@ -445,7 +445,7 @@ describe('AssetsController', () => { ) .catch((error) => { expect(error.message).toContain('Asset of type ERC721 not supported'); - resolve(); + resolve(''); }); }); }); @@ -467,7 +467,7 @@ describe('AssetsController', () => { }); result.catch((error) => { expect(error.message).toContain('User rejected to watch the asset.'); - resolve(); + resolve(''); }); }); }); @@ -485,7 +485,7 @@ describe('AssetsController', () => { result.then((res) => { expect(assetsController.state.suggestedAssets).toHaveLength(0); expect(res).toBe('0xe9f786dfdd9ae4d57e830acb52296837765f0e5b'); - resolve(); + resolve(''); }); await assetsController.acceptWatchAsset(suggestedAssetMeta.id); }); @@ -509,7 +509,7 @@ describe('AssetsController', () => { await assetsController.acceptWatchAsset(suggestedAssetMeta.id); result.catch((error) => { expect(error.message).toContain('Asset of type ERC721 not supported'); - resolve(); + resolve(''); }); }); }); diff --git a/tests/AssetsDetectionController.test.ts b/tests/AssetsDetectionController.test.ts index 41453df91a..4818f882a1 100644 --- a/tests/AssetsDetectionController.test.ts +++ b/tests/AssetsDetectionController.test.ts @@ -1,7 +1,7 @@ import { createSandbox, stub } from 'sinon'; import { getOnce, get } from 'fetch-mock'; import { AssetsDetectionController } from '../src/assets/AssetsDetectionController'; -import { NetworkController } from '../src/network/NetworkController'; +import { NetworkController, NetworksChainId } from '../src/network/NetworkController'; import { PreferencesController } from '../src/user/PreferencesController'; import { ComposableController } from '../src/ComposableController'; import { AssetsController } from '../src/assets/AssetsController'; @@ -189,7 +189,7 @@ describe('AssetsDetectionController', () => { expect(mockCollectibles.calledTwice).toBe(true); mockTokens.restore(); mockCollectibles.restore(); - resolve(); + resolve(''); }, 15); }); }); @@ -210,7 +210,7 @@ describe('AssetsDetectionController', () => { expect(mockCollectibles.called).toBe(false); mockTokens.restore(); mockCollectibles.restore(); - resolve(); + resolve(''); }); }); @@ -444,9 +444,9 @@ describe('AssetsDetectionController', () => { expect(detectAssets.calledTwice).toBe(false); preferences.update({ selectedAddress: firstAddress }); expect(assetsDetection.context.PreferencesController.state.selectedAddress).toEqual(firstAddress); - network.update({ provider: { type: secondNetworkType } }); + network.update({ provider: { type: secondNetworkType, chainId: NetworksChainId[secondNetworkType] } }); expect(assetsDetection.context.NetworkController.state.provider.type).toEqual(secondNetworkType); - network.update({ provider: { type: firstNetworkType } }); + network.update({ provider: { type: firstNetworkType, chainId: NetworksChainId[firstNetworkType] } }); expect(assetsDetection.context.NetworkController.state.provider.type).toEqual(firstNetworkType); assets.update({ tokens: TOKENS }); expect(assetsDetection.config.tokens).toEqual(TOKENS); diff --git a/tests/ComposableController.test.ts b/tests/ComposableController.test.ts index cbed12ee5a..829ffd1869 100644 --- a/tests/ComposableController.test.ts +++ b/tests/ComposableController.test.ts @@ -5,7 +5,7 @@ import ComposableController from '../src/ComposableController'; import PreferencesController from '../src/user/PreferencesController'; import TokenRatesController from '../src/assets/TokenRatesController'; import { AssetsController } from '../src/assets/AssetsController'; -import { NetworkController } from '../src/network/NetworkController'; +import { NetworkController, NetworksChainId } from '../src/network/NetworkController'; import { AssetsContractController } from '../src/assets/AssetsContractController'; import CurrencyRateController from '../src/assets/CurrencyRateController'; @@ -47,7 +47,7 @@ describe('ComposableController', () => { }, NetworkController: { network: 'loading', - provider: { type: 'mainnet' }, + provider: { type: 'mainnet', chainId: NetworksChainId.mainnet }, }, PreferencesController: { featureFlags: {}, @@ -93,7 +93,7 @@ describe('ComposableController', () => { lostIdentities: {}, nativeCurrency: 'ETH', network: 'loading', - provider: { type: 'mainnet' }, + provider: { type: 'mainnet', chainId: NetworksChainId.mainnet }, selectedAddress: '', suggestedAssets: [], tokens: [], @@ -147,7 +147,7 @@ describe('ComposableController', () => { lostIdentities: {}, nativeCurrency: 'ETH', network: 'loading', - provider: { type: 'mainnet' }, + provider: { type: 'mainnet', chainId: NetworksChainId.mainnet }, selectedAddress: '', suggestedAssets: [], tokens: [], diff --git a/tests/NetworkController.test.ts b/tests/NetworkController.test.ts index bbdafad756..3b00e0dc12 100644 --- a/tests/NetworkController.test.ts +++ b/tests/NetworkController.test.ts @@ -1,5 +1,5 @@ import { stub } from 'sinon'; -import NetworkController, { ProviderConfig } from '../src/network/NetworkController'; +import NetworkController, { NetworksChainId, ProviderConfig } from '../src/network/NetworkController'; const Web3ProviderEngine = require('web3-provider-engine'); @@ -12,6 +12,7 @@ describe('NetworkController', () => { network: 'loading', provider: { type: 'mainnet', + chainId: '1', }, }); }); @@ -30,7 +31,10 @@ describe('NetworkController', () => { const testConfig = { infuraProjectId: 'foo', }; - const controller = new NetworkController(testConfig, { network: '0', provider: { type: 'kovan' } }); + const controller = new NetworkController(testConfig, { + network: '0', + provider: { type: 'kovan', chainId: NetworksChainId.kovan }, + }); controller.providerConfig = {} as ProviderConfig; expect(controller.provider instanceof Web3ProviderEngine).toBe(true); }); @@ -39,7 +43,10 @@ describe('NetworkController', () => { const testConfig = { infuraProjectId: 'foo', }; - const controller = new NetworkController(testConfig, { network: '0', provider: { type: 'rinkeby' } }); + const controller = new NetworkController(testConfig, { + network: '0', + provider: { type: 'rinkeby', chainId: NetworksChainId.rinkeby }, + }); controller.providerConfig = {} as ProviderConfig; expect(controller.provider instanceof Web3ProviderEngine).toBe(true); }); @@ -48,7 +55,10 @@ describe('NetworkController', () => { const testConfig = { infuraProjectId: 'foo', }; - const controller = new NetworkController(testConfig, { network: '0', provider: { type: 'ropsten' } }); + const controller = new NetworkController(testConfig, { + network: '0', + provider: { type: 'ropsten', chainId: NetworksChainId.ropsten }, + }); controller.providerConfig = {} as ProviderConfig; expect(controller.provider instanceof Web3ProviderEngine).toBe(true); }); @@ -57,13 +67,19 @@ describe('NetworkController', () => { const testConfig = { infuraProjectId: 'foo', }; - const controller = new NetworkController(testConfig, { network: '0', provider: { type: 'mainnet' } }); + const controller = new NetworkController(testConfig, { + network: '0', + provider: { type: 'mainnet', chainId: NetworksChainId.mainnet }, + }); controller.providerConfig = {} as ProviderConfig; expect(controller.provider instanceof Web3ProviderEngine).toBe(true); }); it('should create a provider instance for local network', () => { - const controller = new NetworkController(undefined, { network: '0', provider: { type: 'localhost' } }); + const controller = new NetworkController(undefined, { + network: '0', + provider: { type: 'localhost', chainId: NetworksChainId.rpc }, + }); controller.providerConfig = {} as ProviderConfig; expect(controller.provider instanceof Web3ProviderEngine).toBe(true); }); @@ -74,6 +90,7 @@ describe('NetworkController', () => { provider: { rpcTarget: RPC_TARGET, type: 'rpc', + chainId: NetworksChainId.mainnet, }, }); controller.providerConfig = {} as ProviderConfig; @@ -82,7 +99,7 @@ describe('NetworkController', () => { it('should set new RPC target', () => { const controller = new NetworkController(); - controller.setRpcTarget(RPC_TARGET); + controller.setRpcTarget(RPC_TARGET, NetworksChainId.rpc); expect(controller.state.provider.rpcTarget).toBe(RPC_TARGET); }); @@ -114,7 +131,7 @@ describe('NetworkController', () => { controller.providerConfig = {} as ProviderConfig; setTimeout(() => { expect(controller.state.network !== 'loading').toBe(true); - resolve(); + resolve(''); }, 4500); }); }); diff --git a/tests/TransactionController.test.ts b/tests/TransactionController.test.ts index d0639d95af..5f4e81bcc5 100644 --- a/tests/TransactionController.test.ts +++ b/tests/TransactionController.test.ts @@ -1,4 +1,5 @@ import { stub } from 'sinon'; +import { NetworksChainId } from '../src/network/NetworkController'; import TransactionController from '../src/transaction/TransactionController'; const globalAny: any = global; @@ -63,18 +64,19 @@ const MOCK_PRFERENCES = { state: { selectedAddress: 'foo' } }; const PROVIDER = new HttpProvider('https://ropsten.infura.io/v3/341eacb578dd44a1a049cbc5f6fd4035'); const MAINNET_PROVIDER = new HttpProvider('https://mainnet.infura.io/v3/341eacb578dd44a1a049cbc5f6fd4035'); const MOCK_NETWORK = { + provider: PROVIDER, + state: { network: '3', provider: { type: 'ropsten', chainId: NetworksChainId.ropsten } }, + subscribe: () => undefined, +}; +const MOCK_NETWORK_WITHOUT_CHAIN_ID = { provider: PROVIDER, state: { network: '3', provider: { type: 'ropsten' } }, - subscribe: () => { - /* eslint-disable-line no-empty */ - }, + subscribe: () => undefined, }; const MOCK_MAINNET_NETWORK = { provider: MAINNET_PROVIDER, - state: { network: '1', provider: { type: 'mainnet' } }, - subscribe: () => { - /* eslint-disable-line no-empty */ - }, + state: { network: '1', provider: { type: 'mainnet', chainId: NetworksChainId.mainnet } }, + subscribe: () => undefined, }; const ETH_TRANSACTIONS = [ @@ -385,7 +387,7 @@ describe('TransactionController', () => { setTimeout(() => { expect(mock.calledTwice).toBe(true); mock.restore(); - resolve(); + resolve(''); }, 15); }); }); @@ -398,7 +400,7 @@ describe('TransactionController', () => { controller.poll(1338); expect(mock.called).toBe(true); mock.restore(); - resolve(); + resolve(''); }, 100); }); }); @@ -410,7 +412,7 @@ describe('TransactionController', () => { setTimeout(() => { expect(func.called).toBe(false); func.restore(); - resolve(); + resolve(''); }, 20); }); }); @@ -422,7 +424,7 @@ describe('TransactionController', () => { await controller.addTransaction({ from: 'foo' } as any); } catch (error) { expect(error.message).toContain('Invalid "from" address'); - resolve(); + resolve(''); } }); }); @@ -463,7 +465,7 @@ describe('TransactionController', () => { controller.cancelTransaction(controller.state.transactions[0].id); result.catch((error) => { expect(error.message).toContain('User rejected the transaction'); - resolve(); + resolve(''); }); }); }); @@ -505,7 +507,7 @@ describe('TransactionController', () => { expect(transaction.to).toBe(to); expect(status).toBe('failed'); expect(error.message).toContain('foo'); - resolve(); + resolve(''); }); await controller.approveTransaction(controller.state.transactions[0].id); }); @@ -527,7 +529,7 @@ describe('TransactionController', () => { }); } catch (error) { expect(error.message).toContain('Uh oh'); - resolve(); + resolve(''); } }); }); @@ -550,7 +552,32 @@ describe('TransactionController', () => { expect(transaction.to).toBe(to); expect(status).toBe('failed'); expect(error.message).toContain('No sign method defined'); - resolve(); + resolve(''); + }); + await controller.approveTransaction(controller.state.transactions[0].id); + }); + }); + + it('should fail if no chainId defined', () => { + return new Promise(async (resolve) => { + const controller = new TransactionController({ + provider: PROVIDER, + sign: async (transaction: any) => transaction, + }); + controller.context = { + NetworkController: MOCK_NETWORK_WITHOUT_CHAIN_ID, + } as any; + controller.onComposed(); + const from = '0xe6509775f3f3614576c0d83f8647752f87cd6659'; + const to = '0xc38bf1ad06ef69f0c04e29dbeb4152b4175f0a8d'; + const { result } = await controller.addTransaction({ from, to }); + result.catch((error) => { + const { transaction, status } = controller.state.transactions[0]; + expect(transaction.from).toBe(from); + expect(transaction.to).toBe(to); + expect(status).toBe('failed'); + expect(error.message).toContain('No chainId defined'); + resolve(''); }); await controller.approveTransaction(controller.state.transactions[0].id); }); @@ -578,7 +605,7 @@ describe('TransactionController', () => { const { transaction, status } = controller.state.transactions[0]; expect(transaction.from).toBe(from); expect(status).toBe('submitted'); - resolve(); + resolve(''); }); controller.approveTransaction(controller.state.transactions[0].id); }); @@ -605,7 +632,7 @@ describe('TransactionController', () => { controller.hub.once(`${controller.state.transactions[0].id}:confirmed`, () => { expect(controller.state.transactions[0].status).toBe('confirmed'); - resolve(); + resolve(''); }); controller.queryTransactionStatuses(); }); @@ -727,7 +754,7 @@ describe('TransactionController', () => { }); result.catch((error) => { expect(error.message).toContain('User cancelled the transaction'); - resolve(); + resolve(''); }); controller.stopTransaction(controller.state.transactions[0].id); }); @@ -750,7 +777,7 @@ describe('TransactionController', () => { await controller.stopTransaction(controller.state.transactions[0].id); } catch (error) { expect(error.message).toContain('No sign method defined'); - resolve(); + resolve(''); } }); }); @@ -777,7 +804,7 @@ describe('TransactionController', () => { expect(controller.state.transactions).toHaveLength(2); expect(controller.state.transactions[1].transaction.gasPrice).toBe('0x5916a6d6'); - resolve(); + resolve(''); }); }); });