mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
[MMI] adds mmi code fences to mm controller (#18279)
* adds code fencing * MMI adds mmi-controller * MMI prettier * chore: create keyring builder for CustodyKeyrings * updates code fence to build-mmi * adds dependencies * fix import and prettier * lint * clean up * clean up * removes old methods and adds new * comment for now * adds two missing methods * runs yarn dedupe * adds missing import * bump target values * lavamoat policy update * bump values in coverage targets * prettier import order * coverage report update * clean up * yarn update * yarn dedupe * ran lavamoat:auto * adds zlib entry to storybook/main.js * adds browserify-zlib * clean up * clean up * prettier * prettier * eslint fix * fix paths * fix prettier * fix file name for mocha * adds to config * rename * adds file to configs * test lavamoat clean up * run dedupe * sets value in storybook main.js as false * runs lavamoat auto * updates mmi packages to lighter versions * updates mmi packages * lavamoat auto * adds finalized tx status * lavamoat auto * yarn dedupe * clean up * moving stuff into mmi controller * clean up * updates tresholds * yarn lock review * updates the mmi controller --------- Co-authored-by: Shane Terence Odlum <shane.odlum@consensys.net>
This commit is contained in:
parent
d9c48cc4c0
commit
edf2cc41cb
@ -237,6 +237,7 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
excludedFiles: [
|
excludedFiles: [
|
||||||
'app/scripts/controllers/app-state.test.js',
|
'app/scripts/controllers/app-state.test.js',
|
||||||
|
'app/scripts/controllers/mmi-controller.test.js',
|
||||||
'app/scripts/controllers/network/**/*.test.js',
|
'app/scripts/controllers/network/**/*.test.js',
|
||||||
'app/scripts/controllers/permissions/**/*.test.js',
|
'app/scripts/controllers/permissions/**/*.test.js',
|
||||||
'app/scripts/lib/**/*.test.js',
|
'app/scripts/lib/**/*.test.js',
|
||||||
@ -265,6 +266,7 @@ module.exports = {
|
|||||||
files: [
|
files: [
|
||||||
'**/__snapshots__/*.snap',
|
'**/__snapshots__/*.snap',
|
||||||
'app/scripts/controllers/app-state.test.js',
|
'app/scripts/controllers/app-state.test.js',
|
||||||
|
'app/scripts/controllers/mmi-controller.test.js',
|
||||||
'app/scripts/controllers/network/**/*.test.js',
|
'app/scripts/controllers/network/**/*.test.js',
|
||||||
'app/scripts/controllers/network/**/*.test.ts',
|
'app/scripts/controllers/network/**/*.test.ts',
|
||||||
'app/scripts/controllers/network/provider-api-tests/*.ts',
|
'app/scripts/controllers/network/provider-api-tests/*.ts',
|
||||||
|
@ -8,6 +8,7 @@ module.exports = {
|
|||||||
'./app/scripts/controllers/app-state.test.js',
|
'./app/scripts/controllers/app-state.test.js',
|
||||||
'./app/scripts/controllers/network/**/*.test.js',
|
'./app/scripts/controllers/network/**/*.test.js',
|
||||||
'./app/scripts/controllers/permissions/**/*.test.js',
|
'./app/scripts/controllers/permissions/**/*.test.js',
|
||||||
|
'./app/scripts/controllers/mmi-controller.test.js',
|
||||||
'./app/scripts/constants/error-utils.test.js',
|
'./app/scripts/constants/error-utils.test.js',
|
||||||
],
|
],
|
||||||
recursive: true,
|
recursive: true,
|
||||||
|
612
app/scripts/controllers/mmi-controller.js
Normal file
612
app/scripts/controllers/mmi-controller.js
Normal file
@ -0,0 +1,612 @@
|
|||||||
|
import EventEmitter from 'events';
|
||||||
|
import log from 'loglevel';
|
||||||
|
import { captureException } from '@sentry/browser';
|
||||||
|
import {
|
||||||
|
PersonalMessageManager,
|
||||||
|
TypedMessageManager,
|
||||||
|
} from '@metamask/message-manager';
|
||||||
|
import { CUSTODIAN_TYPES } from '@metamask-institutional/custody-keyring';
|
||||||
|
import {
|
||||||
|
updateCustodianTransactions,
|
||||||
|
custodianEventHandlerFactory,
|
||||||
|
} from '@metamask-institutional/extension';
|
||||||
|
import {
|
||||||
|
REFRESH_TOKEN_CHANGE_EVENT,
|
||||||
|
INTERACTIVE_REPLACEMENT_TOKEN_CHANGE_EVENT,
|
||||||
|
} from '@metamask-institutional/sdk';
|
||||||
|
import { handleMmiPortfolio } from '@metamask-institutional/portfolio-dashboard';
|
||||||
|
import { toChecksumHexAddress } from '../../../shared/modules/hexstring-utils';
|
||||||
|
import { CHAIN_IDS } from '../../../shared/constants/network';
|
||||||
|
import {
|
||||||
|
BUILD_QUOTE_ROUTE,
|
||||||
|
CONNECT_HARDWARE_ROUTE,
|
||||||
|
} from '../../../ui/helpers/constants/routes';
|
||||||
|
import { getPermissionBackgroundApiMethods } from './permissions';
|
||||||
|
|
||||||
|
export default class MMIController extends EventEmitter {
|
||||||
|
constructor(opts) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.opts = opts;
|
||||||
|
this.mmiConfigurationController = opts.mmiConfigurationController;
|
||||||
|
this.keyringController = opts.keyringController;
|
||||||
|
this.txController = opts.txController;
|
||||||
|
this.securityProviderRequest = opts.securityProviderRequest;
|
||||||
|
this.preferencesController = opts.preferencesController;
|
||||||
|
this.appStateController = opts.appStateController;
|
||||||
|
this.transactionUpdateController = opts.transactionUpdateController;
|
||||||
|
this.custodyController = opts.custodyController;
|
||||||
|
this.institutionalFeaturesController = opts.institutionalFeaturesController;
|
||||||
|
this.getState = opts.getState;
|
||||||
|
this.getPendingNonce = opts.getPendingNonce;
|
||||||
|
this.accountTracker = opts.accountTracker;
|
||||||
|
this.metaMetricsController = opts.metaMetricsController;
|
||||||
|
this.networkController = opts.networkController;
|
||||||
|
this.permissionController = opts.permissionController;
|
||||||
|
this.platform = opts.platform;
|
||||||
|
this.extension = opts.extension;
|
||||||
|
|
||||||
|
this.personalMessageManager = new PersonalMessageManager(
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
this.securityProviderRequest,
|
||||||
|
);
|
||||||
|
this.typedMessageManager = new TypedMessageManager(
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
this.securityProviderRequest,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Prepare event listener after transactionUpdateController gets initiated
|
||||||
|
this.transactionUpdateController.prepareEventListener(
|
||||||
|
this.custodianEventHandlerFactory.bind(this),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get configuration from MMIConfig controller
|
||||||
|
if (!process.env.IN_TEST) {
|
||||||
|
this.mmiConfigurationController.storeConfiguration().then(() => {
|
||||||
|
// This must happen after the configuration is fetched
|
||||||
|
// Otherwise websockets will always be disabled in the first run
|
||||||
|
|
||||||
|
this.transactionUpdateController.subscribeToEvents();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} // End of constructor
|
||||||
|
|
||||||
|
async persistKeyringsAfterRefreshTokenChange() {
|
||||||
|
this.keyringController.persistAllKeyrings();
|
||||||
|
}
|
||||||
|
|
||||||
|
async trackTransactionEventFromCustodianEvent(txMeta, event) {
|
||||||
|
this.txController._trackTransactionMetricsEvent(txMeta, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
async addKeyringIfNotExists(type) {
|
||||||
|
let keyring = await this.keyringController.getKeyringsByType(type)[0];
|
||||||
|
if (!keyring) {
|
||||||
|
keyring = await this.keyringController.addNewKeyring(type);
|
||||||
|
}
|
||||||
|
return keyring;
|
||||||
|
}
|
||||||
|
|
||||||
|
custodianEventHandlerFactory() {
|
||||||
|
return custodianEventHandlerFactory({
|
||||||
|
log,
|
||||||
|
getState: () => this.getState(),
|
||||||
|
getPendingNonce: (address) => this.getPendingNonce(address),
|
||||||
|
setTxHash: (txId, txHash) => this.txController.setTxHash(txId, txHash),
|
||||||
|
typedMessageManager: this.typedMessageManager,
|
||||||
|
personalMessageManager: this.personalMessageManager,
|
||||||
|
txStateManager: this.txController.txStateManager,
|
||||||
|
custodyController: this.custodyController,
|
||||||
|
trackTransactionEvent:
|
||||||
|
this.trackTransactionEventFromCustodianEvent.bind(this),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async storeCustodianSupportedChains(address) {
|
||||||
|
const custodyType = this.custodyController.getCustodyTypeByAddress(
|
||||||
|
toChecksumHexAddress(address),
|
||||||
|
);
|
||||||
|
const keyring = await this.addKeyringIfNotExists(custodyType);
|
||||||
|
|
||||||
|
const supportedChains = await keyring.getSupportedChains(address);
|
||||||
|
|
||||||
|
if (supportedChains?.status === 401) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const accountDetails = this.custodyController.getAccountDetails(address);
|
||||||
|
|
||||||
|
await this.custodyController.storeSupportedChainsForAddress(
|
||||||
|
toChecksumHexAddress(address),
|
||||||
|
supportedChains,
|
||||||
|
accountDetails.custodianName,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async onSubmitPassword() {
|
||||||
|
// Create a keyring for each custodian type
|
||||||
|
let addresses = [];
|
||||||
|
const custodyTypes = this.custodyController.getAllCustodyTypes();
|
||||||
|
for (const type of custodyTypes) {
|
||||||
|
try {
|
||||||
|
const keyring = await this.addKeyringIfNotExists(type);
|
||||||
|
|
||||||
|
keyring.on(REFRESH_TOKEN_CHANGE_EVENT, () => {
|
||||||
|
log.info(`Refresh token change event for ${type}`);
|
||||||
|
this.persistKeyringsAfterRefreshTokenChange();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Trigger this event, listen to sdk, sdk change the state and then Ui is listening for the state changed
|
||||||
|
keyring.on(INTERACTIVE_REPLACEMENT_TOKEN_CHANGE_EVENT, (payload) => {
|
||||||
|
log.info(`Interactive refresh token change event for ${payload}`);
|
||||||
|
this.appStateController.showInteractiveReplacementTokenBanner(
|
||||||
|
payload,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// store the supported chains for this custodian type
|
||||||
|
const accounts = await keyring.getAccounts();
|
||||||
|
addresses = addresses.concat(...accounts);
|
||||||
|
for (const address of accounts) {
|
||||||
|
try {
|
||||||
|
await this.storeCustodianSupportedChains(address);
|
||||||
|
} catch (error) {
|
||||||
|
captureException(error);
|
||||||
|
log.error('Error while unlocking extension.', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const txList = this.txController.txStateManager.getTransactions(
|
||||||
|
{},
|
||||||
|
[],
|
||||||
|
false,
|
||||||
|
); // Includes all transactions, but we are looping through keyrings. Currently filtering is done in updateCustodianTransactions :-/
|
||||||
|
|
||||||
|
try {
|
||||||
|
updateCustodianTransactions({
|
||||||
|
keyring,
|
||||||
|
type,
|
||||||
|
txList,
|
||||||
|
getPendingNonce: this.getPendingNonce.bind(this),
|
||||||
|
txStateManager: this.txController.txStateManager,
|
||||||
|
setTxHash: this.txController.setTxHash.bind(this.txController),
|
||||||
|
custodyController: this.custodyController,
|
||||||
|
transactionUpdateController: this.transactionUpdateController,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
log.error('Error doing offline transaction updates', error);
|
||||||
|
captureException(error);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
log.error(
|
||||||
|
`Error while unlocking extension with custody type ${type}`,
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
captureException(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.mmiConfigurationController.storeConfiguration();
|
||||||
|
} catch (error) {
|
||||||
|
log.error('Error while unlocking extension.', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.transactionUpdateController.subscribeToEvents();
|
||||||
|
} catch (error) {
|
||||||
|
log.error('Error while unlocking extension.', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mmiConfigData =
|
||||||
|
await this.mmiConfigurationController.store.getState();
|
||||||
|
|
||||||
|
if (
|
||||||
|
mmiConfigData &&
|
||||||
|
mmiConfigData.mmiConfiguration.features?.websocketApi
|
||||||
|
) {
|
||||||
|
this.transactionUpdateController.getCustomerProofForAddresses(addresses);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (this.institutionalFeaturesController.getComplianceProjectId()) {
|
||||||
|
this.institutionalFeaturesController.startPolling();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log.error('Failed to start Compliance polling');
|
||||||
|
log.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async connectCustodyAddresses(custodianType, custodianName, accounts) {
|
||||||
|
if (!custodianType) {
|
||||||
|
throw new Error('No custodian');
|
||||||
|
}
|
||||||
|
|
||||||
|
const custodian = CUSTODIAN_TYPES[custodianType.toUpperCase()];
|
||||||
|
if (!custodian) {
|
||||||
|
throw new Error('No such custodian');
|
||||||
|
}
|
||||||
|
|
||||||
|
const newAccounts = Object.keys(accounts);
|
||||||
|
|
||||||
|
// Check if any address is already added
|
||||||
|
const identities = Object.keys(
|
||||||
|
this.preferencesController.store.getState().identities,
|
||||||
|
);
|
||||||
|
if (newAccounts.some((address) => identities.indexOf(address) !== -1)) {
|
||||||
|
throw new Error('Cannot import duplicate accounts');
|
||||||
|
}
|
||||||
|
|
||||||
|
const keyring = await this.addKeyringIfNotExists(
|
||||||
|
custodian.keyringClass.type,
|
||||||
|
);
|
||||||
|
|
||||||
|
keyring.on(REFRESH_TOKEN_CHANGE_EVENT, () => {
|
||||||
|
log.info(`Refresh token change event for ${keyring.type}`);
|
||||||
|
this.persistKeyringsAfterRefreshTokenChange();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Trigger this event, listen to sdk, sdk change the state and then Ui is listening for the state changed
|
||||||
|
keyring.on(INTERACTIVE_REPLACEMENT_TOKEN_CHANGE_EVENT, (payload) => {
|
||||||
|
log.info(`Interactive refresh token change event for ${payload}`);
|
||||||
|
this.appStateController.showInteractiveReplacementTokenBanner(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!keyring) {
|
||||||
|
throw new Error('Unable to get keyring');
|
||||||
|
}
|
||||||
|
const oldAccounts = await this.keyringController.getAccounts();
|
||||||
|
|
||||||
|
await keyring.setSelectedAddresses(
|
||||||
|
newAccounts.map((item) => ({
|
||||||
|
address: toChecksumHexAddress(item),
|
||||||
|
name: accounts[item].name,
|
||||||
|
custodianDetails: accounts[item].custodianDetails,
|
||||||
|
labels: accounts[item].labels,
|
||||||
|
token: accounts[item].token,
|
||||||
|
apiUrl: accounts[item].apiUrl,
|
||||||
|
custodyType: custodian.keyringClass.type,
|
||||||
|
chainId: accounts[item].chainId,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
this.custodyController.setAccountDetails(
|
||||||
|
newAccounts.map((item) => ({
|
||||||
|
address: toChecksumHexAddress(item),
|
||||||
|
name: accounts[item].name,
|
||||||
|
custodianDetails: accounts[item].custodianDetails,
|
||||||
|
labels: accounts[item].labels,
|
||||||
|
apiUrl: accounts[item].apiUrl,
|
||||||
|
custodyType: custodian.keyringClass.type,
|
||||||
|
custodianName,
|
||||||
|
chainId: accounts[item].chainId,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
|
newAccounts.forEach(
|
||||||
|
async () => await this.keyringController.addNewAccount(keyring),
|
||||||
|
);
|
||||||
|
|
||||||
|
const allAccounts = await this.keyringController.getAccounts();
|
||||||
|
|
||||||
|
this.preferencesController.setAddresses(allAccounts);
|
||||||
|
const accountsToTrack = [
|
||||||
|
...new Set(oldAccounts.concat(allAccounts.map((a) => a.toLowerCase()))),
|
||||||
|
];
|
||||||
|
|
||||||
|
allAccounts.forEach((address) => {
|
||||||
|
if (!oldAccounts.includes(address.toLowerCase())) {
|
||||||
|
const label = newAccounts
|
||||||
|
.filter((item) => item.toLowerCase() === address)
|
||||||
|
.map((item) => accounts[item].name)[0];
|
||||||
|
this.preferencesController.setAccountLabel(address, label);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.accountTracker.syncWithAddresses(accountsToTrack);
|
||||||
|
|
||||||
|
for (const address of newAccounts) {
|
||||||
|
try {
|
||||||
|
await this.storeCustodianSupportedChains(address);
|
||||||
|
} catch (error) {
|
||||||
|
captureException(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: status maps are not a thing anymore
|
||||||
|
this.custodyController.storeCustodyStatusMap(
|
||||||
|
custodian.name,
|
||||||
|
keyring.getStatusMap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// MMI - get a WS stream for this account
|
||||||
|
const mmiConfigData =
|
||||||
|
await this.mmiConfigurationController.store.getState();
|
||||||
|
|
||||||
|
if (
|
||||||
|
mmiConfigData &&
|
||||||
|
mmiConfigData.mmiConfiguration.features?.websocketApi
|
||||||
|
) {
|
||||||
|
this.transactionUpdateController.getCustomerProofForAddresses(
|
||||||
|
newAccounts,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newAccounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCustodianAccounts(
|
||||||
|
token,
|
||||||
|
apiUrl,
|
||||||
|
custodianType,
|
||||||
|
getNonImportedAccounts,
|
||||||
|
) {
|
||||||
|
let currentCustodyType;
|
||||||
|
if (!custodianType) {
|
||||||
|
const address = this.preferencesController.getSelectedAddress();
|
||||||
|
currentCustodyType = this.custodyController.getCustodyTypeByAddress(
|
||||||
|
toChecksumHexAddress(address),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let keyring;
|
||||||
|
|
||||||
|
if (custodianType) {
|
||||||
|
const custodian = CUSTODIAN_TYPES[custodianType.toUpperCase()];
|
||||||
|
if (!custodian) {
|
||||||
|
throw new Error('No such custodian');
|
||||||
|
}
|
||||||
|
|
||||||
|
keyring = await this.addKeyringIfNotExists(custodian.keyringClass.type);
|
||||||
|
} else if (currentCustodyType) {
|
||||||
|
keyring = await this.addKeyringIfNotExists(currentCustodyType);
|
||||||
|
} else {
|
||||||
|
throw new Error('No custodian specified');
|
||||||
|
}
|
||||||
|
|
||||||
|
const accounts = await keyring.getCustodianAccounts(
|
||||||
|
token,
|
||||||
|
apiUrl,
|
||||||
|
null,
|
||||||
|
getNonImportedAccounts,
|
||||||
|
);
|
||||||
|
return accounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCustodianAccountsByAddress(token, apiUrl, address, custodianType) {
|
||||||
|
let keyring;
|
||||||
|
|
||||||
|
if (custodianType) {
|
||||||
|
const custodian = CUSTODIAN_TYPES[custodianType.toUpperCase()];
|
||||||
|
if (!custodian) {
|
||||||
|
throw new Error('No such custodian');
|
||||||
|
}
|
||||||
|
|
||||||
|
keyring = await this.addKeyringIfNotExists(custodian.keyringClass.type);
|
||||||
|
} else {
|
||||||
|
throw new Error('No custodian specified');
|
||||||
|
}
|
||||||
|
|
||||||
|
const accounts = await keyring.getCustodianAccounts(token, apiUrl, address);
|
||||||
|
return accounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCustodianTransactionDeepLink(address, txId) {
|
||||||
|
const custodyType = this.custodyController.getCustodyTypeByAddress(
|
||||||
|
toChecksumHexAddress(address),
|
||||||
|
);
|
||||||
|
const keyring = await this.addKeyringIfNotExists(custodyType);
|
||||||
|
return keyring.getTransactionDeepLink(address, txId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCustodianConfirmDeepLink(txId) {
|
||||||
|
const txMeta = this.txController.txStateManager.getTransaction(txId);
|
||||||
|
|
||||||
|
const address = txMeta.txParams.from;
|
||||||
|
const custodyType = this.custodyController.getCustodyTypeByAddress(
|
||||||
|
toChecksumHexAddress(address),
|
||||||
|
);
|
||||||
|
const keyring = await this.addKeyringIfNotExists(custodyType);
|
||||||
|
return {
|
||||||
|
deepLink: await keyring.getTransactionDeepLink(
|
||||||
|
txMeta.txParams.from,
|
||||||
|
txMeta.custodyId,
|
||||||
|
),
|
||||||
|
custodyId: txMeta.custodyId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCustodianSignMessageDeepLink(from, custodyTxId) {
|
||||||
|
const custodyType = this.custodyController.getCustodyTypeByAddress(
|
||||||
|
toChecksumHexAddress(from),
|
||||||
|
);
|
||||||
|
const keyring = await this.addKeyringIfNotExists(custodyType);
|
||||||
|
return keyring.getTransactionDeepLink(from, custodyTxId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCustodianToken(custodianType) {
|
||||||
|
let currentCustodyType;
|
||||||
|
|
||||||
|
const address = this.preferencesController.getSelectedAddress();
|
||||||
|
|
||||||
|
if (!custodianType) {
|
||||||
|
const resultCustody = this.custodyController.getCustodyTypeByAddress(
|
||||||
|
toChecksumHexAddress(address),
|
||||||
|
);
|
||||||
|
currentCustodyType = resultCustody;
|
||||||
|
}
|
||||||
|
let keyring = await this.keyringController.getKeyringsByType(
|
||||||
|
currentCustodyType || `Custody - ${custodianType}`,
|
||||||
|
)[0];
|
||||||
|
if (!keyring) {
|
||||||
|
keyring = await this.keyringController.addNewKeyring(
|
||||||
|
currentCustodyType || `Custody - ${custodianType}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const { authDetails } = keyring.getAccountDetails(address);
|
||||||
|
return keyring ? authDetails.jwt || authDetails.refreshToken : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Based on a custodian name, get all the tokens associated with that custodian
|
||||||
|
async getCustodianJWTList(custodianName) {
|
||||||
|
console.log('getCustodianJWTList', custodianName);
|
||||||
|
|
||||||
|
const { identities } = this.preferencesController.store.getState();
|
||||||
|
|
||||||
|
const { mmiConfiguration } =
|
||||||
|
this.mmiConfigurationController.store.getState();
|
||||||
|
|
||||||
|
const addresses = Object.keys(identities);
|
||||||
|
const tokenList = [];
|
||||||
|
|
||||||
|
const { custodians } = mmiConfiguration;
|
||||||
|
|
||||||
|
const custodian = custodians.find((item) => item.name === custodianName);
|
||||||
|
|
||||||
|
if (!custodian) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const keyrings = await this.keyringController.getKeyringsByType(
|
||||||
|
`Custody - ${custodian.type}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const address of addresses) {
|
||||||
|
for (const keyring of keyrings) {
|
||||||
|
// Narrow down to custodian Type
|
||||||
|
const accountDetails = keyring.getAccountDetails(address);
|
||||||
|
|
||||||
|
if (!accountDetails) {
|
||||||
|
log.debug(`${address} does not belong to ${custodian.type} keyring`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const custodyAccountDetails =
|
||||||
|
this.custodyController.getAccountDetails(address);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!custodyAccountDetails ||
|
||||||
|
custodyAccountDetails.custodianName !== custodianName
|
||||||
|
) {
|
||||||
|
log.debug(`${address} does not belong to ${custodianName} keyring`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { authDetails } = accountDetails;
|
||||||
|
|
||||||
|
let token;
|
||||||
|
if (authDetails.jwt) {
|
||||||
|
token = authDetails.jwt;
|
||||||
|
} else if (authDetails.refreshToken) {
|
||||||
|
token = authDetails.refreshToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tokenList.includes(token)) {
|
||||||
|
tokenList.push(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tokenList;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAllCustodianAccountsWithToken(custodyType, token) {
|
||||||
|
const keyring = await this.keyringController.getKeyringsByType(
|
||||||
|
`Custody - ${custodyType}`,
|
||||||
|
)[0];
|
||||||
|
return keyring ? keyring.getAllAccountsWithToken(token) : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
async setCustodianNewRefreshToken({ address, newAuthDetails }) {
|
||||||
|
const custodyType = this.custodyController.getCustodyTypeByAddress(
|
||||||
|
toChecksumHexAddress(address),
|
||||||
|
);
|
||||||
|
|
||||||
|
const keyring = await this.addKeyringIfNotExists(custodyType);
|
||||||
|
|
||||||
|
await keyring.replaceRefreshTokenAuthDetails(address, newAuthDetails);
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleMmiCheckIfTokenIsPresent(req) {
|
||||||
|
const { token, apiUrl } = req.params;
|
||||||
|
const custodyType = 'Custody - JSONRPC'; // Only JSONRPC is supported for now
|
||||||
|
|
||||||
|
// This can only work if the extension is unlocked
|
||||||
|
await this.appStateController.getUnlockPromise(true);
|
||||||
|
|
||||||
|
const keyring = await this.addKeyringIfNotExists(custodyType);
|
||||||
|
|
||||||
|
return await this.custodyController.handleMmiCheckIfTokenIsPresent({
|
||||||
|
token,
|
||||||
|
apiUrl,
|
||||||
|
keyring,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async setMmiPortfolioCookie() {
|
||||||
|
await this.appStateController.getUnlockPromise(true);
|
||||||
|
const keyringAccounts = await this.keyringController.getAccounts();
|
||||||
|
const { identities } = this.preferencesController.store.getState();
|
||||||
|
const { metaMetricsId } = this.metaMetricsController.store.getState();
|
||||||
|
const getAccountDetails = (address) =>
|
||||||
|
this.custodyController.getAccountDetails(address);
|
||||||
|
const extensionId = this.extension.runtime.id;
|
||||||
|
const networks = [
|
||||||
|
...this.preferencesController.getRpcMethodPreferences(),
|
||||||
|
{ chainId: CHAIN_IDS.MAINNET },
|
||||||
|
{ chainId: CHAIN_IDS.GOERLI },
|
||||||
|
];
|
||||||
|
|
||||||
|
handleMmiPortfolio({
|
||||||
|
keyringAccounts,
|
||||||
|
identities,
|
||||||
|
metaMetricsId,
|
||||||
|
networks,
|
||||||
|
getAccountDetails,
|
||||||
|
extensionId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async setAccountAndNetwork(origin, address, chainId) {
|
||||||
|
await this.appStateController.getUnlockPromise(true);
|
||||||
|
const selectedAddress = this.preferencesController.getSelectedAddress();
|
||||||
|
if (selectedAddress.toLowerCase() !== address.toLowerCase()) {
|
||||||
|
this.preferencesController.setSelectedAddress(address);
|
||||||
|
}
|
||||||
|
const selectedChainId = parseInt(
|
||||||
|
this.networkController.getCurrentChainId(),
|
||||||
|
16,
|
||||||
|
);
|
||||||
|
if (selectedChainId !== chainId && chainId === 1) {
|
||||||
|
this.networkController.setProviderType('mainnet');
|
||||||
|
} else if (selectedChainId !== chainId) {
|
||||||
|
const network = this.preferencesController
|
||||||
|
.getFrequentRpcListDetail()
|
||||||
|
.find((item) => parseInt(item.chainId, 16) === chainId);
|
||||||
|
this.networkController.setRpcTarget(
|
||||||
|
network.rpcUrl,
|
||||||
|
network.chainId,
|
||||||
|
network.ticker,
|
||||||
|
network.nickname,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
getPermissionBackgroundApiMethods(
|
||||||
|
this.permissionController,
|
||||||
|
).addPermittedAccount(origin, address);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleMmiOpenSwaps(origin, address, chainId) {
|
||||||
|
await this.setAccountAndNetwork(origin, address, chainId);
|
||||||
|
this.platform.openExtensionInBrowser(BUILD_QUOTE_ROUTE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleMmiOpenAddHardwareWallet() {
|
||||||
|
await this.appStateController.getUnlockPromise(true);
|
||||||
|
this.platform.openExtensionInBrowser(CONNECT_HARDWARE_ROUTE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
129
app/scripts/controllers/mmi-controller.test.js
Normal file
129
app/scripts/controllers/mmi-controller.test.js
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
import { KeyringController } from '@metamask/eth-keyring-controller';
|
||||||
|
import { MmiConfigurationController } from '@metamask-institutional/custody-keyring';
|
||||||
|
import { TransactionUpdateController } from '@metamask-institutional/transaction-update';
|
||||||
|
|
||||||
|
import MMIController from './mmi-controller';
|
||||||
|
import TransactionController from './transactions';
|
||||||
|
import PreferencesController from './preferences';
|
||||||
|
import AppStateController from './app-state';
|
||||||
|
|
||||||
|
describe('MMIController', function () {
|
||||||
|
let mmiController;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mmiController = new MMIController({
|
||||||
|
mmiConfigurationController: new MmiConfigurationController(),
|
||||||
|
keyringController: new KeyringController({
|
||||||
|
initState: {},
|
||||||
|
}),
|
||||||
|
transactionUpdateController: new TransactionUpdateController({
|
||||||
|
getCustodyKeyring: jest.fn(),
|
||||||
|
}),
|
||||||
|
txController: new TransactionController({
|
||||||
|
initState: {},
|
||||||
|
provider: {
|
||||||
|
chainId: 'fail',
|
||||||
|
nickname: '',
|
||||||
|
rpcTarget: 'https://api.myetherwallet.com/eth',
|
||||||
|
ticker: 'ETH',
|
||||||
|
type: 'rinkeby',
|
||||||
|
},
|
||||||
|
getCurrentChainId: jest.fn(),
|
||||||
|
getNetworkId: jest.fn(),
|
||||||
|
onNetworkStateChange: jest.fn(),
|
||||||
|
}),
|
||||||
|
preferencesController: new PreferencesController({
|
||||||
|
initState: {},
|
||||||
|
onInfuraIsBlocked: jest.fn(),
|
||||||
|
onInfuraIsUnblocked: jest.fn(),
|
||||||
|
provider: {},
|
||||||
|
}),
|
||||||
|
appStateController: new AppStateController({
|
||||||
|
addUnlockListener: jest.fn(),
|
||||||
|
isUnlocked: jest.fn(() => true),
|
||||||
|
initState: {},
|
||||||
|
onInactiveTimeout: jest.fn(),
|
||||||
|
showUnlockRequest: jest.fn(),
|
||||||
|
preferencesStore: {
|
||||||
|
subscribe: jest.fn(),
|
||||||
|
getState: jest.fn(() => ({
|
||||||
|
preferences: {
|
||||||
|
autoLockTimeLimit: 0,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
qrHardwareStore: {
|
||||||
|
subscribe: jest.fn(),
|
||||||
|
},
|
||||||
|
messenger: {
|
||||||
|
call: jest.fn(() => ({
|
||||||
|
catch: jest.fn(),
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
custodianEventHandlerFactory: jest.fn(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('mmiController constructor', function () {
|
||||||
|
it('should instantiate correctly', function () {
|
||||||
|
expect(mmiController).toBeInstanceOf(MMIController);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have all required properties', function () {
|
||||||
|
expect(mmiController.opts).toBeDefined();
|
||||||
|
expect(mmiController.mmiConfigurationController).toBeDefined();
|
||||||
|
expect(mmiController.preferencesController).toBeDefined();
|
||||||
|
expect(mmiController.transactionUpdateController).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('persistKeyringsAfterRefreshTokenChange', function () {
|
||||||
|
it('should call keyringController.persistAllKeyrings', async function () {
|
||||||
|
mmiController.keyringController.persistAllKeyrings = jest.fn();
|
||||||
|
|
||||||
|
await mmiController.persistKeyringsAfterRefreshTokenChange();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
mmiController.keyringController.persistAllKeyrings,
|
||||||
|
).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('trackTransactionEventFromCustodianEvent', function () {
|
||||||
|
it('should call txController._trackTransactionMetricsEvent', function () {
|
||||||
|
const txMeta = {};
|
||||||
|
const event = 'event';
|
||||||
|
mmiController.txController._trackTransactionMetricsEvent = jest.fn();
|
||||||
|
|
||||||
|
mmiController.trackTransactionEventFromCustodianEvent(txMeta, event);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
mmiController.txController._trackTransactionMetricsEvent,
|
||||||
|
).toHaveBeenCalledWith(txMeta, event);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('custodianEventHandlerFactory', function () {
|
||||||
|
it('should call custodianEventHandlerFactory', async function () {
|
||||||
|
mmiController.custodianEventHandlerFactory = jest.fn();
|
||||||
|
|
||||||
|
mmiController.custodianEventHandlerFactory();
|
||||||
|
|
||||||
|
expect(mmiController.custodianEventHandlerFactory).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('storeCustodianSupportedChains', function () {
|
||||||
|
it('should call storeCustodianSupportedChains', async function () {
|
||||||
|
mmiController.storeCustodianSupportedChains = jest.fn();
|
||||||
|
|
||||||
|
mmiController.storeCustodianSupportedChains('0x1');
|
||||||
|
|
||||||
|
expect(mmiController.storeCustodianSupportedChains).toHaveBeenCalledWith(
|
||||||
|
'0x1',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -63,8 +63,28 @@ import {
|
|||||||
} from '@metamask/snaps-controllers';
|
} from '@metamask/snaps-controllers';
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||||
|
import {
|
||||||
|
CUSTODIAN_TYPES,
|
||||||
|
MmiConfigurationController,
|
||||||
|
} from '@metamask-institutional/custody-keyring';
|
||||||
|
import { InstitutionalFeaturesController } from '@metamask-institutional/institutional-features';
|
||||||
|
import { CustodyController } from '@metamask-institutional/custody-controller';
|
||||||
|
import { TransactionUpdateController } from '@metamask-institutional/transaction-update';
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
import { SignatureController } from '@metamask/signature-controller';
|
import { SignatureController } from '@metamask/signature-controller';
|
||||||
|
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||||
|
// eslint-disable-next-line import/order
|
||||||
|
import { DesktopController } from '@metamask/desktop/dist/controllers/desktop';
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
|
||||||
import { ApprovalType } from '@metamask/controller-utils';
|
import { ApprovalType } from '@metamask/controller-utils';
|
||||||
|
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||||
|
import { toChecksumHexAddress } from '../../shared/modules/hexstring-utils';
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AssetType,
|
AssetType,
|
||||||
TransactionStatus,
|
TransactionStatus,
|
||||||
@ -116,15 +136,16 @@ import { STATIC_MAINNET_TOKEN_LIST } from '../../shared/constants/tokens';
|
|||||||
import { getTokenValueParam } from '../../shared/lib/metamask-controller-utils';
|
import { getTokenValueParam } from '../../shared/lib/metamask-controller-utils';
|
||||||
import { isManifestV3 } from '../../shared/modules/mv3.utils';
|
import { isManifestV3 } from '../../shared/modules/mv3.utils';
|
||||||
import { hexToDecimal } from '../../shared/modules/conversion.utils';
|
import { hexToDecimal } from '../../shared/modules/conversion.utils';
|
||||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
|
||||||
// eslint-disable-next-line import/order
|
|
||||||
import { DesktopController } from '@metamask/desktop/dist/controllers/desktop';
|
|
||||||
///: END:ONLY_INCLUDE_IN
|
|
||||||
import { ACTION_QUEUE_METRICS_E2E_TEST } from '../../shared/constants/test-flags';
|
import { ACTION_QUEUE_METRICS_E2E_TEST } from '../../shared/constants/test-flags';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
onMessageReceived,
|
onMessageReceived,
|
||||||
checkForMultipleVersionsRunning,
|
checkForMultipleVersionsRunning,
|
||||||
} from './detect-multiple-instances';
|
} from './detect-multiple-instances';
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||||
|
import MMIController from './controllers/mmi-controller';
|
||||||
|
import { mmiKeyringBuilderFactory } from './mmi-keyring-builder-factory';
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
import ComposableObservableStore from './lib/ComposableObservableStore';
|
import ComposableObservableStore from './lib/ComposableObservableStore';
|
||||||
import AccountTracker from './lib/account-tracker';
|
import AccountTracker from './lib/account-tracker';
|
||||||
import createDupeReqFilterMiddleware from './lib/createDupeReqFilterMiddleware';
|
import createDupeReqFilterMiddleware from './lib/createDupeReqFilterMiddleware';
|
||||||
@ -264,6 +285,13 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||||
|
this.mmiConfigurationController = new MmiConfigurationController({
|
||||||
|
initState: initState.MmiConfigurationController,
|
||||||
|
mmiConfigurationServiceUrl: process.env.MMI_CONFIGURATION_SERVICE_URL,
|
||||||
|
});
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
|
||||||
const networkControllerMessenger = this.controllerMessenger.getRestricted({
|
const networkControllerMessenger = this.controllerMessenger.getRestricted({
|
||||||
name: 'NetworkController',
|
name: 'NetworkController',
|
||||||
allowedEvents: [
|
allowedEvents: [
|
||||||
@ -273,6 +301,7 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
'NetworkController:infuraIsUnblocked',
|
'NetworkController:infuraIsUnblocked',
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
this.networkController = new NetworkController({
|
this.networkController = new NetworkController({
|
||||||
messenger: networkControllerMessenger,
|
messenger: networkControllerMessenger,
|
||||||
state: initState.NetworkController,
|
state: initState.NetworkController,
|
||||||
@ -725,9 +754,21 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
keyringOverrides?.lattice || LatticeKeyring,
|
keyringOverrides?.lattice || LatticeKeyring,
|
||||||
QRHardwareKeyring,
|
QRHardwareKeyring,
|
||||||
];
|
];
|
||||||
|
|
||||||
additionalKeyrings = additionalKeyringTypes.map((keyringType) =>
|
additionalKeyrings = additionalKeyringTypes.map((keyringType) =>
|
||||||
keyringBuilderFactory(keyringType),
|
keyringBuilderFactory(keyringType),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||||
|
for (const custodianType of Object.keys(CUSTODIAN_TYPES)) {
|
||||||
|
additionalKeyrings.push(
|
||||||
|
mmiKeyringBuilderFactory(
|
||||||
|
CUSTODIAN_TYPES[custodianType].keyringClass,
|
||||||
|
{ mmiConfigurationController: this.mmiConfigurationController },
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
}
|
}
|
||||||
|
|
||||||
this.keyringController = new KeyringController({
|
this.keyringController = new KeyringController({
|
||||||
@ -984,6 +1025,22 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
preferencesStore: this.preferencesController.store,
|
preferencesStore: this.preferencesController.store,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||||
|
this.custodyController = new CustodyController({
|
||||||
|
initState: initState.CustodyController,
|
||||||
|
});
|
||||||
|
this.institutionalFeaturesController = new InstitutionalFeaturesController({
|
||||||
|
initState: initState.InstitutionalFeaturesController,
|
||||||
|
showConfirmRequest: opts.showUserConfirmation,
|
||||||
|
});
|
||||||
|
this.transactionUpdateController = new TransactionUpdateController({
|
||||||
|
initState: initState.TransactionUpdateController,
|
||||||
|
getCustodyKeyring: this.getCustodyKeyringIfExists.bind(this),
|
||||||
|
mmiConfigurationController: this.mmiConfigurationController,
|
||||||
|
captureException,
|
||||||
|
});
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
|
||||||
this.backupController = new BackupController({
|
this.backupController = new BackupController({
|
||||||
preferencesController: this.preferencesController,
|
preferencesController: this.preferencesController,
|
||||||
addressBookController: this.addressBookController,
|
addressBookController: this.addressBookController,
|
||||||
@ -1054,6 +1111,9 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
getDeviceModel: this.getDeviceModel.bind(this),
|
getDeviceModel: this.getDeviceModel.bind(this),
|
||||||
getTokenStandardAndDetails: this.getTokenStandardAndDetails.bind(this),
|
getTokenStandardAndDetails: this.getTokenStandardAndDetails.bind(this),
|
||||||
securityProviderRequest: this.securityProviderRequest.bind(this),
|
securityProviderRequest: this.securityProviderRequest.bind(this),
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||||
|
transactionUpdateController: this.transactionUpdateController,
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
messenger: this.controllerMessenger.getRestricted({
|
messenger: this.controllerMessenger.getRestricted({
|
||||||
name: 'TransactionController',
|
name: 'TransactionController',
|
||||||
allowedActions: [
|
allowedActions: [
|
||||||
@ -1064,6 +1124,28 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||||
|
this.mmiController = new MMIController({
|
||||||
|
mmiConfigurationController: this.mmiConfigurationController,
|
||||||
|
keyringController: this.keyringController,
|
||||||
|
txController: this.txController,
|
||||||
|
securityProviderRequest: this.securityProviderRequest.bind(this),
|
||||||
|
preferencesController: this.preferencesController,
|
||||||
|
appStateController: this.appStateController,
|
||||||
|
transactionUpdateController: this.transactionUpdateController,
|
||||||
|
custodyController: this.custodyController,
|
||||||
|
institutionalFeaturesController: this.institutionalFeaturesController,
|
||||||
|
getState: this.getState.bind(this),
|
||||||
|
getPendingNonce: this.getPendingNonce.bind(this),
|
||||||
|
accountTracker: this.accountTracker,
|
||||||
|
metaMetricsController: this.metaMetricsController,
|
||||||
|
networkController: this.networkController,
|
||||||
|
permissionController: this.permissionController,
|
||||||
|
platform: this.platform,
|
||||||
|
extension: this.extension,
|
||||||
|
});
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
|
||||||
this.txController.on(`tx:status-update`, async (txId, status) => {
|
this.txController.on(`tx:status-update`, async (txId, status) => {
|
||||||
if (
|
if (
|
||||||
status === TransactionStatus.confirmed ||
|
status === TransactionStatus.confirmed ||
|
||||||
@ -1431,6 +1513,13 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||||
DesktopController: this.desktopController.store,
|
DesktopController: this.desktopController.store,
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||||
|
CustodyController: this.custodyController.store,
|
||||||
|
InstitutionalFeaturesController:
|
||||||
|
this.institutionalFeaturesController.store,
|
||||||
|
MmiConfigurationController: this.mmiConfigurationController.store,
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
...resetOnRestartStore,
|
...resetOnRestartStore,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1467,6 +1556,13 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||||
DesktopController: this.desktopController.store,
|
DesktopController: this.desktopController.store,
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||||
|
CustodyController: this.custodyController.store,
|
||||||
|
InstitutionalFeaturesController:
|
||||||
|
this.institutionalFeaturesController.store,
|
||||||
|
MmiConfigurationController: this.mmiConfigurationController.store,
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
...resetOnRestartStore,
|
...resetOnRestartStore,
|
||||||
},
|
},
|
||||||
controllerMessenger: this.controllerMessenger,
|
controllerMessenger: this.controllerMessenger,
|
||||||
@ -2236,6 +2332,72 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
rejectPermissionsRequest: this.rejectPermissionsRequest,
|
rejectPermissionsRequest: this.rejectPermissionsRequest,
|
||||||
...getPermissionBackgroundApiMethods(permissionController),
|
...getPermissionBackgroundApiMethods(permissionController),
|
||||||
|
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||||
|
connectCustodyAddresses:
|
||||||
|
this.mmiController.connectCustodyAddresses.bind(this),
|
||||||
|
getCustodianAccounts: this.mmiController.getCustodianAccounts.bind(this),
|
||||||
|
getCustodianAccountsByAddress:
|
||||||
|
this.mmiController.getCustodianAccountsByAddress.bind(this),
|
||||||
|
getCustodianTransactionDeepLink:
|
||||||
|
this.mmiController.getCustodianTransactionDeepLink.bind(this),
|
||||||
|
getCustodianConfirmDeepLink:
|
||||||
|
this.mmiController.getCustodianConfirmDeepLink.bind(this),
|
||||||
|
getCustodianSignMessageDeepLink:
|
||||||
|
this.mmiController.getCustodianSignMessageDeepLink.bind(this),
|
||||||
|
getCustodianToken: this.mmiController.getCustodianToken.bind(this),
|
||||||
|
getCustodianJWTList: this.mmiController.getCustodianJWTList.bind(this),
|
||||||
|
setWaitForConfirmDeepLinkDialog:
|
||||||
|
this.custodyController.setWaitForConfirmDeepLinkDialog.bind(
|
||||||
|
this.custodyController,
|
||||||
|
),
|
||||||
|
setCustodianConnectRequest:
|
||||||
|
this.custodyController.setCustodianConnectRequest.bind(
|
||||||
|
this.custodyController,
|
||||||
|
),
|
||||||
|
getCustodianConnectRequest:
|
||||||
|
this.custodyController.getCustodianConnectRequest.bind(
|
||||||
|
this.custodyController,
|
||||||
|
),
|
||||||
|
getAllCustodianAccountsWithToken:
|
||||||
|
this.mmiController.getAllCustodianAccountsWithToken.bind(this),
|
||||||
|
getMmiConfiguration:
|
||||||
|
this.mmiConfigurationController.getConfiguration.bind(
|
||||||
|
this.mmiConfigurationController,
|
||||||
|
),
|
||||||
|
setComplianceAuthData:
|
||||||
|
this.institutionalFeaturesController.setComplianceAuthData.bind(
|
||||||
|
this.institutionalFeaturesController,
|
||||||
|
),
|
||||||
|
deleteComplianceAuthData:
|
||||||
|
this.institutionalFeaturesController.deleteComplianceAuthData.bind(
|
||||||
|
this.institutionalFeaturesController,
|
||||||
|
),
|
||||||
|
generateComplianceReport:
|
||||||
|
this.institutionalFeaturesController.generateComplianceReport.bind(
|
||||||
|
this.institutionalFeaturesController,
|
||||||
|
),
|
||||||
|
syncReportsInProgress:
|
||||||
|
this.institutionalFeaturesController.syncReportsInProgress.bind(
|
||||||
|
this.institutionalFeaturesController,
|
||||||
|
),
|
||||||
|
|
||||||
|
removeConnectInstitutionalFeature:
|
||||||
|
this.institutionalFeaturesController.removeConnectInstitutionalFeature.bind(
|
||||||
|
this.institutionalFeaturesController,
|
||||||
|
),
|
||||||
|
|
||||||
|
getComplianceHistoricalReportsByAddress:
|
||||||
|
this.institutionalFeaturesController.getComplianceHistoricalReportsByAddress.bind(
|
||||||
|
this.institutionalFeaturesController,
|
||||||
|
),
|
||||||
|
removeAddTokenConnectRequest:
|
||||||
|
this.institutionalFeaturesController.removeAddTokenConnectRequest.bind(
|
||||||
|
this.institutionalFeaturesController,
|
||||||
|
),
|
||||||
|
setCustodianNewRefreshToken:
|
||||||
|
this.mmiController.setCustodianNewRefreshToken.bind(this),
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
|
||||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||||
// snaps
|
// snaps
|
||||||
removeSnapError: this.controllerMessenger.call.bind(
|
removeSnapError: this.controllerMessenger.call.bind(
|
||||||
@ -2666,6 +2828,10 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
async submitPassword(password) {
|
async submitPassword(password) {
|
||||||
await this.keyringController.submitPassword(password);
|
await this.keyringController.submitPassword(password);
|
||||||
|
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||||
|
this.mmiController.onSubmitPassword();
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.blockTracker.checkForLatestBlock();
|
await this.blockTracker.checkForLatestBlock();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -2785,6 +2951,16 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
return keyring.mnemonic;
|
return keyring.mnemonic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||||
|
async getCustodyKeyringIfExists(address) {
|
||||||
|
const custodyType = this.custodyController.getCustodyTypeByAddress(
|
||||||
|
toChecksumHexAddress(address),
|
||||||
|
);
|
||||||
|
const keyring = this.keyringController.getKeyringsByType(custodyType)[0];
|
||||||
|
return keyring?.getAccountDetails(address) ? keyring : undefined;
|
||||||
|
}
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
|
||||||
//
|
//
|
||||||
// Hardware
|
// Hardware
|
||||||
//
|
//
|
||||||
@ -3173,6 +3349,10 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
// Remove account from the account tracker controller
|
// Remove account from the account tracker controller
|
||||||
this.accountTracker.removeAccount([address]);
|
this.accountTracker.removeAccount([address]);
|
||||||
|
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||||
|
this.custodyController.removeAccount(address);
|
||||||
|
///: END:ONLY_INCLUDE_IN(build-mmi)
|
||||||
|
|
||||||
const keyring = await this.keyringController.getKeyringForAccount(address);
|
const keyring = await this.keyringController.getKeyringForAccount(address);
|
||||||
// Remove account from the keyring
|
// Remove account from the keyring
|
||||||
await this.keyringController.removeAccount(address);
|
await this.keyringController.removeAccount(address);
|
||||||
@ -3731,6 +3911,21 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
this.alertController.setWeb3ShimUsageRecorded.bind(
|
this.alertController.setWeb3ShimUsageRecorded.bind(
|
||||||
this.alertController,
|
this.alertController,
|
||||||
),
|
),
|
||||||
|
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||||
|
handleMmiAuthenticate:
|
||||||
|
this.institutionalFeaturesController.handleMmiAuthenticate.bind(
|
||||||
|
this.institutionalFeaturesController,
|
||||||
|
),
|
||||||
|
handleMmiCheckIfTokenIsPresent:
|
||||||
|
this.mmiController.handleMmiCheckIfTokenIsPresent.bind(this),
|
||||||
|
handleMmiPortfolio: this.mmiController.setMmiPortfolioCookie.bind(this),
|
||||||
|
handleMmiOpenSwaps: this.mmiController.handleMmiOpenSwaps.bind(this),
|
||||||
|
handleMmiSetAccountAndNetwork:
|
||||||
|
this.mmiController.setAccountAndNetwork.bind(this),
|
||||||
|
handleMmiOpenAddHardwareWallet:
|
||||||
|
this.mmiController.handleMmiOpenAddHardwareWallet.bind(this),
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
17
app/scripts/mmi-keyring-builder-factory.js
Normal file
17
app/scripts/mmi-keyring-builder-factory.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* Get builder function for MMI keyrings which require an additional `opts`
|
||||||
|
* parameter, used to pass MMI configuration.
|
||||||
|
*
|
||||||
|
* Returns a builder function for `Keyring` with a `type` property.
|
||||||
|
*
|
||||||
|
* @param {Keyring} Keyring - The Keyring class for the builder.
|
||||||
|
* @param {Keyring} opts - Optional parameters to be passed to the builder.
|
||||||
|
* @returns {Function} A builder function for the given Keyring.
|
||||||
|
*/
|
||||||
|
export function mmiKeyringBuilderFactory(Keyring, opts) {
|
||||||
|
const builder = () => new Keyring(opts);
|
||||||
|
|
||||||
|
builder.type = Keyring.type;
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
@ -6,10 +6,10 @@
|
|||||||
// subset of files to check against these targets.
|
// subset of files to check against these targets.
|
||||||
module.exports = {
|
module.exports = {
|
||||||
global: {
|
global: {
|
||||||
lines: 71.15,
|
lines: 70.94,
|
||||||
branches: 59.17,
|
branches: 59.11,
|
||||||
statements: 70.6,
|
statements: 70.38,
|
||||||
functions: 63.82,
|
functions: 63.65,
|
||||||
},
|
},
|
||||||
transforms: {
|
transforms: {
|
||||||
branches: 100,
|
branches: 100,
|
||||||
|
@ -37,6 +37,7 @@ module.exports = {
|
|||||||
testMatch: [
|
testMatch: [
|
||||||
'<rootDir>/app/scripts/constants/error-utils.test.js',
|
'<rootDir>/app/scripts/constants/error-utils.test.js',
|
||||||
'<rootDir>/app/scripts/controllers/app-state.test.js',
|
'<rootDir>/app/scripts/controllers/app-state.test.js',
|
||||||
|
'<rootDir>/app/scripts/controllers/mmi-controller.test.js',
|
||||||
'<rootDir>/app/scripts/controllers/network/**/*.test.js',
|
'<rootDir>/app/scripts/controllers/network/**/*.test.js',
|
||||||
'<rootDir>/app/scripts/controllers/network/**/*.test.ts',
|
'<rootDir>/app/scripts/controllers/network/**/*.test.ts',
|
||||||
'<rootDir>/app/scripts/controllers/permissions/**/*.test.js',
|
'<rootDir>/app/scripts/controllers/permissions/**/*.test.js',
|
||||||
|
@ -216,8 +216,13 @@
|
|||||||
"@keystonehq/metamask-airgapped-keyring": "^0.9.2",
|
"@keystonehq/metamask-airgapped-keyring": "^0.9.2",
|
||||||
"@lavamoat/snow": "^1.5.0",
|
"@lavamoat/snow": "^1.5.0",
|
||||||
"@material-ui/core": "^4.11.0",
|
"@material-ui/core": "^4.11.0",
|
||||||
|
"@metamask-institutional/custody-controller": "0.2.6",
|
||||||
|
"@metamask-institutional/custody-keyring": "0.0.23",
|
||||||
"@metamask-institutional/extension": "^0.1.3",
|
"@metamask-institutional/extension": "^0.1.3",
|
||||||
|
"@metamask-institutional/institutional-features": "^1.1.8",
|
||||||
"@metamask-institutional/portfolio-dashboard": "^1.1.3",
|
"@metamask-institutional/portfolio-dashboard": "^1.1.3",
|
||||||
|
"@metamask-institutional/sdk": "^0.1.17",
|
||||||
|
"@metamask-institutional/transaction-update": "^0.1.21",
|
||||||
"@metamask/address-book-controller": "^2.0.0",
|
"@metamask/address-book-controller": "^2.0.0",
|
||||||
"@metamask/announcement-controller": "^3.0.0",
|
"@metamask/announcement-controller": "^3.0.0",
|
||||||
"@metamask/approval-controller": "^2.1.0",
|
"@metamask/approval-controller": "^2.1.0",
|
||||||
|
@ -165,6 +165,18 @@ export const IN_PROGRESS_TRANSACTION_STATUSES = [
|
|||||||
TransactionStatus.pending,
|
TransactionStatus.pending,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||||
|
/**
|
||||||
|
* Status for finalized transactions.
|
||||||
|
*/
|
||||||
|
export const FINALIZED_TRANSACTION_STATUSES = [
|
||||||
|
TransactionStatus.rejected,
|
||||||
|
TransactionStatus.failed,
|
||||||
|
TransactionStatus.dropped,
|
||||||
|
TransactionStatus.confirmed,
|
||||||
|
];
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transaction Group Status is a MetaMask construct to track the status of groups
|
* Transaction Group Status is a MetaMask construct to track the status of groups
|
||||||
* of transactions.
|
* of transactions.
|
||||||
|
45
yarn.lock
45
yarn.lock
@ -62,6 +62,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@auth0/auth0-spa-js@npm:^2.0.4":
|
||||||
|
version: 2.0.5
|
||||||
|
resolution: "@auth0/auth0-spa-js@npm:2.0.5"
|
||||||
|
checksum: 3354ccfb2cd85657f0fe584f769cc2646c03f58fbc67859220dc9f8c359e9118b3f141b7ed9e75088b55cbaf047ee0351d3d00f7df00e108905259ab8ea6fe62
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@aw-web-design/x-default-browser@npm:1.4.88":
|
"@aw-web-design/x-default-browser@npm:1.4.88":
|
||||||
version: 1.4.88
|
version: 1.4.88
|
||||||
resolution: "@aw-web-design/x-default-browser@npm:1.4.88"
|
resolution: "@aw-web-design/x-default-browser@npm:1.4.88"
|
||||||
@ -3660,7 +3667,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@metamask-institutional/custody-controller@npm:^0.2.5":
|
"@metamask-institutional/custody-controller@npm:0.2.6, @metamask-institutional/custody-controller@npm:^0.2.5":
|
||||||
version: 0.2.6
|
version: 0.2.6
|
||||||
resolution: "@metamask-institutional/custody-controller@npm:0.2.6"
|
resolution: "@metamask-institutional/custody-controller@npm:0.2.6"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -3673,6 +3680,22 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@metamask-institutional/custody-keyring@npm:0.0.23":
|
||||||
|
version: 0.0.23
|
||||||
|
resolution: "@metamask-institutional/custody-keyring@npm:0.0.23"
|
||||||
|
dependencies:
|
||||||
|
"@ethereumjs/tx": ^4.1.1
|
||||||
|
"@ethereumjs/util": ^8.0.5
|
||||||
|
"@metamask-institutional/configuration-client": ^1.0.6
|
||||||
|
"@metamask-institutional/sdk": ^0.1.15
|
||||||
|
"@metamask-institutional/types": ^1.0.2
|
||||||
|
"@metamask/obs-store": ^8.0.0
|
||||||
|
crypto: ^1.0.1
|
||||||
|
lodash.clonedeep: ^4.5.0
|
||||||
|
checksum: eae1003ddfd262526d28c23d6dbdfbc6d42879bb989c9bcf97416d3964aaa6ce202a0803c46256fb1afc1399592176ba2fdfcbd2043c4f1275f5079e067c3a78
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@metamask-institutional/custody-keyring@npm:^0.0.22":
|
"@metamask-institutional/custody-keyring@npm:^0.0.22":
|
||||||
version: 0.0.22
|
version: 0.0.22
|
||||||
resolution: "@metamask-institutional/custody-keyring@npm:0.0.22"
|
resolution: "@metamask-institutional/custody-keyring@npm:0.0.22"
|
||||||
@ -3706,6 +3729,17 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@metamask-institutional/institutional-features@npm:^1.1.8":
|
||||||
|
version: 1.1.8
|
||||||
|
resolution: "@metamask-institutional/institutional-features@npm:1.1.8"
|
||||||
|
dependencies:
|
||||||
|
"@auth0/auth0-spa-js": ^2.0.4
|
||||||
|
"@metamask-institutional/custody-keyring": ^0.0.22
|
||||||
|
"@metamask/obs-store": ^8.0.0
|
||||||
|
checksum: 3942afd436673d19d16e139c4a989a7c099604b915ef63901ee77c52c9087848256d45db0b27950ac2181f9a9db16b231b3d9de89007c244db08b767668a617f
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@metamask-institutional/portfolio-dashboard@npm:^1.1.3":
|
"@metamask-institutional/portfolio-dashboard@npm:^1.1.3":
|
||||||
version: 1.1.3
|
version: 1.1.3
|
||||||
resolution: "@metamask-institutional/portfolio-dashboard@npm:1.1.3"
|
resolution: "@metamask-institutional/portfolio-dashboard@npm:1.1.3"
|
||||||
@ -3713,7 +3747,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@metamask-institutional/sdk@npm:^0.1.14, @metamask-institutional/sdk@npm:^0.1.15, @metamask-institutional/sdk@npm:^0.1.16":
|
"@metamask-institutional/sdk@npm:^0.1.14, @metamask-institutional/sdk@npm:^0.1.15, @metamask-institutional/sdk@npm:^0.1.16, @metamask-institutional/sdk@npm:^0.1.17":
|
||||||
version: 0.1.17
|
version: 0.1.17
|
||||||
resolution: "@metamask-institutional/sdk@npm:0.1.17"
|
resolution: "@metamask-institutional/sdk@npm:0.1.17"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -3734,7 +3768,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@metamask-institutional/transaction-update@npm:^0.1.20":
|
"@metamask-institutional/transaction-update@npm:^0.1.20, @metamask-institutional/transaction-update@npm:^0.1.21":
|
||||||
version: 0.1.21
|
version: 0.1.21
|
||||||
resolution: "@metamask-institutional/transaction-update@npm:0.1.21"
|
resolution: "@metamask-institutional/transaction-update@npm:0.1.21"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -23796,8 +23830,13 @@ __metadata:
|
|||||||
"@lavamoat/lavapack": ^5.0.0
|
"@lavamoat/lavapack": ^5.0.0
|
||||||
"@lavamoat/snow": ^1.5.0
|
"@lavamoat/snow": ^1.5.0
|
||||||
"@material-ui/core": ^4.11.0
|
"@material-ui/core": ^4.11.0
|
||||||
|
"@metamask-institutional/custody-controller": 0.2.6
|
||||||
|
"@metamask-institutional/custody-keyring": 0.0.23
|
||||||
"@metamask-institutional/extension": ^0.1.3
|
"@metamask-institutional/extension": ^0.1.3
|
||||||
|
"@metamask-institutional/institutional-features": ^1.1.8
|
||||||
"@metamask-institutional/portfolio-dashboard": ^1.1.3
|
"@metamask-institutional/portfolio-dashboard": ^1.1.3
|
||||||
|
"@metamask-institutional/sdk": ^0.1.17
|
||||||
|
"@metamask-institutional/transaction-update": ^0.1.21
|
||||||
"@metamask/address-book-controller": ^2.0.0
|
"@metamask/address-book-controller": ^2.0.0
|
||||||
"@metamask/announcement-controller": ^3.0.0
|
"@metamask/announcement-controller": ^3.0.0
|
||||||
"@metamask/approval-controller": ^2.1.0
|
"@metamask/approval-controller": ^2.1.0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user