1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-22 09:57:02 +01:00

Enforce user preferences in incoming transactions controller (#19982)

* Enforce user preferences in incoming transactions controller

* Combine various conditions to determine tx lookup

* Update tests to not use private methods
This commit is contained in:
Nicholas Ellul 2023-07-18 16:21:30 -04:00 committed by GitHub
parent be469db9a0
commit 9469435dd0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 132 additions and 15 deletions

View File

@ -135,15 +135,12 @@ export default class IncomingTransactionsController {
} }
start() { start() {
const { featureFlags = {} } = this.preferencesController.store.getState(); const chainId = this.getCurrentChainId();
const { showIncomingTransactions } = featureFlags;
if (!showIncomingTransactions) { if (this._allowedToMakeFetchIncomingTx(chainId)) {
return; this.blockTracker.removeListener('latest', this._onLatestBlock);
this.blockTracker.addListener('latest', this._onLatestBlock);
} }
this.blockTracker.removeListener('latest', this._onLatestBlock);
this.blockTracker.addListener('latest', this._onLatestBlock);
} }
stop() { stop() {
@ -161,13 +158,9 @@ export default class IncomingTransactionsController {
* @param {number} [newBlockNumberDec] - block number to begin fetching from * @param {number} [newBlockNumberDec] - block number to begin fetching from
*/ */
async _update(address, newBlockNumberDec) { async _update(address, newBlockNumberDec) {
const { completedOnboarding } = this.onboardingController.store.getState();
const chainId = this.getCurrentChainId(); const chainId = this.getCurrentChainId();
if (
!Object.hasOwnProperty.call(ETHERSCAN_SUPPORTED_NETWORKS, chainId) || if (!address || !this._allowedToMakeFetchIncomingTx(chainId)) {
!address ||
!completedOnboarding
) {
return; return;
} }
try { try {
@ -302,4 +295,26 @@ export default class IncomingTransactionsController {
type: TransactionType.incoming, type: TransactionType.incoming,
}; };
} }
/**
* @param chainId - {string} The chainId of the current network
* @returns {boolean} Whether or not the user has consented to show incoming transactions
*/
_allowedToMakeFetchIncomingTx(chainId) {
const { featureFlags = {} } = this.preferencesController.store.getState();
const { completedOnboarding } = this.onboardingController.store.getState();
const hasIncomingTransactionsFeatureEnabled = Boolean(
featureFlags.showIncomingTransactions,
);
const isEtherscanSupportedNetwork = Boolean(
ETHERSCAN_SUPPORTED_NETWORKS[chainId],
);
return (
completedOnboarding &&
isEtherscanSupportedNetwork &&
hasIncomingTransactionsFeatureEnabled
);
}
} }

View File

@ -78,11 +78,11 @@ function getMockPreferencesController({
}; };
} }
function getMockOnboardingController() { function getMockOnboardingController({ completedOnboarding = true } = {}) {
return { return {
store: { store: {
getState: sinon.stub().returns({ getState: sinon.stub().returns({
completedOnboarding: true, completedOnboarding,
}), }),
subscribe: sinon.spy(), subscribe: sinon.spy(),
}, },
@ -98,6 +98,16 @@ function getMockBlockTracker() {
}; };
} }
function getDefaultControllerOpts() {
return {
blockTracker: getMockBlockTracker(),
...getMockNetworkControllerMethods(CHAIN_IDS.GOERLI),
preferencesController: getMockPreferencesController(),
onboardingController: getMockOnboardingController(),
initState: getEmptyInitState(),
};
}
/** /**
* @typedef {import( * @typedef {import(
* '../../../../app/scripts/controllers/incoming-transactions' * '../../../../app/scripts/controllers/incoming-transactions'
@ -226,6 +236,7 @@ describe('IncomingTransactionsController', function () {
preferencesController: getMockPreferencesController(), preferencesController: getMockPreferencesController(),
onboardingController: getMockOnboardingController(), onboardingController: getMockOnboardingController(),
initState: {}, initState: {},
getCurrentChainId: () => CHAIN_IDS.GOERLI,
}, },
); );
@ -831,6 +842,97 @@ describe('IncomingTransactionsController', function () {
}); });
}); });
describe('block explorer lookup', function () {
let sandbox;
beforeEach(function () {
sandbox = sinon.createSandbox();
});
afterEach(function () {
sandbox.restore();
});
function stubFetch() {
return sandbox.stub(window, 'fetch');
}
function assertStubNotCalled(stub) {
assert(stub.callCount === 0);
}
async function triggerUpdate(incomingTransactionsController) {
const subscription =
incomingTransactionsController.preferencesController.store.subscribe.getCall(
1,
).args[0];
// Sets address causing a call to _update
await subscription({ selectedAddress: MOCK_SELECTED_ADDRESS });
}
it('should not happen when incoming transactions feature is disabled', async function () {
const incomingTransactionsController = new IncomingTransactionsController(
{
...getDefaultControllerOpts(),
preferencesController: getMockPreferencesController({
showIncomingTransactions: false,
}),
},
);
const fetchStub = stubFetch();
await triggerUpdate(incomingTransactionsController);
assertStubNotCalled(fetchStub);
});
it('should not happen when onboarding is in progress', async function () {
const incomingTransactionsController = new IncomingTransactionsController(
{
...getDefaultControllerOpts(),
onboardingController: getMockOnboardingController({
completedOnboarding: false,
}),
},
);
const fetchStub = stubFetch();
await triggerUpdate(incomingTransactionsController);
assertStubNotCalled(fetchStub);
});
it('should not happen when chain id is not supported', async function () {
const incomingTransactionsController = new IncomingTransactionsController(
{
...getDefaultControllerOpts(),
getCurrentChainId: () => FAKE_CHAIN_ID,
},
);
const fetchStub = stubFetch();
await triggerUpdate(incomingTransactionsController);
assertStubNotCalled(fetchStub);
});
it('should make api call when chain id, incoming features, and onboarding status are ok', async function () {
const incomingTransactionsController = new IncomingTransactionsController(
{
...getDefaultControllerOpts(),
getCurrentChainId: () => CHAIN_IDS.GOERLI,
onboardingController: getMockOnboardingController({
completedOnboarding: true,
}),
preferencesController: getMockPreferencesController({
showIncomingTransactions: true,
}),
},
);
const fetchStub = stubFetch();
await triggerUpdate(incomingTransactionsController);
assert(fetchStub.callCount === 1);
});
});
describe('_update', function () { describe('_update', function () {
describe('when state is empty (initialized)', function () { describe('when state is empty (initialized)', function () {
it('should use provided block number and update the latest block seen', async function () { it('should use provided block number and update the latest block seen', async function () {