diff --git a/app/core/Engine.js b/app/core/Engine.js index 7062dfc9127..2810a0ca719 100644 --- a/app/core/Engine.js +++ b/app/core/Engine.js @@ -65,76 +65,138 @@ class Engine { currentCurrency: 'usd' }; - this.datamodel = new ComposableController( - [ - new KeyringController({ encryptor }, initialState.KeyringController), - new AccountTrackerController(), - new AddressBookController(), - new AssetsContractController(), - new AssetsController(), - new AssetsDetectionController(), - new CurrencyRateController({ - nativeCurrency, - currentCurrency - }), - new PersonalMessageManager(), - new MessageManager(), - new NetworkController({ - infuraProjectId: process.env.MM_INFURA_PROJECT_ID || NON_EMPTY, - providerConfig: { - static: { - eth_sendTransaction: async (payload, next, end) => { - const { TransactionController } = this.datamodel.context; - try { - const hash = await (await TransactionController.addTransaction( - payload.params[0], - payload.origin, - WalletDevice.MM_MOBILE - )).result; - end(undefined, hash); - } catch (error) { - end(error); - } - } - }, - getAccounts: (end, payload) => { - const { approvedHosts, privacyMode } = store.getState(); - const isEnabled = !privacyMode || approvedHosts[payload.hostname]; - const { KeyringController } = this.datamodel.context; - const isUnlocked = KeyringController.isUnlocked(); - const selectedAddress = this.datamodel.context.PreferencesController.state - .selectedAddress; - end(null, isUnlocked && isEnabled && selectedAddress ? [selectedAddress] : []); + const preferencesController = new PreferencesController( + {}, + { + ipfsGateway: AppConstants.IPFS_DEFAULT_GATEWAY_URL + } + ); + const networkController = new NetworkController({ + infuraProjectId: process.env.MM_INFURA_PROJECT_ID || NON_EMPTY, + providerConfig: { + static: { + eth_sendTransaction: async (payload, next, end) => { + const { TransactionController } = this.context; + try { + const hash = await (await TransactionController.addTransaction( + payload.params[0], + payload.origin, + WalletDevice.MM_MOBILE + )).result; + end(undefined, hash); + } catch (error) { + end(error); } } - }), - new PhishingController(), - new PreferencesController( - {}, - { - ipfsGateway: AppConstants.IPFS_DEFAULT_GATEWAY_URL - } + }, + getAccounts: (end, payload) => { + const { approvedHosts, privacyMode } = store.getState(); + const isEnabled = !privacyMode || approvedHosts[payload.hostname]; + const { KeyringController } = this.context; + const isUnlocked = KeyringController.isUnlocked(); + const selectedAddress = this.context.PreferencesController.state.selectedAddress; + end(null, isUnlocked && isEnabled && selectedAddress ? [selectedAddress] : []); + } + } + }); + const assetsContractController = new AssetsContractController(); + const assetsController = new AssetsController({ + onPreferencesStateChange: listener => preferencesController.subscribe(listener), + onNetworkStateChange: listener => networkController.subscribe(listener), + getAssetName: assetsContractController.getAssetName.bind(assetsContractController), + getAssetSymbol: assetsContractController.getAssetSymbol.bind(assetsContractController), + getCollectibleTokenURI: assetsContractController.getCollectibleTokenURI.bind(assetsContractController) + }); + const currencyRateController = new CurrencyRateController({ + nativeCurrency, + currentCurrency + }); + + const controllers = [ + new KeyringController( + { + removeIdentity: preferencesController.removeIdentity.bind(preferencesController), + syncIdentities: preferencesController.syncIdentities.bind(preferencesController), + updateIdentities: preferencesController.updateIdentities.bind(preferencesController), + setSelectedAddress: preferencesController.setSelectedAddress.bind(preferencesController) + }, + { encryptor }, + initialState.KeyringController + ), + new AccountTrackerController({ + onPreferencesStateChange: listener => preferencesController.subscribe(listener), + getIdentities: () => preferencesController.state.identities + }), + new AddressBookController(), + assetsContractController, + assetsController, + new AssetsDetectionController({ + onAssetsStateChange: listener => assetsController.subscribe(listener), + onPreferencesStateChange: listener => preferencesController.subscribe(listener), + onNetworkStateChange: listener => networkController.subscribe(listener), + getOpenSeaApiKey: () => assetsController.openSeaApiKey, + getBalancesInSingleCall: assetsContractController.getBalancesInSingleCall.bind( + assetsContractController ), - new TokenBalancesController({ interval: 10000 }), - new TokenRatesController(), - new TransactionController(), - new TypedMessageManager(), - new SwapsController({ - clientId: AppConstants.SWAPS.CLIENT_ID, - fetchAggregatorMetadataThreshold: AppConstants.SWAPS.CACHE_AGGREGATOR_METADATA_THRESHOLD, - fetchTokensThreshold: AppConstants.SWAPS.CACHE_TOKENS_THRESHOLD, - fetchTopAssetsThreshold: AppConstants.SWAPS.CACHE_TOP_ASSETS_THRESHOLD - }) - ], - initialState - ); + addTokens: assetsController.addTokens.bind(assetsController), + addCollectible: assetsController.addCollectible.bind(assetsController), + removeCollectible: assetsController.removeCollectible.bind(assetsController), + getAssetsState: () => assetsController.state + }), + currencyRateController, + new PersonalMessageManager(), + new MessageManager(), + networkController, + new PhishingController(), + preferencesController, + new TokenBalancesController( + { + onAssetsStateChange: listener => assetsController.subscribe(listener), + getSelectedAddress: () => preferencesController.state.selectedAddress, + getBalanceOf: assetsContractController.getBalanceOf.bind(assetsContractController) + }, + { interval: 10000 } + ), + new TokenRatesController({ + onAssetsStateChange: listener => assetsController.subscribe(listener), + onCurrencyRateStateChange: listener => currencyRateController.subscribe(listener) + }), + new TransactionController({ + getNetworkState: () => networkController.state, + onNetworkStateChange: listener => networkController.subscribe(listener), + getProvider: () => networkController.provider + }), + new TypedMessageManager(), + new SwapsController({ + clientId: AppConstants.SWAPS.CLIENT_ID, + fetchAggregatorMetadataThreshold: AppConstants.SWAPS.CACHE_AGGREGATOR_METADATA_THRESHOLD, + fetchTokensThreshold: AppConstants.SWAPS.CACHE_TOKENS_THRESHOLD, + fetchTopAssetsThreshold: AppConstants.SWAPS.CACHE_TOP_ASSETS_THRESHOLD + }) + ]; + + // set initial state + // TODO: Pass initial state into each controller constructor instead + // This is being set post-construction for now to ensure it's functionally equivalent with + // how the `ComponsedController` used to set initial state. + for (const controller of controllers) { + if (initialState[controller.name]) { + controller.update(initialState[controller.name]); + } + } + + this.datamodel = new ComposableController(controllers, initialState); + this.context = controllers.reduce((context, controller) => { + context[controller.name] = controller; + return context; + }, {}); const { AssetsController: assets, KeyringController: keyring, NetworkController: network, TransactionController: transaction - } = this.datamodel.context; + } = this.context; assets.setApiKey(process.env.MM_OPENSEA_KEY); network.refreshNetwork(); @@ -162,7 +224,7 @@ class Engine { NetworkController: { provider, state: NetworkControllerState }, TransactionController, SwapsController - } = this.datamodel.context; + } = this.context; provider.sendAsync = provider.sendAsync.bind(provider); AccountTrackerController.configure({ provider }); @@ -181,7 +243,7 @@ class Engine { } refreshTransactionHistory = async forceCheck => { - const { TransactionController, PreferencesController, NetworkController } = this.datamodel.context; + const { TransactionController, PreferencesController, NetworkController } = this.context; const { selectedAddress } = PreferencesController.state; const { type: networkType } = NetworkController.state.provider; const { networkId } = Networks[networkType]; @@ -242,7 +304,7 @@ class Engine { AssetsController, TokenBalancesController, TokenRatesController - } = this.datamodel.context; + } = this.context; const { selectedAddress } = PreferencesController.state; const { conversionRate, currentCurrency } = CurrencyRateController.state; const { accounts } = AccountTrackerController.state; @@ -308,12 +370,7 @@ class Engine { // Whenever we are gonna start a new wallet // either imported or created, we need to // get rid of the old data from state - const { - TransactionController, - AssetsController, - TokenBalancesController, - TokenRatesController - } = this.datamodel.context; + const { TransactionController, AssetsController, TokenBalancesController, TokenRatesController } = this.context; //Clear assets info AssetsController.update({ @@ -346,7 +403,7 @@ class Engine { NetworkController, TransactionController, AssetsController - } = this.datamodel.context; + } = this.context; // Select same network ? await NetworkController.setProviderType(network.provider.type); @@ -434,7 +491,7 @@ let instance; export default { get context() { - return instance && instance.datamodel && instance.datamodel.context; + return instance && instance.context; }, get state() { const { diff --git a/app/core/Engine.test.js b/app/core/Engine.test.js index 8265d38a60f..a400440d10a 100644 --- a/app/core/Engine.test.js +++ b/app/core/Engine.test.js @@ -2,19 +2,19 @@ import Engine from './Engine'; describe('Engine', () => { it('should expose an API', () => { const engine = Engine.init({}); - expect(engine.datamodel.context).toHaveProperty('AccountTrackerController'); - expect(engine.datamodel.context).toHaveProperty('AddressBookController'); - expect(engine.datamodel.context).toHaveProperty('AssetsContractController'); - expect(engine.datamodel.context).toHaveProperty('AssetsController'); - expect(engine.datamodel.context).toHaveProperty('AssetsDetectionController'); - expect(engine.datamodel.context).toHaveProperty('CurrencyRateController'); - expect(engine.datamodel.context).toHaveProperty('KeyringController'); - expect(engine.datamodel.context).toHaveProperty('NetworkController'); - expect(engine.datamodel.context).toHaveProperty('PersonalMessageManager'); - expect(engine.datamodel.context).toHaveProperty('PhishingController'); - expect(engine.datamodel.context).toHaveProperty('PreferencesController'); - expect(engine.datamodel.context).toHaveProperty('TokenBalancesController'); - expect(engine.datamodel.context).toHaveProperty('TokenRatesController'); - expect(engine.datamodel.context).toHaveProperty('TypedMessageManager'); + expect(engine.context).toHaveProperty('AccountTrackerController'); + expect(engine.context).toHaveProperty('AddressBookController'); + expect(engine.context).toHaveProperty('AssetsContractController'); + expect(engine.context).toHaveProperty('AssetsController'); + expect(engine.context).toHaveProperty('AssetsDetectionController'); + expect(engine.context).toHaveProperty('CurrencyRateController'); + expect(engine.context).toHaveProperty('KeyringController'); + expect(engine.context).toHaveProperty('NetworkController'); + expect(engine.context).toHaveProperty('PersonalMessageManager'); + expect(engine.context).toHaveProperty('PhishingController'); + expect(engine.context).toHaveProperty('PreferencesController'); + expect(engine.context).toHaveProperty('TokenBalancesController'); + expect(engine.context).toHaveProperty('TokenRatesController'); + expect(engine.context).toHaveProperty('TypedMessageManager'); }); }); diff --git a/package.json b/package.json index 7d71ad90d16..0cecb3b6bd1 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "dependencies": { "@exodus/react-native-payments": "https://github.com/wachunei/react-native-payments.git#package-json-hack", "@metamask/contract-metadata": "^1.23.0", - "@metamask/controllers": "^7.0.0", + "@metamask/controllers": "^8.0.0", "@metamask/etherscan-link": "^2.0.0", "@metamask/swaps-controller": "^2.0.1", "@react-native-community/async-storage": "1.12.1", diff --git a/yarn.lock b/yarn.lock index a38aacaca60..9ff086d9574 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1539,12 +1539,17 @@ resolved "https://registry.yarnpkg.com/@metamask/contract-metadata/-/contract-metadata-1.23.0.tgz#c70be7f3eaeeb791651ce793b7cdc230e9780b18" integrity sha512-oTUqL9dtXtbng60DZMRsBmZ5HiOUUfEsZjuswOJ0yHO24YsW0ktCcgCJVYPv1HcOsF0SVrRtG4rtrvOl4nY+HA== -"@metamask/controllers@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@metamask/controllers/-/controllers-7.0.0.tgz#8daecd284faa897ca1f112a3f6f28d6936dac674" - integrity sha512-vb2/wgGfJFMUa4Ej67FMkV94s0vp765t2vwOt8EOxhWfmEP2v9myc2B95L5jJJZZgvCk2ojaV11CrFF4ookLng== +"@metamask/contract-metadata@^1.24.0": + version "1.25.0" + resolved "https://registry.yarnpkg.com/@metamask/contract-metadata/-/contract-metadata-1.25.0.tgz#442ace91fb40165310764b68d8096d0017bb0492" + integrity sha512-yhmYB9CQPv0dckNcPoWDcgtrdUp0OgK0uvkRE5QIBv4b3qENI1/03BztvK2ijbTuMlORUpjPq7/1MQDUPoRPVw== + +"@metamask/controllers@^8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@metamask/controllers/-/controllers-8.0.0.tgz#42ac5aaef67a03d3fe599a67a36597e01902ca8d" + integrity sha512-TrteMifsCxV1g3WHcSD1X98fF4hKep3sXZNGfrvkPqa8mrF03hJke21WBSTRtvJ3vkNLRWgi+5I6lVXFTzbYuQ== dependencies: - "@metamask/contract-metadata" "^1.23.0" + "@metamask/contract-metadata" "^1.24.0" "@types/uuid" "^8.3.0" async-mutex "^0.2.6" babel-runtime "^6.26.0"