mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Merge pull request #11423 from MetaMask/Version-v9.7.1
Version v9.7.1 RC
This commit is contained in:
commit
bd38b02d8e
@ -108,7 +108,11 @@ module.exports = {
|
||||
},
|
||||
{
|
||||
files: ['**/*.test.js'],
|
||||
excludedFiles: ['ui/**/*.test.js', 'ui/__mocks__/*.js'],
|
||||
excludedFiles: [
|
||||
'ui/**/*.test.js',
|
||||
'ui/__mocks__/*.js',
|
||||
'shared/**/*.test.js',
|
||||
],
|
||||
extends: ['@metamask/eslint-config-mocha'],
|
||||
rules: {
|
||||
'mocha/no-setup-in-describe': 'off',
|
||||
@ -125,7 +129,7 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['ui/**/*.test.js', 'ui/__mocks__/*.js'],
|
||||
files: ['ui/**/*.test.js', 'ui/__mocks__/*.js', 'shared/**/*.test.js'],
|
||||
extends: ['@metamask/eslint-config-jest'],
|
||||
rules: {
|
||||
'jest/no-restricted-matchers': 'off',
|
||||
|
@ -140,7 +140,6 @@ const state = {
|
||||
}
|
||||
},
|
||||
"participateInMetaMetrics": true,
|
||||
"metaMetricsSendCount": 2,
|
||||
"nextNonce": 71,
|
||||
"connectedStatusPopoverHasBeenShown": true,
|
||||
"swapsWelcomeMessageHasBeenShown": true,
|
||||
|
@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [9.7.1]
|
||||
### Fixed
|
||||
- [#11426](https://github.com/MetaMask/metamask-extension/pull/11426): Fixed bug that broke transaction speed up and cancel, when attempting those actions immediately after opening MetaMask
|
||||
|
||||
## [9.7.0]
|
||||
### Added
|
||||
- [#11021](https://github.com/MetaMask/metamask-extension/pull/11021): Add periodic reminder modal for backing up recovery phrase
|
||||
@ -2317,7 +2321,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Uncategorized
|
||||
- Added the ability to restore accounts from seed words.
|
||||
|
||||
[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v9.7.0...HEAD
|
||||
[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v9.7.1...HEAD
|
||||
[9.7.1]: https://github.com/MetaMask/metamask-extension/compare/v9.7.0...v9.7.1
|
||||
[9.7.0]: https://github.com/MetaMask/metamask-extension/compare/v9.6.1...v9.7.0
|
||||
[9.6.1]: https://github.com/MetaMask/metamask-extension/compare/v9.6.0...v9.6.1
|
||||
[9.6.0]: https://github.com/MetaMask/metamask-extension/compare/v9.5.9...v9.6.0
|
||||
|
@ -7,30 +7,6 @@ import {
|
||||
METAMETRICS_BACKGROUND_PAGE_OBJECT,
|
||||
} from '../../../shared/constants/metametrics';
|
||||
|
||||
/**
|
||||
* Used to determine whether or not to attach a user's metametrics id
|
||||
* to events that include on-chain data. This helps to prevent identifying
|
||||
* a user by being able to trace their activity on etherscan/block exploring
|
||||
*/
|
||||
const trackableSendCounts = {
|
||||
1: true,
|
||||
10: true,
|
||||
30: true,
|
||||
50: true,
|
||||
100: true,
|
||||
250: true,
|
||||
500: true,
|
||||
1000: true,
|
||||
2500: true,
|
||||
5000: true,
|
||||
10000: true,
|
||||
25000: true,
|
||||
};
|
||||
|
||||
export function sendCountIsTrackable(sendCount) {
|
||||
return Boolean(trackableSendCounts[sendCount]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {import('../../../shared/constants/metametrics').MetaMetricsContext} MetaMetricsContext
|
||||
* @typedef {import('../../../shared/constants/metametrics').MetaMetricsEventPayload} MetaMetricsEventPayload
|
||||
@ -48,9 +24,6 @@ export function sendCountIsTrackable(sendCount) {
|
||||
* @property {?boolean} participateInMetaMetrics - The user's preference for
|
||||
* participating in the MetaMetrics analytics program. This setting controls
|
||||
* whether or not events are tracked
|
||||
* @property {number} metaMetricsSendCount - How many send transactions have
|
||||
* been tracked through this controller. Used to prevent attaching sensitive
|
||||
* data that can be traced through on chain data.
|
||||
*/
|
||||
|
||||
export default class MetaMetricsController {
|
||||
@ -89,7 +62,6 @@ export default class MetaMetricsController {
|
||||
this.store = new ObservableStore({
|
||||
participateInMetaMetrics: null,
|
||||
metaMetricsId: null,
|
||||
metaMetricsSendCount: 0,
|
||||
...initState,
|
||||
});
|
||||
|
||||
@ -138,10 +110,6 @@ export default class MetaMetricsController {
|
||||
return this.store.getState();
|
||||
}
|
||||
|
||||
setMetaMetricsSendCount(val) {
|
||||
this.store.updateState({ metaMetricsSendCount: val });
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the context object to attach to page and track events.
|
||||
* @private
|
||||
@ -231,11 +199,7 @@ export default class MetaMetricsController {
|
||||
// to be updated to work with the new tracking plan. I think we should use
|
||||
// a config setting for this instead of trying to match the event name
|
||||
const isSendFlow = Boolean(payload.event.match(/^send|^confirm/iu));
|
||||
if (
|
||||
isSendFlow &&
|
||||
this.state.metaMetricsSendCount &&
|
||||
!sendCountIsTrackable(this.state.metaMetricsSendCount + 1)
|
||||
) {
|
||||
if (isSendFlow) {
|
||||
excludeMetaMetricsId = true;
|
||||
}
|
||||
// If we are tracking sensitive data we will always use the anonymousId
|
||||
|
@ -84,7 +84,6 @@ function getMockPreferencesStore({ currentLocale = LOCALE } = {}) {
|
||||
function getMetaMetricsController({
|
||||
participateInMetaMetrics = true,
|
||||
metaMetricsId = TEST_META_METRICS_ID,
|
||||
metaMetricsSendCount = 0,
|
||||
preferencesStore = getMockPreferencesStore(),
|
||||
networkController = getMockNetworkController(),
|
||||
} = {}) {
|
||||
@ -106,7 +105,6 @@ function getMetaMetricsController({
|
||||
initState: {
|
||||
participateInMetaMetrics,
|
||||
metaMetricsId,
|
||||
metaMetricsSendCount,
|
||||
},
|
||||
});
|
||||
}
|
||||
@ -198,14 +196,6 @@ describe('MetaMetricsController', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('setMetaMetricsSendCount', function () {
|
||||
it('should update the send count in state', function () {
|
||||
const metaMetricsController = getMetaMetricsController();
|
||||
metaMetricsController.setMetaMetricsSendCount(1);
|
||||
assert.equal(metaMetricsController.state.metaMetricsSendCount, 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('trackEvent', function () {
|
||||
it('should not track an event if user is not participating in metametrics', function () {
|
||||
const mock = sinon.mock(segment);
|
||||
@ -337,61 +327,6 @@ describe('MetaMetricsController', function () {
|
||||
mock.verify();
|
||||
});
|
||||
|
||||
it('should use anonymousId when metametrics send count is not trackable in send flow', function () {
|
||||
const mock = sinon.mock(segment);
|
||||
const metaMetricsController = getMetaMetricsController({
|
||||
metaMetricsSendCount: 1,
|
||||
});
|
||||
mock
|
||||
.expects('track')
|
||||
.once()
|
||||
.withArgs({
|
||||
event: 'Send Fake Event',
|
||||
anonymousId: METAMETRICS_ANONYMOUS_ID,
|
||||
context: DEFAULT_TEST_CONTEXT,
|
||||
properties: {
|
||||
test: 1,
|
||||
...DEFAULT_EVENT_PROPERTIES,
|
||||
},
|
||||
});
|
||||
metaMetricsController.trackEvent({
|
||||
event: 'Send Fake Event',
|
||||
category: 'Unit Test',
|
||||
properties: {
|
||||
test: 1,
|
||||
},
|
||||
});
|
||||
mock.verify();
|
||||
});
|
||||
|
||||
it('should use user metametrics id when metametrics send count is trackable in send flow', function () {
|
||||
const mock = sinon.mock(segment);
|
||||
const metaMetricsController = getMetaMetricsController();
|
||||
mock
|
||||
.expects('track')
|
||||
.once()
|
||||
.withArgs({
|
||||
event: 'Send Fake Event',
|
||||
userId: TEST_META_METRICS_ID,
|
||||
context: DEFAULT_TEST_CONTEXT,
|
||||
properties: {
|
||||
test: 1,
|
||||
...DEFAULT_EVENT_PROPERTIES,
|
||||
},
|
||||
});
|
||||
metaMetricsController.trackEvent(
|
||||
{
|
||||
event: 'Send Fake Event',
|
||||
category: 'Unit Test',
|
||||
properties: {
|
||||
test: 1,
|
||||
},
|
||||
},
|
||||
{ metaMetricsSendCount: 0 },
|
||||
);
|
||||
mock.verify();
|
||||
});
|
||||
|
||||
it('should immediately flush queue if flushImmediately set to true', async function () {
|
||||
const metaMetricsController = getMetaMetricsController();
|
||||
const flushStub = sinon.stub(segment, 'flush');
|
||||
|
@ -285,7 +285,7 @@ export default class PreferencesController {
|
||||
*/
|
||||
syncAddresses(addresses) {
|
||||
if (!Array.isArray(addresses) || addresses.length === 0) {
|
||||
throw new Error('Expected non-empty array of addresses.');
|
||||
throw new Error('Expected non-empty array of addresses. Error #11201');
|
||||
}
|
||||
|
||||
const { identities, lostIdentities } = this.store.getState();
|
||||
|
@ -24,6 +24,7 @@ import {
|
||||
} from '../../../../shared/constants/transaction';
|
||||
import { METAMASK_CONTROLLER_EVENTS } from '../../metamask-controller';
|
||||
import { GAS_LIMITS } from '../../../../shared/constants/gas';
|
||||
import { isEIP1559Transaction } from '../../../../shared/modules/transaction.utils';
|
||||
import TransactionStateManager from './tx-state-manager';
|
||||
import TxGasUtil from './tx-gas-utils';
|
||||
import PendingTransactionTracker from './pending-tx-tracker';
|
||||
@ -33,6 +34,14 @@ const hstInterface = new ethers.utils.Interface(abi);
|
||||
|
||||
const MAX_MEMSTORE_TX_LIST_SIZE = 100; // Number of transactions (by unique nonces) to keep in memory
|
||||
|
||||
export const TRANSACTION_EVENTS = {
|
||||
ADDED: 'Transaction Added',
|
||||
APPROVED: 'Transaction Approved',
|
||||
FINALIZED: 'Transaction Finalized',
|
||||
REJECTED: 'Transaction Rejected',
|
||||
SUBMITTED: 'Transaction Submitted',
|
||||
};
|
||||
|
||||
/**
|
||||
Transaction Controller is an aggregate of sub-controllers and trackers
|
||||
composing them in a way to be exposed to the metamask controller
|
||||
@ -153,6 +162,7 @@ export default class TransactionController extends EventEmitter {
|
||||
addTransaction(txMeta) {
|
||||
this.txStateManager.addTransaction(txMeta);
|
||||
this.emit(`${txMeta.id}:unapproved`, txMeta);
|
||||
this._trackTransactionMetricsEvent(txMeta, TRANSACTION_EVENTS.ADDED);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -532,12 +542,13 @@ export default class TransactionController extends EventEmitter {
|
||||
// sign transaction
|
||||
const rawTx = await this.signTransaction(txId);
|
||||
await this.publishTransaction(txId, rawTx);
|
||||
this._trackTransactionMetricsEvent(txMeta, TRANSACTION_EVENTS.APPROVED);
|
||||
// must set transaction to submitted/failed before releasing lock
|
||||
nonceLock.releaseLock();
|
||||
} catch (err) {
|
||||
// this is try-catch wrapped so that we can guarantee that the nonceLock is released
|
||||
try {
|
||||
this.txStateManager.setTxStatusFailed(txId, err);
|
||||
this._failTransaction(txId, err);
|
||||
} catch (err2) {
|
||||
log.error(err2);
|
||||
}
|
||||
@ -615,6 +626,8 @@ export default class TransactionController extends EventEmitter {
|
||||
this.setTxHash(txId, txHash);
|
||||
|
||||
this.txStateManager.setTxStatusSubmitted(txId);
|
||||
|
||||
this._trackTransactionMetricsEvent(txMeta, TRANSACTION_EVENTS.SUBMITTED);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -647,6 +660,29 @@ export default class TransactionController extends EventEmitter {
|
||||
this.txStateManager.setTxStatusConfirmed(txId);
|
||||
this._markNonceDuplicatesDropped(txId);
|
||||
|
||||
const { submittedTime } = txMeta;
|
||||
const { blockNumber } = txReceipt;
|
||||
const metricsParams = { gas_used: gasUsed };
|
||||
const completionTime = await this._getTransactionCompletionTime(
|
||||
blockNumber,
|
||||
submittedTime,
|
||||
);
|
||||
|
||||
if (completionTime) {
|
||||
metricsParams.completion_time = completionTime;
|
||||
}
|
||||
|
||||
if (txReceipt.status === '0x0') {
|
||||
metricsParams.status = 'failed on-chain';
|
||||
// metricsParams.error = TODO: figure out a way to get the on-chain failure reason
|
||||
}
|
||||
|
||||
this._trackTransactionMetricsEvent(
|
||||
txMeta,
|
||||
TRANSACTION_EVENTS.FINALIZED,
|
||||
metricsParams,
|
||||
);
|
||||
|
||||
this.txStateManager.updateTransaction(
|
||||
txMeta,
|
||||
'transactions#confirmTransaction - add txReceipt',
|
||||
@ -680,7 +716,9 @@ export default class TransactionController extends EventEmitter {
|
||||
@returns {Promise<void>}
|
||||
*/
|
||||
async cancelTransaction(txId) {
|
||||
const txMeta = this.txStateManager.getTransaction(txId);
|
||||
this.txStateManager.setTxStatusRejected(txId);
|
||||
this._trackTransactionMetricsEvent(txMeta, TRANSACTION_EVENTS.REJECTED);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -763,7 +801,7 @@ export default class TransactionController extends EventEmitter {
|
||||
txMeta,
|
||||
'failed to estimate gas during boot cleanup.',
|
||||
);
|
||||
this.txStateManager.setTxStatusFailed(txMeta.id, error);
|
||||
this._failTransaction(txMeta.id, error);
|
||||
});
|
||||
});
|
||||
|
||||
@ -777,7 +815,7 @@ export default class TransactionController extends EventEmitter {
|
||||
const txSignError = new Error(
|
||||
'Transaction found as "approved" during boot - possibly stuck during signing',
|
||||
);
|
||||
this.txStateManager.setTxStatusFailed(txMeta.id, txSignError);
|
||||
this._failTransaction(txMeta.id, txSignError);
|
||||
});
|
||||
}
|
||||
|
||||
@ -797,17 +835,15 @@ export default class TransactionController extends EventEmitter {
|
||||
'transactions/pending-tx-tracker#event: tx:warning',
|
||||
);
|
||||
});
|
||||
this.pendingTxTracker.on(
|
||||
'tx:failed',
|
||||
this.txStateManager.setTxStatusFailed.bind(this.txStateManager),
|
||||
);
|
||||
this.pendingTxTracker.on('tx:failed', (txId, error) => {
|
||||
this._failTransaction(txId, error);
|
||||
});
|
||||
this.pendingTxTracker.on('tx:confirmed', (txId, transactionReceipt) =>
|
||||
this.confirmTransaction(txId, transactionReceipt),
|
||||
);
|
||||
this.pendingTxTracker.on(
|
||||
'tx:dropped',
|
||||
this.txStateManager.setTxStatusDropped.bind(this.txStateManager),
|
||||
);
|
||||
this.pendingTxTracker.on('tx:dropped', (txId) => {
|
||||
this._dropTransaction(txId);
|
||||
});
|
||||
this.pendingTxTracker.on('tx:block-update', (txMeta, latestBlockNumber) => {
|
||||
if (!txMeta.firstRetryBlockNumber) {
|
||||
txMeta.firstRetryBlockNumber = latestBlockNumber;
|
||||
@ -917,7 +953,7 @@ export default class TransactionController extends EventEmitter {
|
||||
txMeta,
|
||||
'transactions/pending-tx-tracker#event: tx:confirmed reference to confirmed txHash with same nonce',
|
||||
);
|
||||
this.txStateManager.setTxStatusDropped(otherTxMeta.id);
|
||||
this._dropTransaction(otherTxMeta.id);
|
||||
});
|
||||
}
|
||||
|
||||
@ -1010,4 +1046,84 @@ export default class TransactionController extends EventEmitter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts relevant properties from a transaction meta
|
||||
* object and uses them to create and send metrics for various transaction
|
||||
* events.
|
||||
* @param {Object} txMeta - the txMeta object
|
||||
* @param {string} event - the name of the transaction event
|
||||
* @param {Object} extraParams - optional props and values to include in sensitiveProperties
|
||||
*/
|
||||
_trackTransactionMetricsEvent(txMeta, event, extraParams = {}) {
|
||||
const {
|
||||
type,
|
||||
time,
|
||||
status,
|
||||
chainId,
|
||||
origin: referrer,
|
||||
txParams: { gasPrice, gas: gasLimit, maxFeePerGas, maxPriorityFeePerGas },
|
||||
metamaskNetworkId: network,
|
||||
} = txMeta;
|
||||
const source = referrer === 'metamask' ? 'user' : 'dapp';
|
||||
|
||||
const gasParams = {};
|
||||
|
||||
if (isEIP1559Transaction(txMeta)) {
|
||||
gasParams.max_fee_per_gas = maxFeePerGas;
|
||||
gasParams.max_priority_fee_per_gas = maxPriorityFeePerGas;
|
||||
} else {
|
||||
gasParams.gas_price = gasPrice;
|
||||
}
|
||||
|
||||
this._trackMetaMetricsEvent({
|
||||
event,
|
||||
category: 'Transactions',
|
||||
sensitiveProperties: {
|
||||
type,
|
||||
status,
|
||||
referrer,
|
||||
source,
|
||||
network,
|
||||
chain_id: chainId,
|
||||
transaction_envelope_type: isEIP1559Transaction(txMeta)
|
||||
? 'fee-market'
|
||||
: 'legacy',
|
||||
first_seen: time,
|
||||
gas_limit: gasLimit,
|
||||
...gasParams,
|
||||
...extraParams,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async _getTransactionCompletionTime(blockNumber, submittedTime) {
|
||||
const transactionBlock = await this.query.getBlockByNumber(
|
||||
blockNumber.toString(16),
|
||||
false,
|
||||
);
|
||||
|
||||
if (!transactionBlock) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return new BigNumber(transactionBlock.timestamp, 10)
|
||||
.minus(submittedTime / 1000)
|
||||
.round()
|
||||
.toString(10);
|
||||
}
|
||||
|
||||
_failTransaction(txId, error) {
|
||||
this.txStateManager.setTxStatusFailed(txId, error);
|
||||
const txMeta = this.txStateManager.getTransaction(txId);
|
||||
this._trackTransactionMetricsEvent(txMeta, TRANSACTION_EVENTS.FINALIZED, {
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
|
||||
_dropTransaction(txId) {
|
||||
this.txStateManager.setTxStatusDropped(txId);
|
||||
const txMeta = this.txStateManager.getTransaction(txId);
|
||||
this._trackTransactionMetricsEvent(txMeta, TRANSACTION_EVENTS.FINALIZED);
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ import {
|
||||
} from '../../../../shared/constants/transaction';
|
||||
import { SECOND } from '../../../../shared/constants/time';
|
||||
import { METAMASK_CONTROLLER_EVENTS } from '../../metamask-controller';
|
||||
import TransactionController from '.';
|
||||
import TransactionController, { TRANSACTION_EVENTS } from '.';
|
||||
|
||||
const noop = () => true;
|
||||
const currentNetworkId = '42';
|
||||
@ -56,6 +56,7 @@ describe('Transaction Controller', function () {
|
||||
getPermittedAccounts: () => undefined,
|
||||
getCurrentChainId: () => currentChainId,
|
||||
getParticipateInMetrics: () => false,
|
||||
trackMetaMetricsEvent: () => undefined,
|
||||
});
|
||||
txController.nonceTracker.getNonceLock = () =>
|
||||
Promise.resolve({ nextNonce: 0, releaseLock: noop });
|
||||
@ -414,6 +415,19 @@ describe('Transaction Controller', function () {
|
||||
});
|
||||
|
||||
describe('#addTransaction', function () {
|
||||
let trackTransactionMetricsEventSpy;
|
||||
|
||||
beforeEach(function () {
|
||||
trackTransactionMetricsEventSpy = sinon.spy(
|
||||
txController,
|
||||
'_trackTransactionMetricsEvent',
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
trackTransactionMetricsEventSpy.restore();
|
||||
});
|
||||
|
||||
it('should emit updates', function (done) {
|
||||
const txMeta = {
|
||||
id: '1',
|
||||
@ -451,6 +465,38 @@ describe('Transaction Controller', function () {
|
||||
.catch(done);
|
||||
txController.addTransaction(txMeta);
|
||||
});
|
||||
|
||||
it('should call _trackTransactionMetricsEvent with the correct params', function () {
|
||||
const txMeta = {
|
||||
id: 1,
|
||||
status: TRANSACTION_STATUSES.UNAPPROVED,
|
||||
txParams: {
|
||||
from: fromAccount.address,
|
||||
to: '0x1678a085c290ebd122dc42cba69373b5953b831d',
|
||||
gasPrice: '0x77359400',
|
||||
gas: '0x7b0d',
|
||||
nonce: '0x4b',
|
||||
},
|
||||
type: 'sentEther',
|
||||
transaction_envelope_type: 'legacy',
|
||||
origin: 'metamask',
|
||||
chainId: currentChainId,
|
||||
time: 1624408066355,
|
||||
metamaskNetworkId: currentNetworkId,
|
||||
};
|
||||
|
||||
txController.addTransaction(txMeta);
|
||||
|
||||
assert.equal(trackTransactionMetricsEventSpy.callCount, 1);
|
||||
assert.deepEqual(
|
||||
trackTransactionMetricsEventSpy.getCall(0).args[0],
|
||||
txMeta,
|
||||
);
|
||||
assert.equal(
|
||||
trackTransactionMetricsEventSpy.getCall(0).args[1],
|
||||
TRANSACTION_EVENTS.ADDED,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#approveTransaction', function () {
|
||||
@ -723,7 +769,8 @@ describe('Transaction Controller', function () {
|
||||
});
|
||||
|
||||
describe('#publishTransaction', function () {
|
||||
let hash, txMeta;
|
||||
let hash, txMeta, trackTransactionMetricsEventSpy;
|
||||
|
||||
beforeEach(function () {
|
||||
hash =
|
||||
'0x2a5523c6fa98b47b7d9b6c8320179785150b42a16bcff36b398c5062b65657e8';
|
||||
@ -731,12 +778,21 @@ describe('Transaction Controller', function () {
|
||||
id: 1,
|
||||
status: TRANSACTION_STATUSES.UNAPPROVED,
|
||||
txParams: {
|
||||
gas: '0x7b0d',
|
||||
to: VALID_ADDRESS,
|
||||
from: VALID_ADDRESS_TWO,
|
||||
},
|
||||
metamaskNetworkId: currentNetworkId,
|
||||
};
|
||||
providerResultStub.eth_sendRawTransaction = hash;
|
||||
trackTransactionMetricsEventSpy = sinon.spy(
|
||||
txController,
|
||||
'_trackTransactionMetricsEvent',
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
trackTransactionMetricsEventSpy.restore();
|
||||
});
|
||||
|
||||
it('should publish a tx, updates the rawTx when provided a one', async function () {
|
||||
@ -764,6 +820,22 @@ describe('Transaction Controller', function () {
|
||||
);
|
||||
assert.equal(publishedTx.status, TRANSACTION_STATUSES.SUBMITTED);
|
||||
});
|
||||
|
||||
it('should call _trackTransactionMetricsEvent with the correct params', async function () {
|
||||
const rawTx =
|
||||
'0x477b2e6553c917af0db0388ae3da62965ff1a184558f61b749d1266b2e6d024c';
|
||||
txController.txStateManager.addTransaction(txMeta);
|
||||
await txController.publishTransaction(txMeta.id, rawTx);
|
||||
assert.equal(trackTransactionMetricsEventSpy.callCount, 1);
|
||||
assert.deepEqual(
|
||||
trackTransactionMetricsEventSpy.getCall(0).args[0],
|
||||
txMeta,
|
||||
);
|
||||
assert.equal(
|
||||
trackTransactionMetricsEventSpy.getCall(0).args[1],
|
||||
TRANSACTION_EVENTS.SUBMITTED,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#_markNonceDuplicatesDropped', function () {
|
||||
@ -1105,4 +1177,213 @@ describe('Transaction Controller', function () {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#_trackTransactionMetricsEvent', function () {
|
||||
let trackMetaMetricsEventSpy;
|
||||
|
||||
beforeEach(function () {
|
||||
trackMetaMetricsEventSpy = sinon.spy(
|
||||
txController,
|
||||
'_trackMetaMetricsEvent',
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
trackMetaMetricsEventSpy.restore();
|
||||
});
|
||||
|
||||
it('should call _trackMetaMetricsEvent with the correct payload (user source)', function () {
|
||||
const txMeta = {
|
||||
id: 1,
|
||||
status: TRANSACTION_STATUSES.UNAPPROVED,
|
||||
txParams: {
|
||||
from: fromAccount.address,
|
||||
to: '0x1678a085c290ebd122dc42cba69373b5953b831d',
|
||||
gasPrice: '0x77359400',
|
||||
gas: '0x7b0d',
|
||||
nonce: '0x4b',
|
||||
},
|
||||
type: 'sentEther',
|
||||
origin: 'metamask',
|
||||
chainId: currentChainId,
|
||||
time: 1624408066355,
|
||||
metamaskNetworkId: currentNetworkId,
|
||||
};
|
||||
const expectedPayload = {
|
||||
event: 'Transaction Added',
|
||||
category: 'Transactions',
|
||||
sensitiveProperties: {
|
||||
chain_id: '0x2a',
|
||||
gas_price: '0x77359400',
|
||||
gas_limit: '0x7b0d',
|
||||
first_seen: 1624408066355,
|
||||
transaction_envelope_type: 'legacy',
|
||||
network: '42',
|
||||
referrer: 'metamask',
|
||||
source: 'user',
|
||||
status: 'unapproved',
|
||||
type: 'sentEther',
|
||||
},
|
||||
};
|
||||
|
||||
txController._trackTransactionMetricsEvent(
|
||||
txMeta,
|
||||
TRANSACTION_EVENTS.ADDED,
|
||||
);
|
||||
assert.equal(trackMetaMetricsEventSpy.callCount, 1);
|
||||
assert.deepEqual(
|
||||
trackMetaMetricsEventSpy.getCall(0).args[0],
|
||||
expectedPayload,
|
||||
);
|
||||
});
|
||||
|
||||
it('should call _trackMetaMetricsEvent with the correct payload (dapp source)', function () {
|
||||
const txMeta = {
|
||||
id: 1,
|
||||
status: TRANSACTION_STATUSES.UNAPPROVED,
|
||||
txParams: {
|
||||
from: fromAccount.address,
|
||||
to: '0x1678a085c290ebd122dc42cba69373b5953b831d',
|
||||
gasPrice: '0x77359400',
|
||||
gas: '0x7b0d',
|
||||
nonce: '0x4b',
|
||||
},
|
||||
type: 'sentEther',
|
||||
origin: 'other',
|
||||
chainId: currentChainId,
|
||||
time: 1624408066355,
|
||||
metamaskNetworkId: currentNetworkId,
|
||||
};
|
||||
const expectedPayload = {
|
||||
event: 'Transaction Added',
|
||||
category: 'Transactions',
|
||||
sensitiveProperties: {
|
||||
chain_id: '0x2a',
|
||||
gas_price: '0x77359400',
|
||||
gas_limit: '0x7b0d',
|
||||
first_seen: 1624408066355,
|
||||
transaction_envelope_type: 'legacy',
|
||||
network: '42',
|
||||
referrer: 'other',
|
||||
source: 'dapp',
|
||||
status: 'unapproved',
|
||||
type: 'sentEther',
|
||||
},
|
||||
};
|
||||
|
||||
txController._trackTransactionMetricsEvent(
|
||||
txMeta,
|
||||
TRANSACTION_EVENTS.ADDED,
|
||||
);
|
||||
assert.equal(trackMetaMetricsEventSpy.callCount, 1);
|
||||
assert.deepEqual(
|
||||
trackMetaMetricsEventSpy.getCall(0).args[0],
|
||||
expectedPayload,
|
||||
);
|
||||
});
|
||||
|
||||
it('should call _trackMetaMetricsEvent with the correct payload (extra params)', function () {
|
||||
const txMeta = {
|
||||
id: 1,
|
||||
status: TRANSACTION_STATUSES.UNAPPROVED,
|
||||
txParams: {
|
||||
from: fromAccount.address,
|
||||
to: '0x1678a085c290ebd122dc42cba69373b5953b831d',
|
||||
gasPrice: '0x77359400',
|
||||
gas: '0x7b0d',
|
||||
nonce: '0x4b',
|
||||
},
|
||||
type: 'sentEther',
|
||||
origin: 'other',
|
||||
chainId: currentChainId,
|
||||
time: 1624408066355,
|
||||
metamaskNetworkId: currentNetworkId,
|
||||
};
|
||||
const expectedPayload = {
|
||||
event: 'Transaction Added',
|
||||
category: 'Transactions',
|
||||
sensitiveProperties: {
|
||||
baz: 3.0,
|
||||
foo: 'bar',
|
||||
chain_id: '0x2a',
|
||||
gas_price: '0x77359400',
|
||||
gas_limit: '0x7b0d',
|
||||
first_seen: 1624408066355,
|
||||
transaction_envelope_type: 'legacy',
|
||||
network: '42',
|
||||
referrer: 'other',
|
||||
source: 'dapp',
|
||||
status: 'unapproved',
|
||||
type: 'sentEther',
|
||||
},
|
||||
};
|
||||
|
||||
txController._trackTransactionMetricsEvent(
|
||||
txMeta,
|
||||
TRANSACTION_EVENTS.ADDED,
|
||||
{
|
||||
baz: 3.0,
|
||||
foo: 'bar',
|
||||
},
|
||||
);
|
||||
assert.equal(trackMetaMetricsEventSpy.callCount, 1);
|
||||
assert.deepEqual(
|
||||
trackMetaMetricsEventSpy.getCall(0).args[0],
|
||||
expectedPayload,
|
||||
);
|
||||
});
|
||||
|
||||
it('should call _trackMetaMetricsEvent with the correct payload (EIP-1559)', function () {
|
||||
const txMeta = {
|
||||
id: 1,
|
||||
status: TRANSACTION_STATUSES.UNAPPROVED,
|
||||
txParams: {
|
||||
from: fromAccount.address,
|
||||
to: '0x1678a085c290ebd122dc42cba69373b5953b831d',
|
||||
maxFeePerGas: '0x77359400',
|
||||
maxPriorityFeePerGas: '0x77359400',
|
||||
gas: '0x7b0d',
|
||||
nonce: '0x4b',
|
||||
},
|
||||
type: 'sentEther',
|
||||
origin: 'other',
|
||||
chainId: currentChainId,
|
||||
time: 1624408066355,
|
||||
metamaskNetworkId: currentNetworkId,
|
||||
};
|
||||
const expectedPayload = {
|
||||
event: 'Transaction Added',
|
||||
category: 'Transactions',
|
||||
sensitiveProperties: {
|
||||
baz: 3.0,
|
||||
foo: 'bar',
|
||||
chain_id: '0x2a',
|
||||
max_fee_per_gas: '0x77359400',
|
||||
max_priority_fee_per_gas: '0x77359400',
|
||||
gas_limit: '0x7b0d',
|
||||
first_seen: 1624408066355,
|
||||
transaction_envelope_type: 'fee-market',
|
||||
network: '42',
|
||||
referrer: 'other',
|
||||
source: 'dapp',
|
||||
status: 'unapproved',
|
||||
type: 'sentEther',
|
||||
},
|
||||
};
|
||||
|
||||
txController._trackTransactionMetricsEvent(
|
||||
txMeta,
|
||||
TRANSACTION_EVENTS.ADDED,
|
||||
{
|
||||
baz: 3.0,
|
||||
foo: 'bar',
|
||||
},
|
||||
);
|
||||
assert.equal(trackMetaMetricsEventSpy.callCount, 1);
|
||||
assert.deepEqual(
|
||||
trackMetaMetricsEventSpy.getCall(0).args[0],
|
||||
expectedPayload,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -36,7 +36,6 @@ export const SENTRY_STATE = {
|
||||
isInitialized: true,
|
||||
isUnlocked: true,
|
||||
metaMetricsId: true,
|
||||
metaMetricsSendCount: true,
|
||||
nativeCurrency: true,
|
||||
network: true,
|
||||
nextNonce: true,
|
||||
|
@ -676,7 +676,6 @@ export default class MetamaskController extends EventEmitter {
|
||||
setUsePhishDetect: this.setUsePhishDetect.bind(this),
|
||||
setIpfsGateway: this.setIpfsGateway.bind(this),
|
||||
setParticipateInMetaMetrics: this.setParticipateInMetaMetrics.bind(this),
|
||||
setMetaMetricsSendCount: this.setMetaMetricsSendCount.bind(this),
|
||||
setFirstTimeFlowType: this.setFirstTimeFlowType.bind(this),
|
||||
setCurrentLocale: this.setCurrentLocale.bind(this),
|
||||
markPasswordForgotten: this.markPasswordForgotten.bind(this),
|
||||
@ -2760,18 +2759,6 @@ export default class MetamaskController extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
setMetaMetricsSendCount(val, cb) {
|
||||
try {
|
||||
this.metaMetricsController.setMetaMetricsSendCount(val);
|
||||
cb(null);
|
||||
return;
|
||||
} catch (err) {
|
||||
cb(err);
|
||||
// eslint-disable-next-line no-useless-return
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type of first time flow the user wishes to follow: create or import
|
||||
* @param {string} type - Indicates the type of first time flow the user wishes to follow
|
||||
|
28
app/scripts/migrations/062.js
Normal file
28
app/scripts/migrations/062.js
Normal file
@ -0,0 +1,28 @@
|
||||
import { cloneDeep } from 'lodash';
|
||||
|
||||
const version = 62;
|
||||
|
||||
/**
|
||||
* Removes metaMetricsSendCount from MetaMetrics controller
|
||||
*/
|
||||
export default {
|
||||
version,
|
||||
async migrate(originalVersionedData) {
|
||||
const versionedData = cloneDeep(originalVersionedData);
|
||||
versionedData.meta.version = version;
|
||||
const state = versionedData.data;
|
||||
const newState = transformState(state);
|
||||
versionedData.data = newState;
|
||||
return versionedData;
|
||||
},
|
||||
};
|
||||
|
||||
function transformState(state) {
|
||||
if (state.MetaMetricsController) {
|
||||
const { metaMetricsSendCount } = state.MetaMetricsController;
|
||||
if (metaMetricsSendCount !== undefined) {
|
||||
delete state.MetaMetricsController.metaMetricsSendCount;
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
80
app/scripts/migrations/062.test.js
Normal file
80
app/scripts/migrations/062.test.js
Normal file
@ -0,0 +1,80 @@
|
||||
import { strict as assert } from 'assert';
|
||||
import migration62 from './062';
|
||||
|
||||
describe('migration #62', function () {
|
||||
it('should update the version metadata', async function () {
|
||||
const oldStorage = {
|
||||
meta: {
|
||||
version: 61,
|
||||
},
|
||||
data: {},
|
||||
};
|
||||
|
||||
const newStorage = await migration62.migrate(oldStorage);
|
||||
assert.deepEqual(newStorage.meta, {
|
||||
version: 62,
|
||||
});
|
||||
});
|
||||
|
||||
it('should remove metaMetricsSendCount from MetaMetricsController', async function () {
|
||||
const oldStorage = {
|
||||
meta: {},
|
||||
data: {
|
||||
MetaMetricsController: {
|
||||
metaMetricsSendCount: 1,
|
||||
bar: 'baz',
|
||||
},
|
||||
foo: 'bar',
|
||||
},
|
||||
};
|
||||
|
||||
const newStorage = await migration62.migrate(oldStorage);
|
||||
assert.deepStrictEqual(newStorage.data, {
|
||||
MetaMetricsController: {
|
||||
bar: 'baz',
|
||||
},
|
||||
foo: 'bar',
|
||||
});
|
||||
});
|
||||
|
||||
it('should remove metaMetricsSendCount from MetaMetricsController (falsey but defined)', async function () {
|
||||
const oldStorage = {
|
||||
meta: {},
|
||||
data: {
|
||||
MetaMetricsController: {
|
||||
metaMetricsSendCount: 0,
|
||||
bar: 'baz',
|
||||
},
|
||||
foo: 'bar',
|
||||
},
|
||||
};
|
||||
|
||||
const newStorage = await migration62.migrate(oldStorage);
|
||||
assert.deepStrictEqual(newStorage.data, {
|
||||
MetaMetricsController: {
|
||||
bar: 'baz',
|
||||
},
|
||||
foo: 'bar',
|
||||
});
|
||||
});
|
||||
|
||||
it('should not modify MetaMetricsController when metaMetricsSendCount is undefined', async function () {
|
||||
const oldStorage = {
|
||||
meta: {},
|
||||
data: {
|
||||
MetaMetricsController: {
|
||||
bar: 'baz',
|
||||
},
|
||||
foo: 'bar',
|
||||
},
|
||||
};
|
||||
|
||||
const newStorage = await migration62.migrate(oldStorage);
|
||||
assert.deepStrictEqual(newStorage.data, {
|
||||
MetaMetricsController: {
|
||||
bar: 'baz',
|
||||
},
|
||||
foo: 'bar',
|
||||
});
|
||||
});
|
||||
});
|
@ -66,6 +66,7 @@ const migrations = [
|
||||
require('./059').default,
|
||||
require('./060').default,
|
||||
require('./061').default,
|
||||
require('./062').default,
|
||||
];
|
||||
|
||||
export default migrations;
|
||||
|
@ -13,5 +13,8 @@ module.exports = {
|
||||
},
|
||||
setupFiles: ['./test/setup.js', './test/env.js'],
|
||||
setupFilesAfterEnv: ['./test/jest/setup.js'],
|
||||
testMatch: ['**/ui/**/?(*.)+(test).js'],
|
||||
testMatch: [
|
||||
'<rootDir>/ui/**/?(*.)+(test).js',
|
||||
'<rootDir>/shared/**/?(*.)+(test).js',
|
||||
],
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "metamask-crx",
|
||||
"version": "9.7.0",
|
||||
"version": "9.7.1",
|
||||
"private": true,
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -23,13 +23,13 @@
|
||||
"dapp-chain": "GANACHE_ARGS='-b 2' concurrently -k -n ganache,dapp -p '[{time}][{name}]' 'yarn ganache:start' 'sleep 5 && yarn dapp'",
|
||||
"forwarder": "node ./development/static-server.js ./node_modules/@metamask/forwarder/dist/ --port 9010",
|
||||
"dapp-forwarder": "concurrently -k -n forwarder,dapp -p '[{time}][{name}]' 'yarn forwarder' 'yarn dapp'",
|
||||
"test:unit": "mocha --exit --require test/env.js --require test/setup.js --recursive './{app,shared}/**/*.test.js'",
|
||||
"test:unit": "mocha --exit --require test/env.js --require test/setup.js --recursive './app/**/*.test.js'",
|
||||
"test:unit:global": "mocha --exit --require test/env.js --require test/setup.js --recursive test/unit-global/*.test.js",
|
||||
"test:unit:jest": "jest",
|
||||
"test:unit:jest:watch": "jest --watch",
|
||||
"test:unit:jest:watch:silent": "jest --watch --silent",
|
||||
"test:unit:jest:ci": "jest --maxWorkers=2",
|
||||
"test:unit:lax": "mocha --exit --require test/env.js --require test/setup.js --ignore './app/scripts/controllers/permissions/*.test.js' --recursive './{app,shared}/**/*.test.js'",
|
||||
"test:unit:lax": "mocha --exit --require test/env.js --require test/setup.js --ignore './app/scripts/controllers/permissions/*.test.js' --recursive './app/**/*.test.js'",
|
||||
"test:unit:strict": "mocha --exit --require test/env.js --require test/setup.js --recursive './app/scripts/controllers/permissions/*.test.js'",
|
||||
"test:unit:path": "mocha --exit --require test/env.js --require test/setup.js --recursive",
|
||||
"test:e2e:chrome": "SELENIUM_BROWSER=chrome test/e2e/run-all.sh",
|
||||
@ -53,7 +53,7 @@
|
||||
"verify-locales": "node ./development/verify-locale-strings.js",
|
||||
"verify-locales:fix": "node ./development/verify-locale-strings.js --fix",
|
||||
"mozilla-lint": "addons-linter dist/firefox",
|
||||
"watch": "mocha --watch --require test/env.js --require test/setup.js --reporter min --recursive \"test/unit/**/*.js\" \"ui/**/*.test.js\" \"shared/**/*.test.js\"",
|
||||
"watch": "mocha --watch --require test/env.js --require test/setup.js --reporter min --recursive \"test/unit/**/*.js\" \"ui/**/*.test.js\"",
|
||||
"devtools:react": "react-devtools",
|
||||
"devtools:redux": "remotedev --hostname=localhost --port=8000",
|
||||
"start:dev": "concurrently -k -n build,react,redux yarn:start yarn:devtools:react yarn:devtools:redux",
|
||||
|
@ -1,6 +1,40 @@
|
||||
import { isHexString } from 'ethereumjs-util';
|
||||
|
||||
export function transactionMatchesNetwork(transaction, chainId, networkId) {
|
||||
if (typeof transaction.chainId !== 'undefined') {
|
||||
return transaction.chainId === chainId;
|
||||
}
|
||||
return transaction.metamaskNetworkId === networkId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the maxFeePerGas and maxPriorityFeePerGas fields are supplied
|
||||
* and valid inputs. This will return false for non hex string inputs.
|
||||
* @param {import("../constants/transaction").TransactionMeta} transaction -
|
||||
* the transaction to check
|
||||
* @returns {boolean} true if transaction uses valid EIP1559 fields
|
||||
*/
|
||||
export function isEIP1559Transaction(transaction) {
|
||||
return (
|
||||
isHexString(transaction.txParams.maxFeePerGas) &&
|
||||
isHexString(transaction.txParams.maxPriorityFeePerGas)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the maxFeePerGas and maxPriorityFeePerGas fields are not
|
||||
* supplied and that the gasPrice field is valid if it is provided. This will
|
||||
* return false if gasPrice is a non hex string.
|
||||
* @param {import("../constants/transaction").TransactionMeta} transaction -
|
||||
* the transaction to check
|
||||
* @returns {boolean} true if transaction uses valid Legacy fields OR lacks
|
||||
* EIP1559 fields
|
||||
*/
|
||||
export function isLegacyTransaction(transaction) {
|
||||
return (
|
||||
typeof transaction.txParams.maxFeePerGas === 'undefined' &&
|
||||
typeof transaction.txParams.maxPriorityFeePerGas === 'undefined' &&
|
||||
(typeof transaction.txParams.gasPrice === 'undefined' ||
|
||||
isHexString(transaction.txParams.gasPrice))
|
||||
);
|
||||
}
|
||||
|
83
shared/modules/transaction.utils.test.js
Normal file
83
shared/modules/transaction.utils.test.js
Normal file
@ -0,0 +1,83 @@
|
||||
import { isEIP1559Transaction, isLegacyTransaction } from './transaction.utils';
|
||||
|
||||
describe('Transaction.utils', function () {
|
||||
describe('isEIP1559Transaction', function () {
|
||||
it('should return true if both maxFeePerGas and maxPriorityFeePerGas are hex strings', () => {
|
||||
expect(
|
||||
isEIP1559Transaction({
|
||||
txParams: { maxFeePerGas: '0x1', maxPriorityFeePerGas: '0x1' },
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if either maxFeePerGas and maxPriorityFeePerGas are non-hex strings', () => {
|
||||
expect(
|
||||
isEIP1559Transaction({
|
||||
txParams: { maxFeePerGas: 0, maxPriorityFeePerGas: '0x1' },
|
||||
}),
|
||||
).toBe(false);
|
||||
expect(
|
||||
isEIP1559Transaction({
|
||||
txParams: { maxFeePerGas: '0x1', maxPriorityFeePerGas: 'fail' },
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false if either maxFeePerGas or maxPriorityFeePerGas are not supplied', () => {
|
||||
expect(
|
||||
isEIP1559Transaction({
|
||||
txParams: { maxPriorityFeePerGas: '0x1' },
|
||||
}),
|
||||
).toBe(false);
|
||||
expect(
|
||||
isEIP1559Transaction({
|
||||
txParams: { maxFeePerGas: '0x1' },
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isLegacyTransaction', function () {
|
||||
it('should return true if no gas related fields are supplied', () => {
|
||||
expect(
|
||||
isLegacyTransaction({
|
||||
txParams: {},
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true if gasPrice is solely provided', () => {
|
||||
expect(
|
||||
isLegacyTransaction({
|
||||
txParams: { gasPrice: '0x1' },
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if gasPrice is not a hex string', () => {
|
||||
expect(
|
||||
isLegacyTransaction({
|
||||
txParams: { gasPrice: 100 },
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false if either maxFeePerGas or maxPriorityFeePerGas are supplied', () => {
|
||||
expect(
|
||||
isLegacyTransaction({
|
||||
txParams: {
|
||||
maxFeePerGas: '0x1',
|
||||
},
|
||||
}),
|
||||
).toBe(false);
|
||||
|
||||
expect(
|
||||
isLegacyTransaction({
|
||||
txParams: {
|
||||
maxPriorityFeePerGas: 'any data',
|
||||
},
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
@ -128,7 +128,6 @@
|
||||
"knownMethodData": {},
|
||||
"lostIdentities": {},
|
||||
"metaMetricsId": null,
|
||||
"metaMetricsSendCount": 0,
|
||||
"participateInMetaMetrics": false,
|
||||
"preferences": {
|
||||
"useNativeCurrencyAsPrimaryCurrency": true
|
||||
|
@ -138,7 +138,6 @@
|
||||
},
|
||||
"completedOnboarding": true,
|
||||
"metaMetricsId": null,
|
||||
"metaMetricsSendCount": 0,
|
||||
"ipfsGateway": "dweb.link",
|
||||
"selectedAddress": "0x5cfe73b6021e818b776b421b1c4db2474086a7e1"
|
||||
},
|
||||
|
@ -129,7 +129,6 @@
|
||||
"knownMethodData": {},
|
||||
"lostIdentities": {},
|
||||
"metaMetricsId": null,
|
||||
"metaMetricsSendCount": 0,
|
||||
"participateInMetaMetrics": false,
|
||||
"preferences": {
|
||||
"useNativeCurrencyAsPrimaryCurrency": true
|
||||
|
@ -121,7 +121,6 @@
|
||||
"knownMethodData": {},
|
||||
"lostIdentities": {},
|
||||
"metaMetricsId": null,
|
||||
"metaMetricsSendCount": 0,
|
||||
"participateInMetaMetrics": false,
|
||||
"preferences": {
|
||||
"useNativeCurrencyAsPrimaryCurrency": true
|
||||
|
@ -125,8 +125,7 @@
|
||||
},
|
||||
"MetaMetricsController": {
|
||||
"participateInMetaMetrics": false,
|
||||
"metaMetricsId": null,
|
||||
"metaMetricsSendCount": 1
|
||||
"metaMetricsId": null
|
||||
},
|
||||
"PermissionsController": {
|
||||
"permissionsRequests": [],
|
||||
|
@ -114,7 +114,6 @@
|
||||
"knownMethodData": {},
|
||||
"lostIdentities": {},
|
||||
"metaMetricsId": null,
|
||||
"metaMetricsSendCount": 0,
|
||||
"participateInMetaMetrics": false,
|
||||
"preferences": {
|
||||
"useNativeCurrencyAsPrimaryCurrency": true
|
||||
|
@ -114,7 +114,6 @@
|
||||
"knownMethodData": {},
|
||||
"lostIdentities": {},
|
||||
"metaMetricsId": null,
|
||||
"metaMetricsSendCount": 0,
|
||||
"participateInMetaMetrics": false,
|
||||
"preferences": {
|
||||
"showFiatInTestnets": true,
|
||||
|
@ -138,7 +138,6 @@
|
||||
},
|
||||
"completedOnboarding": true,
|
||||
"metaMetricsId": "fake-metrics-id",
|
||||
"metaMetricsSendCount": 0,
|
||||
"ipfsGateway": "dweb.link",
|
||||
"selectedAddress": "0x5cfe73b6021e818b776b421b1c4db2474086a7e1"
|
||||
},
|
||||
|
@ -115,7 +115,6 @@
|
||||
"knownMethodData": {},
|
||||
"lostIdentities": {},
|
||||
"metaMetricsId": null,
|
||||
"metaMetricsSendCount": 0,
|
||||
"participateInMetaMetrics": false,
|
||||
"preferences": {
|
||||
"useNativeCurrencyAsPrimaryCurrency": true
|
||||
|
@ -120,8 +120,7 @@
|
||||
},
|
||||
"MetaMetricsController": {
|
||||
"metaMetricsId": null,
|
||||
"participateInMetaMetrics": false,
|
||||
"metaMetricsSendCount": 0
|
||||
"participateInMetaMetrics": false
|
||||
},
|
||||
"ThreeBoxController": {
|
||||
"threeBoxSyncingAllowed": true,
|
||||
|
@ -41,6 +41,7 @@ import {
|
||||
getAveragePriceEstimateInHexWEI,
|
||||
isCustomPriceExcessive,
|
||||
getIsGasEstimatesFetched,
|
||||
getIsCustomNetworkGasPriceFetched,
|
||||
} from '../../../../selectors';
|
||||
|
||||
import {
|
||||
@ -141,13 +142,17 @@ const mapStateToProps = (state, ownProps) => {
|
||||
conversionRate,
|
||||
});
|
||||
const isGasEstimate = getIsGasEstimatesFetched(state);
|
||||
const customNetworkEstimateWasFetched = getIsCustomNetworkGasPriceFetched(
|
||||
state,
|
||||
);
|
||||
|
||||
let customPriceIsSafe;
|
||||
let customPriceIsSafe = true;
|
||||
if ((isMainnet || process.env.IN_TEST) && isGasEstimate) {
|
||||
customPriceIsSafe = isCustomPriceSafe(state);
|
||||
} else if (isTestnet) {
|
||||
customPriceIsSafe = true;
|
||||
} else {
|
||||
} else if (
|
||||
!(isMainnet || process.env.IN_TEST || isTestnet) &&
|
||||
customNetworkEstimateWasFetched
|
||||
) {
|
||||
customPriceIsSafe = isCustomPriceSafeForCustomNetwork(state);
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,6 @@ export default function reduceMetamask(state = {}, action) {
|
||||
completedOnboarding: false,
|
||||
knownMethodData: {},
|
||||
participateInMetaMetrics: null,
|
||||
metaMetricsSendCount: 0,
|
||||
nextNonce: null,
|
||||
conversionRate: null,
|
||||
nativeCurrency: 'ETH',
|
||||
@ -126,12 +125,6 @@ export default function reduceMetamask(state = {}, action) {
|
||||
participateInMetaMetrics: action.value,
|
||||
};
|
||||
|
||||
case actionConstants.SET_METAMETRICS_SEND_COUNT:
|
||||
return {
|
||||
...metamaskState,
|
||||
metaMetricsSendCount: action.value,
|
||||
};
|
||||
|
||||
case actionConstants.SET_USE_BLOCKIE:
|
||||
return {
|
||||
...metamaskState,
|
||||
|
@ -86,8 +86,6 @@ export default class ConfirmTransactionBase extends Component {
|
||||
hideSubtitle: PropTypes.bool,
|
||||
identiconAddress: PropTypes.string,
|
||||
onEdit: PropTypes.func,
|
||||
setMetaMetricsSendCount: PropTypes.func,
|
||||
metaMetricsSendCount: PropTypes.number,
|
||||
subtitleComponent: PropTypes.node,
|
||||
title: PropTypes.string,
|
||||
advancedInlineGasShown: PropTypes.bool,
|
||||
@ -475,35 +473,16 @@ export default class ConfirmTransactionBase extends Component {
|
||||
}
|
||||
|
||||
handleCancel() {
|
||||
const { metricsEvent } = this.context;
|
||||
const {
|
||||
txData,
|
||||
cancelTransaction,
|
||||
history,
|
||||
mostRecentOverviewPage,
|
||||
clearConfirmTransaction,
|
||||
actionKey,
|
||||
txData: { origin },
|
||||
methodData = {},
|
||||
updateCustomNonce,
|
||||
} = this.props;
|
||||
|
||||
this._removeBeforeUnload();
|
||||
metricsEvent({
|
||||
eventOpts: {
|
||||
category: 'Transactions',
|
||||
action: 'Confirm Screen',
|
||||
name: 'Cancel',
|
||||
},
|
||||
customVariables: {
|
||||
recipientKnown: null,
|
||||
functionType:
|
||||
actionKey ||
|
||||
getMethodName(methodData.name) ||
|
||||
TRANSACTION_TYPES.CONTRACT_INTERACTION,
|
||||
origin,
|
||||
},
|
||||
});
|
||||
updateCustomNonce('');
|
||||
cancelTransaction(txData).then(() => {
|
||||
clearConfirmTransaction();
|
||||
@ -512,18 +491,12 @@ export default class ConfirmTransactionBase extends Component {
|
||||
}
|
||||
|
||||
handleSubmit() {
|
||||
const { metricsEvent } = this.context;
|
||||
const {
|
||||
txData: { origin },
|
||||
sendTransaction,
|
||||
clearConfirmTransaction,
|
||||
txData,
|
||||
history,
|
||||
actionKey,
|
||||
mostRecentOverviewPage,
|
||||
metaMetricsSendCount = 0,
|
||||
setMetaMetricsSendCount,
|
||||
methodData = {},
|
||||
updateCustomNonce,
|
||||
} = this.props;
|
||||
const { submitting } = this.state;
|
||||
@ -539,44 +512,27 @@ export default class ConfirmTransactionBase extends Component {
|
||||
},
|
||||
() => {
|
||||
this._removeBeforeUnload();
|
||||
metricsEvent({
|
||||
eventOpts: {
|
||||
category: 'Transactions',
|
||||
action: 'Confirm Screen',
|
||||
name: 'Transaction Completed',
|
||||
},
|
||||
customVariables: {
|
||||
recipientKnown: null,
|
||||
functionType:
|
||||
actionKey ||
|
||||
getMethodName(methodData.name) ||
|
||||
TRANSACTION_TYPES.CONTRACT_INTERACTION,
|
||||
origin,
|
||||
},
|
||||
});
|
||||
|
||||
setMetaMetricsSendCount(metaMetricsSendCount + 1).then(() => {
|
||||
sendTransaction(txData)
|
||||
.then(() => {
|
||||
clearConfirmTransaction();
|
||||
this.setState(
|
||||
{
|
||||
submitting: false,
|
||||
},
|
||||
() => {
|
||||
history.push(mostRecentOverviewPage);
|
||||
updateCustomNonce('');
|
||||
},
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.setState({
|
||||
sendTransaction(txData)
|
||||
.then(() => {
|
||||
clearConfirmTransaction();
|
||||
this.setState(
|
||||
{
|
||||
submitting: false,
|
||||
submitError: error.message,
|
||||
});
|
||||
updateCustomNonce('');
|
||||
},
|
||||
() => {
|
||||
history.push(mostRecentOverviewPage);
|
||||
updateCustomNonce('');
|
||||
},
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.setState({
|
||||
submitting: false,
|
||||
submitError: error.message,
|
||||
});
|
||||
});
|
||||
updateCustomNonce('');
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -643,18 +599,7 @@ export default class ConfirmTransactionBase extends Component {
|
||||
}
|
||||
|
||||
_beforeUnload = () => {
|
||||
const { txData: { origin, id } = {}, cancelTransaction } = this.props;
|
||||
const { metricsEvent } = this.context;
|
||||
metricsEvent({
|
||||
eventOpts: {
|
||||
category: 'Transactions',
|
||||
action: 'Confirm Screen',
|
||||
name: 'Cancel Tx Via Notification Close',
|
||||
},
|
||||
customVariables: {
|
||||
origin,
|
||||
},
|
||||
});
|
||||
const { txData: { id } = {}, cancelTransaction } = this.props;
|
||||
cancelTransaction({ id });
|
||||
};
|
||||
|
||||
|
@ -10,7 +10,6 @@ import {
|
||||
cancelTxs,
|
||||
updateAndApproveTx,
|
||||
showModal,
|
||||
setMetaMetricsSendCount,
|
||||
updateTransaction,
|
||||
getNextNonce,
|
||||
tryReverseResolveAddress,
|
||||
@ -76,7 +75,6 @@ const mapStateToProps = (state, ownProps) => {
|
||||
assetImages,
|
||||
network,
|
||||
unapprovedTxs,
|
||||
metaMetricsSendCount,
|
||||
nextNonce,
|
||||
provider: { chainId },
|
||||
} = metamask;
|
||||
@ -186,7 +184,6 @@ const mapStateToProps = (state, ownProps) => {
|
||||
insufficientBalance,
|
||||
hideSubtitle: !isMainnet && !showFiatInTestnets,
|
||||
hideFiatConversion: !isMainnet && !showFiatInTestnets,
|
||||
metaMetricsSendCount,
|
||||
type,
|
||||
nextNonce,
|
||||
mostRecentOverviewPage: getMostRecentOverviewPage(state),
|
||||
@ -234,7 +231,6 @@ export const mapDispatchToProps = (dispatch) => {
|
||||
cancelAllTransactions: (txList) => dispatch(cancelTxs(txList)),
|
||||
sendTransaction: (txData) =>
|
||||
dispatch(updateAndApproveTx(customNonceMerge(txData))),
|
||||
setMetaMetricsSendCount: (val) => dispatch(setMetaMetricsSendCount(val)),
|
||||
getNextNonce: () => dispatch(getNextNonce()),
|
||||
setDefaultHomeActiveTabName: (tabName) =>
|
||||
dispatch(setDefaultHomeActiveTabName(tabName)),
|
||||
|
@ -32,11 +32,11 @@ export default class SendGasRow extends Component {
|
||||
|
||||
static contextTypes = {
|
||||
t: PropTypes.func,
|
||||
metricsEvent: PropTypes.func,
|
||||
trackEvent: PropTypes.func,
|
||||
};
|
||||
|
||||
renderAdvancedOptionsButton() {
|
||||
const { metricsEvent } = this.context;
|
||||
const { trackEvent } = this.context;
|
||||
const {
|
||||
showCustomizeGasModal,
|
||||
isMainnet,
|
||||
@ -54,12 +54,9 @@ export default class SendGasRow extends Component {
|
||||
<div
|
||||
className="advanced-gas-options-btn"
|
||||
onClick={() => {
|
||||
metricsEvent({
|
||||
eventOpts: {
|
||||
category: 'Transactions',
|
||||
action: 'Edit Screen',
|
||||
name: 'Clicked "Advanced Options"',
|
||||
},
|
||||
trackEvent({
|
||||
category: 'Transactions',
|
||||
event: 'Clicked "Advanced Options"',
|
||||
});
|
||||
showCustomizeGasModal();
|
||||
}}
|
||||
@ -105,7 +102,7 @@ export default class SendGasRow extends Component {
|
||||
isEthGasPrice,
|
||||
noGasPrice,
|
||||
} = this.props;
|
||||
const { metricsEvent } = this.context;
|
||||
const { trackEvent } = this.context;
|
||||
const gasPriceFetchFailure = isEthGasPrice || noGasPrice;
|
||||
|
||||
const gasPriceButtonGroup = (
|
||||
@ -115,11 +112,11 @@ export default class SendGasRow extends Component {
|
||||
showCheck={false}
|
||||
{...gasPriceButtonGroupProps}
|
||||
handleGasPriceSelection={async (opts) => {
|
||||
metricsEvent({
|
||||
eventOpts: {
|
||||
category: 'Transactions',
|
||||
action: 'Edit Screen',
|
||||
name: 'Changed Gas Button',
|
||||
trackEvent({
|
||||
category: 'Transactions',
|
||||
event: 'User Clicked Gas Estimate Button',
|
||||
properties: {
|
||||
gasEstimateType: opts.gasEstimateType.toLowerCase(),
|
||||
},
|
||||
});
|
||||
await gasPriceButtonGroupProps.handleGasPriceSelection(opts);
|
||||
|
@ -32,7 +32,7 @@ describe('SendGasRow Component', () => {
|
||||
anotherGasPriceButtonGroupProp: 'bar',
|
||||
}}
|
||||
/>,
|
||||
{ context: { t: (str) => `${str}_t`, metricsEvent: () => ({}) } },
|
||||
{ context: { t: (str) => `${str}_t`, trackEvent: () => ({}) } },
|
||||
);
|
||||
wrapper.setProps({ isMainnet: true });
|
||||
});
|
||||
|
@ -109,6 +109,10 @@ export function isCustomPriceSafeForCustomNetwork(state) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!estimatedPrice) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const customPriceSafe = conversionGreaterThan(
|
||||
{
|
||||
value: customGasPrice,
|
||||
@ -391,6 +395,15 @@ export function getIsEthGasPriceFetched(state) {
|
||||
);
|
||||
}
|
||||
|
||||
export function getIsCustomNetworkGasPriceFetched(state) {
|
||||
const gasState = state.gas;
|
||||
return Boolean(
|
||||
gasState.estimateSource === GAS_SOURCE.ETHGASPRICE &&
|
||||
gasState.basicEstimateStatus === BASIC_ESTIMATE_STATES.READY &&
|
||||
!getIsMainnet(state),
|
||||
);
|
||||
}
|
||||
|
||||
export function getNoGasPriceFetched(state) {
|
||||
const gasState = state.gas;
|
||||
return Boolean(gasState.basicEstimateStatus === BASIC_ESTIMATE_STATES.FAILED);
|
||||
|
@ -60,7 +60,6 @@ export const UPDATE_CUSTOM_NONCE = 'UPDATE_CUSTOM_NONCE';
|
||||
export const SET_IPFS_GATEWAY = 'SET_IPFS_GATEWAY';
|
||||
|
||||
export const SET_PARTICIPATE_IN_METAMETRICS = 'SET_PARTICIPATE_IN_METAMETRICS';
|
||||
export const SET_METAMETRICS_SEND_COUNT = 'SET_METAMETRICS_SEND_COUNT';
|
||||
|
||||
// locale
|
||||
export const SET_CURRENT_LOCALE = 'SET_CURRENT_LOCALE';
|
||||
|
@ -1968,27 +1968,6 @@ export function setParticipateInMetaMetrics(val) {
|
||||
};
|
||||
}
|
||||
|
||||
export function setMetaMetricsSendCount(val) {
|
||||
return (dispatch) => {
|
||||
log.debug(`background.setMetaMetricsSendCount`);
|
||||
return new Promise((resolve, reject) => {
|
||||
background.setMetaMetricsSendCount(val, (err) => {
|
||||
if (err) {
|
||||
dispatch(displayWarning(err.message));
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: actionConstants.SET_METAMETRICS_SEND_COUNT,
|
||||
value: val,
|
||||
});
|
||||
resolve(val);
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function setUseBlockie(val) {
|
||||
return (dispatch) => {
|
||||
dispatch(showLoadingIndication());
|
||||
|
Loading…
Reference in New Issue
Block a user