1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

cancel unapproved confirmations on network change (#10357)

This commit is contained in:
Brad Decker 2021-02-08 17:22:30 -06:00 committed by GitHub
parent 6677bd9cce
commit 19fa2f5962
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 145 additions and 36 deletions

View File

@ -32,7 +32,9 @@ import LocalStore from './lib/local-store';
import ReadOnlyNetworkStore from './lib/network-store';
import createStreamSink from './lib/createStreamSink';
import NotificationManager from './lib/notification-manager';
import MetamaskController from './metamask-controller';
import MetamaskController, {
METAMASK_CONTROLLER_EVENTS,
} from './metamask-controller';
import rawFirstTimeState from './first-time-state';
import getFirstPreferredLangCode from './lib/get-first-preferred-lang-code';
import getObjStructure from './lib/getObjStructure';
@ -396,14 +398,35 @@ function setupController(initState, initLangCode) {
//
updateBadge();
controller.txController.on('update:badge', updateBadge);
controller.messageManager.on('updateBadge', updateBadge);
controller.personalMessageManager.on('updateBadge', updateBadge);
controller.decryptMessageManager.on('updateBadge', updateBadge);
controller.encryptionPublicKeyManager.on('updateBadge', updateBadge);
controller.typedMessageManager.on('updateBadge', updateBadge);
controller.txController.on(
METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE,
updateBadge,
);
controller.messageManager.on(
METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE,
updateBadge,
);
controller.personalMessageManager.on(
METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE,
updateBadge,
);
controller.decryptMessageManager.on(
METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE,
updateBadge,
);
controller.encryptionPublicKeyManager.on(
METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE,
updateBadge,
);
controller.typedMessageManager.on(
METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE,
updateBadge,
);
controller.approvalController.subscribe(updateBadge);
controller.appStateController.on('updateBadge', updateBadge);
controller.appStateController.on(
METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE,
updateBadge,
);
/**
* Updates the Web Extension's "badge" number, on the little fox in the toolbar.

View File

@ -1,5 +1,6 @@
import EventEmitter from 'events';
import { ObservableStore } from '@metamask/obs-store';
import { METAMASK_CONTROLLER_EVENTS } from '../metamask-controller';
export default class AppStateController extends EventEmitter {
/**
@ -74,7 +75,7 @@ export default class AppStateController extends EventEmitter {
*/
waitForUnlock(resolve, shouldShowUnlockRequest) {
this.waitingForUnlock.push({ resolve });
this.emit('updateBadge');
this.emit(METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE);
if (shouldShowUnlockRequest) {
this._showUnlockRequest();
}
@ -88,7 +89,7 @@ export default class AppStateController extends EventEmitter {
while (this.waitingForUnlock.length > 0) {
this.waitingForUnlock.shift().resolve();
}
this.emit('updateBadge');
this.emit(METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE);
}
}

View File

@ -23,6 +23,7 @@ import {
ROPSTEN,
ROPSTEN_CHAIN_ID,
} from '../../../shared/constants/network';
import { NETWORK_EVENTS } from './network';
const fetchWithTimeout = getFetchWithTimeout(30000);
@ -111,7 +112,7 @@ export default class IncomingTransactionsController {
}),
);
this.networkController.on('networkDidChange', async () => {
this.networkController.on(NETWORK_EVENTS.NETWORK_DID_CHANGE, async () => {
const address = this.preferencesController.getSelectedAddress();
await this._update({
address,

View File

@ -1 +1 @@
export { default } from './network';
export { default, NETWORK_EVENTS } from './network';

View File

@ -47,6 +47,13 @@ const defaultProviderConfig = {
...defaultProviderConfigOpts,
};
export const NETWORK_EVENTS = {
// Fired after the actively selected network is changed
NETWORK_DID_CHANGE: 'networkDidChange',
// Fired when the actively selected network *will* change
NETWORK_WILL_CHANGE: 'networkWillChange',
};
export default class NetworkController extends EventEmitter {
constructor(opts = {}) {
super();
@ -73,7 +80,7 @@ export default class NetworkController extends EventEmitter {
this._providerProxy = null;
this._blockTrackerProxy = null;
this.on('networkDidChange', this.lookupNetwork);
this.on(NETWORK_EVENTS.NETWORK_DID_CHANGE, this.lookupNetwork);
}
/**
@ -229,9 +236,10 @@ export default class NetworkController extends EventEmitter {
//
_switchNetwork(opts) {
this.emit(NETWORK_EVENTS.NETWORK_WILL_CHANGE);
this.setNetworkState('loading');
this._configureProvider(opts);
this.emit('networkDidChange', opts.type);
this.emit(NETWORK_EVENTS.NETWORK_DID_CHANGE, opts.type);
}
_configureProvider({ type, rpcUrl, chainId }) {

View File

@ -19,6 +19,7 @@ import {
fetchSwapsFeatureLiveness as defaultFetchSwapsFeatureLiveness,
fetchSwapsQuoteRefreshTime as defaultFetchSwapsQuoteRefreshTime,
} from '../../../ui/app/pages/swaps/swaps.util';
import { NETWORK_EVENTS } from './network';
const METASWAP_ADDRESS = '0x881d40237659c251811cec9c364ef91dc08d300c';
@ -103,7 +104,7 @@ export default class SwapsController {
this.ethersProvider = new ethers.providers.Web3Provider(provider);
this._currentNetwork = networkController.store.getState().network;
networkController.on('networkDidChange', (network) => {
networkController.on(NETWORK_EVENTS.NETWORK_DID_CHANGE, (network) => {
if (network !== 'loading' && network !== this._currentNetwork) {
this._currentNetwork = network;
this.ethersProvider = new ethers.providers.Web3Provider(provider);

View File

@ -23,6 +23,7 @@ import {
TRANSACTION_STATUSES,
TRANSACTION_TYPES,
} from '../../../../shared/constants/transaction';
import { METAMASK_CONTROLLER_EVENTS } from '../../metamask-controller';
import TransactionStateManager from './tx-state-manager';
import TxGasUtil from './tx-gas-utils';
import PendingTransactionTracker from './pending-tx-tracker';
@ -113,7 +114,9 @@ export default class TransactionController extends EventEmitter {
),
});
this.txStateManager.store.subscribe(() => this.emit('update:badge'));
this.txStateManager.store.subscribe(() =>
this.emit(METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE),
);
this._setupListeners();
// memstore is computed from a few different stores
this._updateMemstore();

View File

@ -3,6 +3,7 @@ import { ObservableStore } from '@metamask/obs-store';
import log from 'loglevel';
import createId from '../../lib/random-id';
import { TRANSACTION_STATUSES } from '../../../../shared/constants/transaction';
import { METAMASK_CONTROLLER_EVENTS } from '../../metamask-controller';
import {
generateHistoryEntry,
replayHistory,
@ -474,7 +475,7 @@ export default class TransactionStateManager extends EventEmitter {
* @param {TransactionStatuses[keyof TransactionStatuses]} status - the status to set on the txMeta
* @emits tx:status-update - passes txId and status
* @emits ${txMeta.id}:finished - if it is a finished state. Passes the txMeta
* @emits update:badge
* @emits 'updateBadge'
*/
_setTxStatus(txId, status) {
const txMeta = this.getTx(txId);
@ -497,7 +498,7 @@ export default class TransactionStateManager extends EventEmitter {
) {
this.emit(`${txMeta.id}:finished`, txMeta);
}
this.emit('update:badge');
this.emit(METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE);
} catch (error) {
log.error(error);
}

View File

@ -4,6 +4,7 @@ import ethUtil from 'ethereumjs-util';
import { ethErrors } from 'eth-rpc-errors';
import log from 'loglevel';
import { MESSAGE_TYPE } from '../../../shared/constants/app';
import { METAMASK_CONTROLLER_EVENTS } from '../metamask-controller';
import { addHexPrefix } from './util';
import createId from './random-id';
@ -253,6 +254,14 @@ export default class DecryptMessageManager extends EventEmitter {
this._setMsgStatus(msgId, 'errored');
}
/**
* Clears all unapproved messages from memory.
*/
clearUnapproved() {
this.messages = this.messages.filter((msg) => msg.status !== 'unapproved');
this._saveMsgList();
}
/**
* Updates the status of a DecryptMessage in this.messages via a call to this._updateMsg
*
@ -316,7 +325,7 @@ export default class DecryptMessageManager extends EventEmitter {
unapprovedDecryptMsgs,
unapprovedDecryptMsgCount,
});
this.emit('updateBadge');
this.emit(METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE);
}
/**

View File

@ -3,6 +3,7 @@ import { ObservableStore } from '@metamask/obs-store';
import { ethErrors } from 'eth-rpc-errors';
import log from 'loglevel';
import { MESSAGE_TYPE } from '../../../shared/constants/app';
import { METAMASK_CONTROLLER_EVENTS } from '../metamask-controller';
import createId from './random-id';
/**
@ -242,6 +243,14 @@ export default class EncryptionPublicKeyManager extends EventEmitter {
this._setMsgStatus(msgId, 'errored');
}
/**
* Clears all unapproved messages from memory.
*/
clearUnapproved() {
this.messages = this.messages.filter((msg) => msg.status !== 'unapproved');
this._saveMsgList();
}
/**
* Updates the status of a EncryptionPublicKey in this.messages via a call to this._updateMsg
*
@ -303,6 +312,6 @@ export default class EncryptionPublicKeyManager extends EventEmitter {
unapprovedEncryptionPublicKeyMsgs,
unapprovedEncryptionPublicKeyMsgCount,
});
this.emit('updateBadge');
this.emit(METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE);
}
}

View File

@ -3,6 +3,7 @@ import { ObservableStore } from '@metamask/obs-store';
import ethUtil from 'ethereumjs-util';
import { ethErrors } from 'eth-rpc-errors';
import { MESSAGE_TYPE } from '../../../shared/constants/app';
import { METAMASK_CONTROLLER_EVENTS } from '../metamask-controller';
import createId from './random-id';
/**
@ -220,6 +221,14 @@ export default class MessageManager extends EventEmitter {
this._setMsgStatus(msgId, 'rejected');
}
/**
* Clears all unapproved messages from memory.
*/
clearUnapproved() {
this.messages = this.messages.filter((msg) => msg.status !== 'unapproved');
this._saveMsgList();
}
/**
* Updates the status of a Message in this.messages via a call to this._updateMsg
*
@ -272,7 +281,7 @@ export default class MessageManager extends EventEmitter {
const unapprovedMsgs = this.getUnapprovedMsgs();
const unapprovedMsgCount = Object.keys(unapprovedMsgs).length;
this.memStore.updateState({ unapprovedMsgs, unapprovedMsgCount });
this.emit('updateBadge');
this.emit(METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE);
}
}

View File

@ -4,6 +4,7 @@ import ethUtil from 'ethereumjs-util';
import { ethErrors } from 'eth-rpc-errors';
import log from 'loglevel';
import { MESSAGE_TYPE } from '../../../shared/constants/app';
import { METAMASK_CONTROLLER_EVENTS } from '../metamask-controller';
import { addHexPrefix } from './util';
import createId from './random-id';
@ -241,6 +242,14 @@ export default class PersonalMessageManager extends EventEmitter {
this._setMsgStatus(msgId, 'rejected');
}
/**
* Clears all unapproved messages from memory.
*/
clearUnapproved() {
this.messages = this.messages.filter((msg) => msg.status !== 'unapproved');
this._saveMsgList();
}
/**
* Updates the status of a PersonalMessage in this.messages via a call to this._updateMsg
*
@ -301,7 +310,7 @@ export default class PersonalMessageManager extends EventEmitter {
unapprovedPersonalMsgs,
unapprovedPersonalMsgCount,
});
this.emit('updateBadge');
this.emit(METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE);
}
/**

View File

@ -7,6 +7,7 @@ import { isValidAddress } from 'ethereumjs-util';
import log from 'loglevel';
import jsonschema from 'jsonschema';
import { MESSAGE_TYPE } from '../../../shared/constants/app';
import { METAMASK_CONTROLLER_EVENTS } from '../metamask-controller';
import createId from './random-id';
/**
@ -313,6 +314,14 @@ export default class TypedMessageManager extends EventEmitter {
this._setMsgStatus(msgId, 'errored');
}
/**
* Clears all unapproved messages from memory.
*/
clearUnapproved() {
this.messages = this.messages.filter((msg) => msg.status !== 'unapproved');
this._saveMsgList();
}
//
// PRIVATE METHODS
//
@ -377,6 +386,6 @@ export default class TypedMessageManager extends EventEmitter {
unapprovedTypedMessages,
unapprovedTypedMessagesCount,
});
this.emit('updateBadge');
this.emit(METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE);
}
}

View File

@ -35,7 +35,7 @@ import createTabIdMiddleware from './lib/createTabIdMiddleware';
import createOnboardingMiddleware from './lib/createOnboardingMiddleware';
import { setupMultiplex } from './lib/stream-utils';
import EnsController from './controllers/ens';
import NetworkController from './controllers/network';
import NetworkController, { NETWORK_EVENTS } from './controllers/network';
import PreferencesController from './controllers/preferences';
import AppStateController from './controllers/app-state';
import CachedBalancesController from './controllers/cached-balances';
@ -61,6 +61,12 @@ import seedPhraseVerifier from './lib/seed-phrase-verifier';
import MetaMetricsController from './controllers/metametrics';
import { segment, segmentLegacy } from './lib/segment';
export const METAMASK_CONTROLLER_EVENTS = {
// Fired after state changes that impact the extension badge (unapproved msg count)
// The process of updating the badge happens in app/scripts/background.js.
UPDATE_BADGE: 'updateBadge',
};
export default class MetamaskController extends EventEmitter {
/**
* @constructor
@ -127,7 +133,7 @@ export default class MetamaskController extends EventEmitter {
preferencesStore: this.preferencesController.store,
onNetworkDidChange: this.networkController.on.bind(
this.networkController,
'networkDidChange',
NETWORK_EVENTS.NETWORK_DID_CHANGE,
),
getNetworkIdentifier: this.networkController.getNetworkIdentifier.bind(
this.networkController,
@ -214,11 +220,6 @@ export default class MetamaskController extends EventEmitter {
preferencesController: this.preferencesController,
});
// ensure accountTracker updates balances after network change
this.networkController.on('networkDidChange', () => {
this.accountTracker._updateAccounts();
});
const additionalKeyrings = [TrezorKeyring, LedgerBridgeKeyring];
this.keyringController = new KeyringController({
keyringTypes: additionalKeyrings,
@ -323,7 +324,7 @@ export default class MetamaskController extends EventEmitter {
}
});
this.networkController.on('networkDidChange', () => {
this.networkController.on(NETWORK_EVENTS.NETWORK_DID_CHANGE, () => {
this.setCurrentCurrency(
this.currencyRateController.state.currentCurrency,
(error) => {
@ -357,6 +358,21 @@ export default class MetamaskController extends EventEmitter {
tokenRatesStore: this.tokenRatesController.store,
});
// ensure accountTracker updates balances after network change
this.networkController.on(NETWORK_EVENTS.NETWORK_DID_CHANGE, () => {
this.accountTracker._updateAccounts();
});
// clear unapproved transactions and messages when the network will change
this.networkController.on(NETWORK_EVENTS.NETWORK_WILL_CHANGE, () => {
this.txController.txStateManager.clearUnapprovedTxs();
this.encryptionPublicKeyManager.clearUnapproved();
this.personalMessageManager.clearUnapproved();
this.typedMessageManager.clearUnapproved();
this.decryptMessageManager.clearUnapproved();
this.messageManager.clearUnapproved();
});
// ensure isClientOpenAndUnlocked is updated when memState updates
this.on('update', (memState) => this._onStateUpdate(memState));

View File

@ -19,6 +19,7 @@ import {
TRANSACTION_CATEGORIES,
TRANSACTION_STATUSES,
} from '../../../../shared/constants/transaction';
import { NETWORK_EVENTS } from '../../../../app/scripts/controllers/network';
const IncomingTransactionsController = proxyquire(
'../../../../app/scripts/controllers/incoming-transactions',
@ -161,7 +162,7 @@ describe('IncomingTransactionsController', function () {
assert(incomingTransactionsController.networkController.on.calledOnce);
assert.equal(
incomingTransactionsController.networkController.on.getCall(0).args[0],
'networkDidChange',
NETWORK_EVENTS.NETWORK_DID_CHANGE,
);
const networkControllerListenerCallback = incomingTransactionsController.networkController.on.getCall(
0,

View File

@ -8,6 +8,7 @@ import {
METAMETRICS_BACKGROUND_PAGE_OBJECT,
} from '../../../../shared/constants/metametrics';
import waitUntilCalled from '../../../lib/wait-until-called';
import { NETWORK_EVENTS } from '../../../../app/scripts/controllers/network';
const segment = createSegmentMock(2, 10000);
const segmentLegacy = createSegmentMock(2, 10000);
@ -49,7 +50,7 @@ function getMockNetworkController(
provider = { type: NETWORK },
) {
let networkStore = { chainId, provider };
const on = sinon.stub().withArgs('networkDidChange');
const on = sinon.stub().withArgs(NETWORK_EVENTS.NETWORK_DID_CHANGE);
const updateState = (newState) => {
networkStore = { ...networkStore, ...newState };
on.getCall(0).args[1]();
@ -99,7 +100,7 @@ function getMetaMetricsController({
),
onNetworkDidChange: networkController.on.bind(
networkController,
'networkDidChange',
NETWORK_EVENTS.NETWORK_DID_CHANGE,
),
preferencesStore,
version: '0.0.1',

View File

@ -14,6 +14,7 @@ import { createTestProviderTools } from '../../../stub/provider';
import SwapsController, {
utils,
} from '../../../../app/scripts/controllers/swaps';
import { NETWORK_EVENTS } from '../../../../app/scripts/controllers/network';
const MOCK_FETCH_PARAMS = {
slippage: 3,
@ -101,7 +102,10 @@ function getMockNetworkController() {
};
},
},
on: sinon.stub().withArgs('networkDidChange').callsArgAsync(1),
on: sinon
.stub()
.withArgs(NETWORK_EVENTS.NETWORK_DID_CHANGE)
.callsArgAsync(1),
};
}

View File

@ -15,6 +15,7 @@ import {
TRANSACTION_STATUSES,
TRANSACTION_TYPES,
} from '../../../../../shared/constants/transaction';
import { METAMASK_CONTROLLER_EVENTS } from '../../../../../app/scripts/metamask-controller';
const noop = () => true;
const currentNetworkId = '42';
@ -395,7 +396,10 @@ describe('Transaction Controller', function () {
txParams: {},
};
const eventNames = ['update:badge', '1:unapproved'];
const eventNames = [
METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE,
'1:unapproved',
];
const listeners = [];
eventNames.forEach((eventName) => {
listeners.push(