mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Keep memstore contents after service worker restarts (#15913)
* Add all controllers in memstore to store Add methods to controller to reset memstore Reset memstore when popup or tab is closed. Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * When profile is loaded, set isFirstTime to true.. After resetting the controllers, set the flag to false. Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * Remove console.logs Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * For some reason programmatically computing the store is not working. Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * Proper check for browser.storage Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * do a list of rest methods instead of reset controllers. Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * Mock controller resetStates and localstore get/set Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * Comments about TLC Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * bind this. Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * use globalThis instead of locastore to store first time state. Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * Test to check that resetStates is not called a second time Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * Set init state in GasFeeController and other controllers so that their state is persisted accross SW restarts Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * Revert localstore changes Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * wrap the reset states changes in MV3 flag Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * Remove localstore from metamask-controller Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * Always reset state on MMController start in MV2. Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * Use relative path for import of isManifestV3 Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * Fix unit test Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>
This commit is contained in:
parent
4042519441
commit
086a7d0483
@ -127,6 +127,10 @@ chrome.runtime.onMessage.addListener(() => {
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
chrome.runtime.onStartup.addListener(() => {
|
||||||
|
globalThis.isFirstTimeProfileLoaded = true;
|
||||||
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This content script is injected programmatically because
|
* This content script is injected programmatically because
|
||||||
* MAIN world injection does not work properly via manifest
|
* MAIN world injection does not work properly via manifest
|
||||||
|
@ -27,6 +27,11 @@ export default class EnsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.store = new ObservableStore(initState);
|
this.store = new ObservableStore(initState);
|
||||||
|
|
||||||
|
this.resetState = () => {
|
||||||
|
this.store.updateState(initState);
|
||||||
|
};
|
||||||
|
|
||||||
onNetworkDidChange(() => {
|
onNetworkDidChange(() => {
|
||||||
this.store.putState(initState);
|
this.store.putState(initState);
|
||||||
const chainId = getCurrentChainId();
|
const chainId = getCurrentChainId();
|
||||||
|
@ -115,6 +115,10 @@ export default class SwapsController {
|
|||||||
swapsState: { ...initialState.swapsState },
|
swapsState: { ...initialState.swapsState },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.resetState = () => {
|
||||||
|
this.store.updateState({ swapsState: { ...initialState.swapsState } });
|
||||||
|
};
|
||||||
|
|
||||||
this._fetchTradesInfo = fetchTradesInfo;
|
this._fetchTradesInfo = fetchTradesInfo;
|
||||||
this._getCurrentChainId = getCurrentChainId;
|
this._getCurrentChainId = getCurrentChainId;
|
||||||
this._getEIP1559GasFeeEstimates = getEIP1559GasFeeEstimates;
|
this._getEIP1559GasFeeEstimates = getEIP1559GasFeeEstimates;
|
||||||
|
@ -151,6 +151,11 @@ export default class TransactionController extends EventEmitter {
|
|||||||
this.getTokenStandardAndDetails = opts.getTokenStandardAndDetails;
|
this.getTokenStandardAndDetails = opts.getTokenStandardAndDetails;
|
||||||
|
|
||||||
this.memStore = new ObservableStore({});
|
this.memStore = new ObservableStore({});
|
||||||
|
|
||||||
|
this.resetState = () => {
|
||||||
|
this._updateMemstore();
|
||||||
|
};
|
||||||
|
|
||||||
this.query = new EthQuery(this.provider);
|
this.query = new EthQuery(this.provider);
|
||||||
|
|
||||||
this.txGasUtil = new TxGasUtil(this.provider);
|
this.txGasUtil = new TxGasUtil(this.provider);
|
||||||
|
@ -62,6 +62,10 @@ export default class AccountTracker {
|
|||||||
};
|
};
|
||||||
this.store = new ObservableStore(initState);
|
this.store = new ObservableStore(initState);
|
||||||
|
|
||||||
|
this.resetState = () => {
|
||||||
|
this.store.updateState(initState);
|
||||||
|
};
|
||||||
|
|
||||||
this._provider = opts.provider;
|
this._provider = opts.provider;
|
||||||
this._query = pify(new EthQuery(this._provider));
|
this._query = pify(new EthQuery(this._provider));
|
||||||
this._blockTracker = opts.blockTracker;
|
this._blockTracker = opts.blockTracker;
|
||||||
|
@ -41,6 +41,14 @@ export default class DecryptMessageManager extends EventEmitter {
|
|||||||
unapprovedDecryptMsgs: {},
|
unapprovedDecryptMsgs: {},
|
||||||
unapprovedDecryptMsgCount: 0,
|
unapprovedDecryptMsgCount: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.resetState = () => {
|
||||||
|
this.memStore.updateState({
|
||||||
|
unapprovedDecryptMsgs: {},
|
||||||
|
unapprovedDecryptMsgCount: 0,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
this.messages = [];
|
this.messages = [];
|
||||||
this.metricsEvent = opts.metricsEvent;
|
this.metricsEvent = opts.metricsEvent;
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,14 @@ export default class EncryptionPublicKeyManager extends EventEmitter {
|
|||||||
unapprovedEncryptionPublicKeyMsgs: {},
|
unapprovedEncryptionPublicKeyMsgs: {},
|
||||||
unapprovedEncryptionPublicKeyMsgCount: 0,
|
unapprovedEncryptionPublicKeyMsgCount: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.resetState = () => {
|
||||||
|
this.memStore.updateState({
|
||||||
|
unapprovedEncryptionPublicKeyMsgs: {},
|
||||||
|
unapprovedEncryptionPublicKeyMsgCount: 0,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
this.messages = [];
|
this.messages = [];
|
||||||
this.metricsEvent = opts.metricsEvent;
|
this.metricsEvent = opts.metricsEvent;
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,14 @@ export default class MessageManager extends EventEmitter {
|
|||||||
unapprovedMsgs: {},
|
unapprovedMsgs: {},
|
||||||
unapprovedMsgCount: 0,
|
unapprovedMsgCount: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.resetState = () => {
|
||||||
|
this.memStore.updateState({
|
||||||
|
unapprovedMsgs: {},
|
||||||
|
unapprovedMsgCount: 0,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
this.messages = [];
|
this.messages = [];
|
||||||
this.metricsEvent = metricsEvent;
|
this.metricsEvent = metricsEvent;
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,14 @@ export default class PersonalMessageManager extends EventEmitter {
|
|||||||
unapprovedPersonalMsgs: {},
|
unapprovedPersonalMsgs: {},
|
||||||
unapprovedPersonalMsgCount: 0,
|
unapprovedPersonalMsgCount: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.resetState = () => {
|
||||||
|
this.memStore.updateState({
|
||||||
|
unapprovedPersonalMsgs: {},
|
||||||
|
unapprovedPersonalMsgCount: 0,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
this.messages = [];
|
this.messages = [];
|
||||||
this.metricsEvent = metricsEvent;
|
this.metricsEvent = metricsEvent;
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,14 @@ export default class TypedMessageManager extends EventEmitter {
|
|||||||
unapprovedTypedMessages: {},
|
unapprovedTypedMessages: {},
|
||||||
unapprovedTypedMessagesCount: 0,
|
unapprovedTypedMessagesCount: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.resetState = () => {
|
||||||
|
this.memStore.updateState({
|
||||||
|
unapprovedTypedMessages: {},
|
||||||
|
unapprovedTypedMessagesCount: 0,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
this.messages = [];
|
this.messages = [];
|
||||||
this.metricsEvent = metricsEvent;
|
this.metricsEvent = metricsEvent;
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,12 @@ const browserPolyfillMock = {
|
|||||||
},
|
},
|
||||||
getPlatformInfo: async () => 'mac',
|
getPlatformInfo: async () => 'mac',
|
||||||
},
|
},
|
||||||
|
storage: {
|
||||||
|
local: {
|
||||||
|
get: sinon.stub().resolves({}),
|
||||||
|
set: sinon.stub().resolves(),
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let loggerMiddlewareMock;
|
let loggerMiddlewareMock;
|
||||||
|
@ -100,6 +100,7 @@ import {
|
|||||||
getTokenValueParam,
|
getTokenValueParam,
|
||||||
hexToDecimal,
|
hexToDecimal,
|
||||||
} from '../../shared/lib/metamask-controller-utils';
|
} from '../../shared/lib/metamask-controller-utils';
|
||||||
|
import { isManifestV3 } from '../../shared/modules/mv3.utils';
|
||||||
import {
|
import {
|
||||||
onMessageReceived,
|
onMessageReceived,
|
||||||
checkForMultipleVersionsRunning,
|
checkForMultipleVersionsRunning,
|
||||||
@ -417,6 +418,7 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
: GAS_API_BASE_URL;
|
: GAS_API_BASE_URL;
|
||||||
|
|
||||||
this.gasFeeController = new GasFeeController({
|
this.gasFeeController = new GasFeeController({
|
||||||
|
state: initState.GasFeeController,
|
||||||
interval: 10000,
|
interval: 10000,
|
||||||
messenger: gasFeeMessenger,
|
messenger: gasFeeMessenger,
|
||||||
clientId: SWAPS_CLIENT_ID,
|
clientId: SWAPS_CLIENT_ID,
|
||||||
@ -481,26 +483,30 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// token exchange rate tracker
|
// token exchange rate tracker
|
||||||
this.tokenRatesController = new TokenRatesController({
|
this.tokenRatesController = new TokenRatesController(
|
||||||
onTokensStateChange: (listener) =>
|
{
|
||||||
this.tokensController.subscribe(listener),
|
onTokensStateChange: (listener) =>
|
||||||
onCurrencyRateStateChange: (listener) =>
|
this.tokensController.subscribe(listener),
|
||||||
this.controllerMessenger.subscribe(
|
onCurrencyRateStateChange: (listener) =>
|
||||||
`${this.currencyRateController.name}:stateChange`,
|
this.controllerMessenger.subscribe(
|
||||||
listener,
|
`${this.currencyRateController.name}:stateChange`,
|
||||||
),
|
listener,
|
||||||
onNetworkStateChange: (cb) =>
|
),
|
||||||
this.networkController.store.subscribe((networkState) => {
|
onNetworkStateChange: (cb) =>
|
||||||
const modifiedNetworkState = {
|
this.networkController.store.subscribe((networkState) => {
|
||||||
...networkState,
|
const modifiedNetworkState = {
|
||||||
provider: {
|
...networkState,
|
||||||
...networkState.provider,
|
provider: {
|
||||||
chainId: hexToDecimal(networkState.provider.chainId),
|
...networkState.provider,
|
||||||
},
|
chainId: hexToDecimal(networkState.provider.chainId),
|
||||||
};
|
},
|
||||||
return cb(modifiedNetworkState);
|
};
|
||||||
}),
|
return cb(modifiedNetworkState);
|
||||||
});
|
}),
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
initState.TokenRatesController,
|
||||||
|
);
|
||||||
|
|
||||||
this.ensController = new EnsController({
|
this.ensController = new EnsController({
|
||||||
provider: this.provider,
|
provider: this.provider,
|
||||||
@ -719,6 +725,7 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.rateLimitController = new RateLimitController({
|
this.rateLimitController = new RateLimitController({
|
||||||
|
state: initState.RateLimitController,
|
||||||
messenger: this.controllerMessenger.getRestricted({
|
messenger: this.controllerMessenger.getRestricted({
|
||||||
name: 'RateLimitController',
|
name: 'RateLimitController',
|
||||||
}),
|
}),
|
||||||
@ -1029,6 +1036,24 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
// ensure isClientOpenAndUnlocked is updated when memState updates
|
// ensure isClientOpenAndUnlocked is updated when memState updates
|
||||||
this.on('update', (memState) => this._onStateUpdate(memState));
|
this.on('update', (memState) => this._onStateUpdate(memState));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All controllers in Memstore but not in store. They are not persisted.
|
||||||
|
* On chrome profile re-start, they will be re-initialized.
|
||||||
|
*/
|
||||||
|
const resetOnRestartStore = {
|
||||||
|
AccountTracker: this.accountTracker.store,
|
||||||
|
TxController: this.txController.memStore,
|
||||||
|
TokenRatesController: this.tokenRatesController,
|
||||||
|
MessageManager: this.messageManager.memStore,
|
||||||
|
PersonalMessageManager: this.personalMessageManager.memStore,
|
||||||
|
DecryptMessageManager: this.decryptMessageManager.memStore,
|
||||||
|
EncryptionPublicKeyManager: this.encryptionPublicKeyManager.memStore,
|
||||||
|
TypesMessageManager: this.typedMessageManager.memStore,
|
||||||
|
SwapsController: this.swapsController.store,
|
||||||
|
EnsController: this.ensController.store,
|
||||||
|
ApprovalController: this.approvalController,
|
||||||
|
};
|
||||||
|
|
||||||
this.store.updateStructure({
|
this.store.updateStructure({
|
||||||
AppStateController: this.appStateController.store,
|
AppStateController: this.appStateController.store,
|
||||||
TransactionController: this.txController.store,
|
TransactionController: this.txController.store,
|
||||||
@ -1057,21 +1082,14 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
CronjobController: this.cronjobController,
|
CronjobController: this.cronjobController,
|
||||||
NotificationController: this.notificationController,
|
NotificationController: this.notificationController,
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
...resetOnRestartStore,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.memStore = new ComposableObservableStore({
|
this.memStore = new ComposableObservableStore({
|
||||||
config: {
|
config: {
|
||||||
AppStateController: this.appStateController.store,
|
AppStateController: this.appStateController.store,
|
||||||
NetworkController: this.networkController.store,
|
NetworkController: this.networkController.store,
|
||||||
AccountTracker: this.accountTracker.store,
|
|
||||||
TxController: this.txController.memStore,
|
|
||||||
CachedBalancesController: this.cachedBalancesController.store,
|
CachedBalancesController: this.cachedBalancesController.store,
|
||||||
TokenRatesController: this.tokenRatesController,
|
|
||||||
MessageManager: this.messageManager.memStore,
|
|
||||||
PersonalMessageManager: this.personalMessageManager.memStore,
|
|
||||||
DecryptMessageManager: this.decryptMessageManager.memStore,
|
|
||||||
EncryptionPublicKeyManager: this.encryptionPublicKeyManager.memStore,
|
|
||||||
TypesMessageManager: this.typedMessageManager.memStore,
|
|
||||||
KeyringController: this.keyringController.memStore,
|
KeyringController: this.keyringController.memStore,
|
||||||
PreferencesController: this.preferencesController.store,
|
PreferencesController: this.preferencesController.store,
|
||||||
MetaMetricsController: this.metaMetricsController.store,
|
MetaMetricsController: this.metaMetricsController.store,
|
||||||
@ -1085,9 +1103,6 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
PermissionLogController: this.permissionLogController.store,
|
PermissionLogController: this.permissionLogController.store,
|
||||||
SubjectMetadataController: this.subjectMetadataController,
|
SubjectMetadataController: this.subjectMetadataController,
|
||||||
BackupController: this.backupController,
|
BackupController: this.backupController,
|
||||||
SwapsController: this.swapsController.store,
|
|
||||||
EnsController: this.ensController.store,
|
|
||||||
ApprovalController: this.approvalController,
|
|
||||||
AnnouncementController: this.announcementController,
|
AnnouncementController: this.announcementController,
|
||||||
GasFeeController: this.gasFeeController,
|
GasFeeController: this.gasFeeController,
|
||||||
TokenListController: this.tokenListController,
|
TokenListController: this.tokenListController,
|
||||||
@ -1099,11 +1114,36 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
CronjobController: this.cronjobController,
|
CronjobController: this.cronjobController,
|
||||||
NotificationController: this.notificationController,
|
NotificationController: this.notificationController,
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
...resetOnRestartStore,
|
||||||
},
|
},
|
||||||
controllerMessenger: this.controllerMessenger,
|
controllerMessenger: this.controllerMessenger,
|
||||||
});
|
});
|
||||||
this.memStore.subscribe(this.sendUpdate.bind(this));
|
this.memStore.subscribe(this.sendUpdate.bind(this));
|
||||||
|
|
||||||
|
// if this is the first time, clear the state of by calling these methods
|
||||||
|
const resetMethods = [
|
||||||
|
this.accountTracker.resetState,
|
||||||
|
this.txController.resetState,
|
||||||
|
this.messageManager.resetState,
|
||||||
|
this.personalMessageManager.resetState,
|
||||||
|
this.decryptMessageManager.resetState,
|
||||||
|
this.encryptionPublicKeyManager.resetState,
|
||||||
|
this.typedMessageManager.resetState,
|
||||||
|
this.swapsController.resetState,
|
||||||
|
this.ensController.resetState,
|
||||||
|
this.approvalController.clear.bind(this.approvalController),
|
||||||
|
// WE SHOULD ADD TokenListController.resetState here too. But it's not implemented yet.
|
||||||
|
];
|
||||||
|
|
||||||
|
if (isManifestV3) {
|
||||||
|
if (globalThis.isFirstTimeProfileLoaded === true) {
|
||||||
|
this.resetStates(resetMethods);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// it's always the first time in MV2
|
||||||
|
this.resetStates(resetMethods);
|
||||||
|
}
|
||||||
|
|
||||||
const password = process.env.CONF?.PASSWORD;
|
const password = process.env.CONF?.PASSWORD;
|
||||||
if (
|
if (
|
||||||
password &&
|
password &&
|
||||||
@ -1137,6 +1177,18 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
checkForMultipleVersionsRunning();
|
checkForMultipleVersionsRunning();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetStates(resetMethods) {
|
||||||
|
resetMethods.forEach((resetMethod) => {
|
||||||
|
try {
|
||||||
|
resetMethod();
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
globalThis.isFirstTimeProfileLoaded = false;
|
||||||
|
}
|
||||||
|
|
||||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||||
/**
|
/**
|
||||||
* Constructor helper for getting Snap permission specifications.
|
* Constructor helper for getting Snap permission specifications.
|
||||||
|
@ -102,11 +102,14 @@ const CUSTOM_RPC_CHAIN_ID = '0x539';
|
|||||||
|
|
||||||
describe('MetaMaskController', function () {
|
describe('MetaMaskController', function () {
|
||||||
let metamaskController;
|
let metamaskController;
|
||||||
|
|
||||||
const sandbox = sinon.createSandbox();
|
const sandbox = sinon.createSandbox();
|
||||||
const noop = () => undefined;
|
const noop = () => undefined;
|
||||||
|
|
||||||
before(async function () {
|
before(async function () {
|
||||||
|
globalThis.isFirstTimeProfileLoaded = true;
|
||||||
await ganacheServer.start();
|
await ganacheServer.start();
|
||||||
|
sinon.spy(MetaMaskController.prototype, 'resetStates');
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
@ -160,6 +163,18 @@ describe('MetaMaskController', function () {
|
|||||||
await ganacheServer.quit();
|
await ganacheServer.quit();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('should reset states on first time profile load', function () {
|
||||||
|
it('should reset state', function () {
|
||||||
|
assert(metamaskController.resetStates.calledOnce);
|
||||||
|
assert.equal(globalThis.isFirstTimeProfileLoaded, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not reset states if already set', function () {
|
||||||
|
// global.isFirstTime should also remain false
|
||||||
|
assert.equal(globalThis.isFirstTimeProfileLoaded, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('#getAccounts', function () {
|
describe('#getAccounts', function () {
|
||||||
it('returns first address when dapp calls web3.eth.getAccounts', async function () {
|
it('returns first address when dapp calls web3.eth.getAccounts', async function () {
|
||||||
const password = 'a-fake-password';
|
const password = 'a-fake-password';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user