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

Merge remote-tracking branch 'origin/develop' into master-sync

This commit is contained in:
ryanml 2022-04-28 12:04:17 -07:00
commit f19173b0f2
185 changed files with 25646 additions and 5332 deletions

View File

@ -6,13 +6,13 @@ set -x
set -o pipefail
# use `improved-yarn-audit` since that allows for exclude
# exclude 1002401 until we remove use of 3Box, 1002581 until we can find a better solution
yarn run improved-yarn-audit --ignore-dev-deps --min-severity moderate --exclude GHSA-93q8-gq69-wqmw,GHSA-257v-vj4p-3w2h,GHSA-fwr7-v2mv-hh25
audit_status="$?"
# exclusions are in .iyarc now
yarn run improved-yarn-audit \
--ignore-dev-deps \
--min-severity moderate \
--fail-on-missing-exclusions
# Use a bitmask to ignore INFO and LOW severity audit results
# See here: https://yarnpkg.com/lang/en/docs/cli/audit/
audit_status="$(( audit_status & 11100 ))"
audit_status="$?"
if [[ "$audit_status" != 0 ]]
then

View File

@ -44,14 +44,6 @@ module.exports = {
path.resolve(__dirname, '.eslintrc.babel.js'),
path.resolve(__dirname, '.eslintrc.typescript-compat.js'),
],
parserOptions: {
sourceType: 'module',
},
rules: {
// This rule does not work with CommonJS modules. We will just have to
// trust that all of the files specified above are indeed modules.
'import/unambiguous': 'off',
},
settings: {
'import/resolver': {
// When determining the location of a `require()` call, use Node's

View File

@ -21,7 +21,7 @@ jobs:
uses: actions/checkout@v2
- name: crowdin action
uses: crowdin/github-action@9237b4cb361788dfce63feb2e2f15c09e2fe7415
uses: crowdin/github-action@a3160b9e5a9e00739392c23da5e580c6cabe526d
with:
upload_translations: true
download_translations: true

4
.iyarc Normal file
View File

@ -0,0 +1,4 @@
# improved-yarn-audit advisory exclusions
GHSA-93q8-gq69-wqmw
GHSA-257v-vj4p-3w2h
GHSA-fwr7-v2mv-hh25

View File

@ -1248,20 +1248,7 @@ const state = {
method: 'eth_accounts',
methodType: 'restricted',
origin: 'https://metamask.io',
request: {
method: 'eth_accounts',
params: [],
jsonrpc: '2.0',
id: 522690215,
origin: 'https://metamask.io',
tabId: 5,
},
requestTime: 1602643170686,
response: {
id: 522690215,
jsonrpc: '2.0',
result: [],
},
responseTime: 1602643170688,
success: true,
},
@ -1270,20 +1257,7 @@ const state = {
method: 'eth_accounts',
methodType: 'restricted',
origin: 'https://widget.getacute.io',
request: {
method: 'eth_accounts',
params: [],
jsonrpc: '2.0',
id: 1620464600,
origin: 'https://widget.getacute.io',
tabId: 5,
},
requestTime: 1602643172935,
response: {
id: 1620464600,
jsonrpc: '2.0',
result: [],
},
responseTime: 1602643172935,
success: true,
},
@ -1292,19 +1266,7 @@ const state = {
method: 'eth_accounts',
methodType: 'restricted',
origin: 'https://app.uniswap.org',
request: {
method: 'eth_accounts',
jsonrpc: '2.0',
id: 4279100021,
origin: 'https://app.uniswap.org',
tabId: 5,
},
requestTime: 1620710669962,
response: {
id: 4279100021,
jsonrpc: '2.0',
result: [],
},
responseTime: 1620710669963,
success: true,
},
@ -1313,19 +1275,7 @@ const state = {
method: 'eth_requestAccounts',
methodType: 'restricted',
origin: 'https://app.uniswap.org',
request: {
method: 'eth_requestAccounts',
jsonrpc: '2.0',
id: 4279100022,
origin: 'https://app.uniswap.org',
tabId: 5,
},
requestTime: 1620710686872,
response: {
id: 4279100022,
jsonrpc: '2.0',
result: ['0x64a845a5b02460acf8a3d84503b0d68d028b4bb4'],
},
responseTime: 1620710693187,
success: true,
},
@ -1334,19 +1284,7 @@ const state = {
method: 'eth_requestAccounts',
methodType: 'restricted',
origin: 'https://app.uniswap.org',
request: {
method: 'eth_requestAccounts',
jsonrpc: '2.0',
id: 4279100023,
origin: 'https://app.uniswap.org',
tabId: 5,
},
requestTime: 1620710693204,
response: {
id: 4279100023,
jsonrpc: '2.0',
result: ['0x64a845a5b02460acf8a3d84503b0d68d028b4bb4'],
},
responseTime: 1620710693213,
success: true,
},
@ -1355,20 +1293,7 @@ const state = {
method: 'eth_accounts',
methodType: 'restricted',
origin: 'https://app.uniswap.org',
request: {
method: 'eth_accounts',
params: [],
jsonrpc: '2.0',
id: 4279100034,
origin: 'https://app.uniswap.org',
tabId: 5,
},
requestTime: 1620710712072,
response: {
id: 4279100034,
jsonrpc: '2.0',
result: ['0x64a845a5b02460acf8a3d84503b0d68d028b4bb4'],
},
responseTime: 1620710712075,
success: true,
},

View File

@ -60,9 +60,23 @@ You can run the linter by itself with `yarn lint`, and you can automatically fix
Our e2e test suite can be run on either Firefox or Chrome. In either case, start by creating a test build by running `yarn build:test`.
Firefox e2e tests can be run with `yarn test:e2e:firefox`.
- Firefox e2e tests can be run with `yarn test:e2e:firefox`.
Chrome e2e tests can be run with `yarn test:e2e:chrome`, but they will only work if you have Chrome v79 installed. Update the `chromedriver` package to a version matching your local Chrome installation to run e2e tests on newer Chrome versions.
- Chrome e2e tests can be run with `yarn test:e2e:chrome`. The `chromedriver` package major version must match the major version of your local Chrome installation. If they don't match, update whichever is behind before running Chrome e2e tests.
- Single e2e tests can be run with `yarn test:e2e:single test/e2e/tests/TEST_NAME.spec.js` along with the options below.
```console
--browser Set the browser used; either 'chrome' or 'firefox'.
--leave-running Leaves the browser running after a test fails, along with anything else
that the test used (ganache, the test dapp, etc.).
--retries Set how many times the test should be retried upon failure. Default is 0.
```
An example for running `account-details` testcase with chrome and leaving the browser open would be:
`yarn test:e2e:single test/e2e/tests/account-details.spec.js --browser=chrome --leave-running`
### Changing dependencies

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -160,6 +160,10 @@
"addNetwork": {
"message": "Add Network"
},
"addNetworkTooltipWarning": {
"message": "This network connection relies on third parties. This connection may be less reliable or enable third-parties to track activity. $1",
"description": "$1 is Learn more link"
},
"addSuggestedTokens": {
"message": "Add Suggested Tokens"
},
@ -734,9 +738,6 @@
"customGasSubTitle": {
"message": "Increasing fee may decrease processing times, but it is not guaranteed."
},
"customNetworks": {
"message": "Custom networks"
},
"customSpendLimit": {
"message": "Custom Spend Limit"
},
@ -804,9 +805,6 @@
"decryptRequest": {
"message": "Decrypt request"
},
"defaultTheme": {
"message": "Default"
},
"delete": {
"message": "Delete"
},
@ -1251,6 +1249,10 @@
"message": "All Flask APIs are experimental. They may be changed or removed without notice, or they might stay on Flask indefinitely without ever being migrated to stable MetaMask. Use them at your own risk.",
"description": "This message warns developers about unstable Flask APIs"
},
"flaskWelcomeWarning4": {
"message": "Make sure to disable your regular MetaMask extension when using Flask.",
"description": "This message calls to pay attention about multiple versions of MetaMask running on the same site (Flask + Prod)"
},
"flaskWelcomeWarningAcceptButton": {
"message": "I accept the risks",
"description": "this text is shown on a button, which the user presses to confirm they understand the risks of using Flask"
@ -1442,6 +1444,9 @@
"hide": {
"message": "Hide"
},
"hideFullTransactionDetails": {
"message": "Hide full transaction details"
},
"hideSeedPhrase": {
"message": "Hide seed phrase"
},
@ -1714,6 +1719,9 @@
"levelArrow": {
"message": "level arrow"
},
"lightTheme": {
"message": "Light"
},
"likeToImportTokens": {
"message": "Would you like to import these tokens?"
},
@ -2299,9 +2307,6 @@
"onlyConnectTrust": {
"message": "Only connect with sites you trust."
},
"onlyInteractWith": {
"message": "Only interact with entities you trust."
},
"openFullScreenForLedgerWebHid": {
"message": "Open MetaMask in full screen to connect your ledger via WebHID.",
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
@ -2321,6 +2326,9 @@
"origin": {
"message": "Origin"
},
"osTheme": {
"message": "System"
},
"padlock": {
"message": "Padlock"
},
@ -2423,6 +2431,9 @@
"message": "+ $1 more",
"description": "$1 is a number of additional but unshown items in a list- this message will be shown in place of those items"
},
"popularCustomNetworks": {
"message": "Popular custom networks"
},
"preferredLedgerConnectionType": {
"message": "Preferred Ledger Connection Type",
"description": "A header for a dropdown in the advanced section of settings. Appears above the ledgerConnectionPreferenceDescription message"
@ -3926,6 +3937,9 @@
"walletCreationSuccessTitle": {
"message": "Wallet creation successful"
},
"warning": {
"message": "Warning"
},
"weak": {
"message": "Weak"
},

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

3993
app/_locales/zh/messages.json generated Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

1
app/images/palm.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 314 KiB

View File

@ -1,4 +1,13 @@
import { isEqual, merge, omit, omitBy, pickBy, size, sum } from 'lodash';
import {
isEqual,
memoize,
merge,
omit,
omitBy,
pickBy,
size,
sum,
} from 'lodash';
import { ObservableStore } from '@metamask/obs-store';
import { bufferToHex, keccak } from 'ethereumjs-util';
import { generateUUID } from 'pubnub';
@ -540,9 +549,7 @@ export default class MetaMetricsController {
* @returns {MetaMetricsTraits | null} traits that have changed since last update
*/
_buildUserTraitsObject(metamaskState) {
/**
* @type {MetaMetricsTraits}
*/
/** @type {MetaMetricsTraits} */
const currentTraits = {
[TRAITS.ADDRESS_BOOK_ENTRIES]: sum(
Object.values(metamaskState.addressBook).map(size),
@ -554,9 +561,12 @@ export default class MetaMetricsController {
[TRAITS.NFT_AUTODETECTION_ENABLED]: metamaskState.useCollectibleDetection,
[TRAITS.NUMBER_OF_ACCOUNTS]: Object.values(metamaskState.identities)
.length,
[TRAITS.NUMBER_OF_NFT_COLLECTIONS]: this._getNumberOfNFtCollection(
metamaskState,
[TRAITS.NUMBER_OF_NFT_COLLECTIONS]: this._getAllUniqueNFTAddressesLength(
metamaskState.allCollectibles,
),
[TRAITS.NUMBER_OF_NFTS]: this._getAllNFTsFlattened(
metamaskState.allCollectibles,
).length,
[TRAITS.NUMBER_OF_TOKENS]: this._getNumberOfTokens(metamaskState),
[TRAITS.OPENSEA_API_ENABLED]: metamaskState.openSeaEnabled,
[TRAITS.THREE_BOX_ENABLED]: metamaskState.threeBoxSyncingAllowed,
@ -603,22 +613,31 @@ export default class MetaMetricsController {
}
/**
* Returns an array of all of the collectibles/NFTs the user
* possesses across all networks and accounts.
*
* @param {object} metamaskState
* @returns number of unique collectible addresses
* @param {Object} allCollectibles
* @returns {[]}
*/
_getNumberOfNFtCollection(metamaskState) {
const { allCollectibles } = metamaskState;
if (!allCollectibles) {
return 0;
}
_getAllNFTsFlattened = memoize((allCollectibles = {}) => {
return Object.values(allCollectibles)
.flatMap((chainNFTs) => Object.values(chainNFTs))
.flat();
});
const allAddresses = Object.values(allCollectibles)
.flatMap((chainCollectibles) => Object.values(chainCollectibles))
.flat()
.map((collectible) => collectible.address);
const unique = [...new Set(allAddresses)];
return unique.length;
/**
* Returns the number of unique collectible/NFT addresses the user
* possesses across all networks and accounts.
*
* @param {Object} allCollectibles
* @returns {number}
*/
_getAllUniqueNFTAddressesLength(allCollectibles = {}) {
const allNFTAddresses = this._getAllNFTsFlattened(allCollectibles).map(
(nft) => nft.address,
);
const uniqueAddresses = new Set(allNFTAddresses);
return uniqueAddresses.size;
}
/**

View File

@ -691,6 +691,7 @@ describe('MetaMetricsController', function () {
[TRAITS.NFT_AUTODETECTION_ENABLED]: false,
[TRAITS.NUMBER_OF_ACCOUNTS]: 2,
[TRAITS.NUMBER_OF_NFT_COLLECTIONS]: 3,
[TRAITS.NUMBER_OF_NFTS]: 4,
[TRAITS.NUMBER_OF_TOKENS]: 5,
[TRAITS.OPENSEA_API_ENABLED]: true,
[TRAITS.THREE_BOX_ENABLED]: false,

View File

@ -18,6 +18,7 @@ import {
MAINNET_CHAIN_ID,
RINKEBY_CHAIN_ID,
INFURA_BLOCKED_KEY,
TEST_NETWORK_TICKER_MAP,
} from '../../../../shared/constants/network';
import { SECOND } from '../../../../shared/constants/time';
import {
@ -41,7 +42,11 @@ if (process.env.IN_TEST) {
nickname: 'Localhost 8545',
};
} else if (process.env.METAMASK_DEBUG || env === 'test') {
defaultProviderConfigOpts = { type: RINKEBY, chainId: RINKEBY_CHAIN_ID };
defaultProviderConfigOpts = {
type: RINKEBY,
chainId: RINKEBY_CHAIN_ID,
ticker: TEST_NETWORK_TICKER_MAP.rinkeby,
};
} else {
defaultProviderConfigOpts = { type: MAINNET, chainId: MAINNET_CHAIN_ID };
}
@ -296,12 +301,12 @@ export default class NetworkController extends EventEmitter {
INFURA_PROVIDER_TYPES.includes(type),
`Unknown Infura provider type "${type}".`,
);
const { chainId } = NETWORK_TYPE_TO_ID_MAP[type];
const { chainId, ticker } = NETWORK_TYPE_TO_ID_MAP[type];
this.setProviderConfig({
type,
rpcUrl: '',
chainId,
ticker: 'ETH',
ticker: ticker ?? 'ETH',
nickname: '',
});
}

View File

@ -1,5 +1,4 @@
import { ObservableStore } from '@metamask/obs-store';
import stringify from 'fast-safe-stringify';
import { CaveatTypes } from '../../../../shared/constants/permissions';
import {
LOG_IGNORE_METHODS,
@ -158,9 +157,7 @@ export class PermissionLogController {
? LOG_METHOD_TYPES.internal
: LOG_METHOD_TYPES.restricted,
origin: request.origin,
request: stringify(request, null, 2),
requestTime: Date.now(),
response: null,
responseTime: null,
success: null,
};
@ -181,9 +178,12 @@ export class PermissionLogController {
return;
}
entry.response = stringify(response, null, 2);
// The JSON-RPC 2.0 specification defines "success" by the presence of
// either the "result" or "error" property. The specification forbids
// both properties from being present simultaneously, and our JSON-RPC
// stack is spec-compliant at the time of writing.
entry.success = Object.hasOwnProperty.call(response, 'result');
entry.responseTime = time;
entry.success = !response.error;
}
/**

View File

@ -1,6 +1,5 @@
import nanoid from 'nanoid';
import { useFakeTimers } from 'sinon';
import stringify from 'fast-safe-stringify';
import { constants, getters, noop } from '../../../../test/mocks/permissions';
import { PermissionLogController } from './permission-log';
import { LOG_LIMIT, LOG_METHOD_TYPES } from './enums';
@ -67,7 +66,7 @@ describe('PermissionLogController', () => {
req = RPC_REQUESTS.test_method(SUBJECTS.a.origin);
req.id = REQUEST_IDS.a;
res = { foo: 'bar' };
res = { result: 'bar' };
logMiddleware({ ...req }, res);
@ -143,11 +142,17 @@ describe('PermissionLogController', () => {
false,
);
// validate final state
// Validate final state
expect(entry1).toStrictEqual(log[0]);
expect(entry2).toStrictEqual(log[1]);
expect(entry3).toStrictEqual(log[2]);
expect(entry4).toStrictEqual(log[3]);
// Regression test: ensure "response" and "request" properties
// are not present
log.forEach((entry) =>
expect('request' in entry && 'response' in entry).toBe(false),
);
});
it('handles responses added out of order', () => {
@ -163,15 +168,15 @@ describe('PermissionLogController', () => {
// get make requests
req.id = id1;
const res1 = { foo: id1 };
const res1 = { result: id1 };
logMiddleware({ ...req }, { ...res1 }, getSavedMockNext(handlerArray));
req.id = id2;
const res2 = { foo: id2 };
const res2 = { result: id2 };
logMiddleware({ ...req }, { ...res2 }, getSavedMockNext(handlerArray));
req.id = id3;
const res3 = { foo: id3 };
const res3 = { result: id3 };
logMiddleware({ ...req }, { ...res3 }, getSavedMockNext(handlerArray));
// verify log state
@ -181,10 +186,10 @@ describe('PermissionLogController', () => {
const entry2 = log[1];
const entry3 = log[2];
// all entries should be in correct order, without responses
expect(entry1).toMatchObject({ id: id1, response: null });
expect(entry2).toMatchObject({ id: id2, response: null });
expect(entry3).toMatchObject({ id: id3, response: null });
// all entries should be in correct order
expect(entry1).toMatchObject({ id: id1, responseTime: null });
expect(entry2).toMatchObject({ id: id2, responseTime: null });
expect(entry3).toMatchObject({ id: id3, responseTime: null });
// call response handlers
for (const i of [1, 2, 0]) {
@ -226,7 +231,7 @@ describe('PermissionLogController', () => {
it('handles a lack of response', () => {
let req = RPC_REQUESTS.test_method(SUBJECTS.a.origin);
req.id = REQUEST_IDS.a;
let res = { foo: 'bar' };
let res = { result: 'bar' };
// noop for next handler prevents recording of response
logMiddleware({ ...req }, res, noop);
@ -270,7 +275,7 @@ describe('PermissionLogController', () => {
let log = permLog.getActivityLog();
expect(log).toHaveLength(0);
const res = { foo: 'bar' };
const res = { result: 'bar' };
const req1 = RPC_REQUESTS.metamask_sendDomainMetadata(
SUBJECTS.c.origin,
'foobar',
@ -288,7 +293,7 @@ describe('PermissionLogController', () => {
it('enforces log limit', () => {
const req = RPC_REQUESTS.test_method(SUBJECTS.a.origin);
const res = { foo: 'bar' };
const res = { result: 'bar' };
// max out log
let lastId;
@ -647,19 +652,15 @@ function validateActivityEntry(entry, req, res, methodType, success) {
expect(entry.method).toStrictEqual(req.method);
expect(entry.origin).toStrictEqual(req.origin);
expect(entry.methodType).toStrictEqual(methodType);
expect(entry.request).toStrictEqual(stringify(req, null, 2));
expect(Number.isInteger(entry.requestTime)).toBe(true);
if (res) {
expect(Number.isInteger(entry.responseTime)).toBe(true);
expect(entry.requestTime <= entry.responseTime).toBe(true);
expect(entry.success).toStrictEqual(success);
expect(entry.response).toStrictEqual(stringify(res, null, 2));
} else {
expect(entry.requestTime > 0).toBe(true);
expect(entry).toMatchObject({
response: null,
responseTime: null,
success: null,
});

View File

@ -68,7 +68,7 @@ export default class PreferencesController {
ledgerTransportType: window.navigator.hid
? LEDGER_TRANSPORT_TYPES.WEBHID
: LEDGER_TRANSPORT_TYPES.U2F,
theme: 'default',
theme: 'light',
...opts.initState,
};

View File

@ -353,14 +353,14 @@ describe('preferences controller', function () {
});
describe('setTheme', function () {
it('should default to value "default"', function () {
it('should default to value "light"', function () {
const state = preferencesController.store.getState();
assert.equal(state.theme, 'default');
assert.equal(state.theme, 'light');
});
it('should set the setTheme property in state', function () {
const state = preferencesController.store.getState();
assert.equal(state.theme, 'default');
assert.equal(state.theme, 'light');
preferencesController.setTheme('dark');
assert.equal(preferencesController.store.getState().theme, 'dark');
});

View File

@ -39,6 +39,7 @@ import {
PRIORITY_LEVELS,
} from '../../../../shared/constants/gas';
import { decGWEIToHexWEI } from '../../../../shared/modules/conversion.utils';
import { EVENT } from '../../../../shared/constants/metametrics';
import {
HARDFORKS,
MAINNET,
@ -50,6 +51,7 @@ import {
determineTransactionType,
isEIP1559Transaction,
} from '../../../../shared/modules/transaction.utils';
import { ORIGIN_METAMASK } from '../../../../shared/constants/app';
import TransactionStateManager from './tx-state-manager';
import TxGasUtil from './tx-gas-utils';
import PendingTransactionTracker from './pending-tx-tracker';
@ -62,6 +64,15 @@ const SWAP_TRANSACTION_TYPES = [
TRANSACTION_TYPES.SWAP_APPROVAL,
];
// Only certain types of transactions should be allowed to be specified when
// adding a new unapproved transaction.
const VALID_UNAPPROVED_TRANSACTION_TYPES = [
...SWAP_TRANSACTION_TYPES,
TRANSACTION_TYPES.SIMPLE_SEND,
TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER,
TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM,
];
/**
* @typedef {import('../../../../shared/constants/transaction').TransactionMeta} TransactionMeta
* @typedef {import('../../../../shared/constants/transaction').TransactionMetaMetricsEventString} TransactionMetaMetricsEventString
@ -650,7 +661,7 @@ export default class TransactionController extends EventEmitter {
async addUnapprovedTransaction(txParams, origin, transactionType) {
if (
transactionType !== undefined &&
!SWAP_TRANSACTION_TYPES.includes(transactionType)
!VALID_UNAPPROVED_TRANSACTION_TYPES.includes(transactionType)
) {
throw new Error(
`TransactionController - invalid transactionType value: ${transactionType}`,
@ -674,7 +685,7 @@ export default class TransactionController extends EventEmitter {
origin,
});
if (origin === 'metamask') {
if (origin === ORIGIN_METAMASK) {
// Assert the from address is the selected address
if (normalizedTxParams.from !== this.getSelectedAddress()) {
throw ethErrors.rpc.internal({
@ -783,7 +794,7 @@ export default class TransactionController extends EventEmitter {
// then we set maxFeePerGas and maxPriorityFeePerGas to the suggested gasPrice.
txMeta.txParams.maxFeePerGas = txMeta.txParams.gasPrice;
txMeta.txParams.maxPriorityFeePerGas = txMeta.txParams.gasPrice;
if (eip1559V2Enabled && txMeta.origin !== 'metamask') {
if (eip1559V2Enabled && txMeta.origin !== ORIGIN_METAMASK) {
txMeta.userFeeLevel = PRIORITY_LEVELS.DAPP_SUGGESTED;
} else {
txMeta.userFeeLevel = CUSTOM_GAS_ESTIMATE;
@ -794,7 +805,7 @@ export default class TransactionController extends EventEmitter {
defaultMaxPriorityFeePerGas &&
!txMeta.txParams.maxFeePerGas &&
!txMeta.txParams.maxPriorityFeePerGas) ||
txMeta.origin === 'metamask'
txMeta.origin === ORIGIN_METAMASK
) {
txMeta.userFeeLevel = GAS_RECOMMENDATIONS.MEDIUM;
} else if (eip1559V2Enabled) {
@ -1779,7 +1790,10 @@ export default class TransactionController extends EventEmitter {
txMeta,
'transactions/pending-tx-tracker#event: tx:confirmed reference to confirmed txHash with same nonce',
);
this._dropTransaction(otherTxMeta.id);
// Drop any transaction that wasn't previously failed (off chain failure)
if (otherTxMeta.status !== TRANSACTION_STATUSES.FAILED) {
this._dropTransaction(otherTxMeta.id);
}
});
}
@ -1833,7 +1847,7 @@ export default class TransactionController extends EventEmitter {
this._trackMetaMetricsEvent({
event: 'Swap Failed',
sensitiveProperties: { ...txMeta.swapMetaData },
category: 'swaps',
category: EVENT.CATEGORIES.SWAPS,
});
} else {
const tokensReceived = getSwapsTokensReceivedFromTxMeta(
@ -1863,7 +1877,7 @@ export default class TransactionController extends EventEmitter {
this._trackMetaMetricsEvent({
event: 'Swap Completed',
category: 'swaps',
category: EVENT.CATEGORIES.SWAPS,
sensitiveProperties: {
...txMeta.swapMetaData,
token_to_amount_received: tokensReceived,
@ -1893,7 +1907,7 @@ export default class TransactionController extends EventEmitter {
defaultGasEstimates,
metamaskNetworkId: network,
} = txMeta;
const source = referrer === 'metamask' ? 'user' : 'dapp';
const source = referrer === ORIGIN_METAMASK ? 'user' : 'dapp';
const { assetType, tokenStandard } = await determineTransactionAssetType(
txMeta,
@ -2036,7 +2050,7 @@ export default class TransactionController extends EventEmitter {
// occur.
case TRANSACTION_EVENTS.ADDED:
this.createEventFragment({
category: 'Transactions',
category: EVENT.CATEGORIES.TRANSACTIONS,
initialEvent: TRANSACTION_EVENTS.ADDED,
successEvent: TRANSACTION_EVENTS.APPROVED,
failureEvent: TRANSACTION_EVENTS.REJECTED,
@ -2057,7 +2071,7 @@ export default class TransactionController extends EventEmitter {
case TRANSACTION_EVENTS.APPROVED:
case TRANSACTION_EVENTS.REJECTED:
this.createEventFragment({
category: 'Transactions',
category: EVENT.CATEGORIES.TRANSACTIONS,
successEvent: TRANSACTION_EVENTS.APPROVED,
failureEvent: TRANSACTION_EVENTS.REJECTED,
properties,
@ -2078,7 +2092,7 @@ export default class TransactionController extends EventEmitter {
// properties to the transaction event.
case TRANSACTION_EVENTS.SUBMITTED:
this.createEventFragment({
category: 'Transactions',
category: EVENT.CATEGORIES.TRANSACTIONS,
initialEvent: TRANSACTION_EVENTS.SUBMITTED,
successEvent: TRANSACTION_EVENTS.FINALIZED,
properties,
@ -2097,7 +2111,7 @@ export default class TransactionController extends EventEmitter {
// fragment does not exist.
case TRANSACTION_EVENTS.FINALIZED:
this.createEventFragment({
category: 'Transactions',
category: EVENT.CATEGORIES.TRANSACTIONS,
successEvent: TRANSACTION_EVENTS.FINALIZED,
properties,
sensitiveProperties,

View File

@ -10,6 +10,7 @@ import {
getTestAccounts,
} from '../../../../test/stub/provider';
import mockEstimates from '../../../../test/data/mock-estimates.json';
import { EVENT } from '../../../../shared/constants/metametrics';
import {
TRANSACTION_STATUSES,
TRANSACTION_TYPES,
@ -26,6 +27,7 @@ import {
import { TRANSACTION_ENVELOPE_TYPE_NAMES } from '../../../../ui/helpers/constants/transactions';
import { METAMASK_CONTROLLER_EVENTS } from '../../metamask-controller';
import { TOKEN_STANDARDS } from '../../../../ui/helpers/constants/common';
import { ORIGIN_METAMASK } from '../../../../shared/constants/app';
import TransactionController from '.';
const noop = () => true;
@ -791,7 +793,7 @@ describe('Transaction Controller', function () {
},
type: TRANSACTION_TYPES.SIMPLE_SEND,
transaction_envelope_type: TRANSACTION_ENVELOPE_TYPE_NAMES.LEGACY,
origin: 'metamask',
origin: ORIGIN_METAMASK,
chainId: currentChainId,
time: 1624408066355,
metamaskNetworkId: currentNetworkId,
@ -1442,7 +1444,7 @@ describe('Transaction Controller', function () {
nonce: '0x4b',
},
type: TRANSACTION_TYPES.SIMPLE_SEND,
origin: 'metamask',
origin: ORIGIN_METAMASK,
chainId: currentChainId,
time: 1624408066355,
metamaskNetworkId: currentNetworkId,
@ -1459,7 +1461,7 @@ describe('Transaction Controller', function () {
successEvent: 'Transaction Approved',
failureEvent: 'Transaction Rejected',
uniqueIdentifier: 'transaction-added-1',
category: 'Transactions',
category: EVENT.CATEGORIES.TRANSACTIONS,
persist: true,
properties: {
chain_id: '0x2a',
@ -1467,7 +1469,7 @@ describe('Transaction Controller', function () {
gas_edit_attempted: 'none',
gas_edit_type: 'none',
network: '42',
referrer: 'metamask',
referrer: ORIGIN_METAMASK,
source: 'user',
type: TRANSACTION_TYPES.SIMPLE_SEND,
account_type: 'MetaMask',
@ -1538,7 +1540,7 @@ describe('Transaction Controller', function () {
initialEvent: 'Transaction Submitted',
successEvent: 'Transaction Finalized',
uniqueIdentifier: 'transaction-submitted-1',
category: 'Transactions',
category: EVENT.CATEGORIES.TRANSACTIONS,
persist: true,
properties: {
chain_id: '0x2a',
@ -1546,7 +1548,7 @@ describe('Transaction Controller', function () {
gas_edit_attempted: 'none',
gas_edit_type: 'none',
network: '42',
referrer: 'metamask',
referrer: ORIGIN_METAMASK,
source: 'user',
type: TRANSACTION_TYPES.SIMPLE_SEND,
account_type: 'MetaMask',
@ -1627,7 +1629,7 @@ describe('Transaction Controller', function () {
successEvent: 'Transaction Approved',
failureEvent: 'Transaction Rejected',
uniqueIdentifier: 'transaction-added-1',
category: 'Transactions',
category: EVENT.CATEGORIES.TRANSACTIONS,
persist: true,
properties: {
chain_id: '0x2a',
@ -1708,7 +1710,7 @@ describe('Transaction Controller', function () {
initialEvent: 'Transaction Submitted',
successEvent: 'Transaction Finalized',
uniqueIdentifier: 'transaction-submitted-1',
category: 'Transactions',
category: EVENT.CATEGORIES.TRANSACTIONS,
persist: true,
properties: {
chain_id: '0x2a',
@ -1789,7 +1791,7 @@ describe('Transaction Controller', function () {
successEvent: 'Transaction Approved',
failureEvent: 'Transaction Rejected',
uniqueIdentifier: 'transaction-added-1',
category: 'Transactions',
category: EVENT.CATEGORIES.TRANSACTIONS,
persist: true,
properties: {
chain_id: '0x2a',
@ -1853,7 +1855,7 @@ describe('Transaction Controller', function () {
failureEvent: 'Transaction Rejected',
uniqueIdentifier: 'transaction-added-1',
persist: true,
category: 'Transactions',
category: EVENT.CATEGORIES.TRANSACTIONS,
properties: {
network: '42',
referrer: 'other',
@ -1926,7 +1928,7 @@ describe('Transaction Controller', function () {
failureEvent: 'Transaction Rejected',
uniqueIdentifier: 'transaction-added-1',
persist: true,
category: 'Transactions',
category: EVENT.CATEGORIES.TRANSACTIONS,
properties: {
chain_id: '0x2a',
eip_1559_version: '1',

View File

@ -6,6 +6,7 @@ import createId from '../../../../shared/modules/random-id';
import { TRANSACTION_STATUSES } from '../../../../shared/constants/transaction';
import { METAMASK_CONTROLLER_EVENTS } from '../../metamask-controller';
import { transactionMatchesNetwork } from '../../../../shared/modules/transaction.utils';
import { ORIGIN_METAMASK } from '../../../../shared/constants/app';
import {
generateHistoryEntry,
replayHistory,
@ -92,7 +93,7 @@ export default class TransactionStateManager extends EventEmitter {
if (
opts.txParams &&
typeof opts.origin === 'string' &&
opts.origin !== 'metamask'
opts.origin !== ORIGIN_METAMASK
) {
if (typeof opts.txParams.gasPrice !== 'undefined') {
dappSuggestedGasFees = {

View File

@ -11,6 +11,7 @@ import {
KOVAN_NETWORK_ID,
} from '../../../../shared/constants/network';
import { GAS_LIMITS } from '../../../../shared/constants/gas';
import { ORIGIN_METAMASK } from '../../../../shared/constants/app';
import TxStateManager from './tx-state-manager';
import { snapshotFromTxMeta } from './lib/tx-state-history-helpers';
@ -1188,7 +1189,7 @@ describe('TransactionStateManager', function () {
};
const generatedTransaction = txStateManager.generateTxMeta({
txParams,
origin: 'metamask',
origin: ORIGIN_METAMASK,
});
assert.ok(generatedTransaction);
assert.strictEqual(generatedTransaction.dappSuggestedGasFees, null);

View File

@ -0,0 +1,55 @@
/**
* Sets up two-way communication between the
* mainline version of extension and Flask build
* in order to detect & warn if there are two different
* versions running simultaneously.
*/
import browser from 'webextension-polyfill';
import {
PLATFORM_CHROME,
PLATFORM_FIREFOX,
CHROME_BUILD_IDS,
FIREFOX_BUILD_IDS,
} from '../../shared/constants/app';
import { getPlatform } from './lib/util';
const MESSAGE_TEXT = 'isRunning';
const showWarning = () =>
console.warn('Warning! You have multiple instances of MetaMask running!');
/**
* Handles the ping message sent from other extension.
* Displays console warning if it's active.
*
* @param message - The message received from the other extension
*/
export const onMessageReceived = (message) => {
if (message === MESSAGE_TEXT) {
showWarning();
}
};
/**
* Sends the ping message sent to other extensions to detect whether it's active or not.
*/
export const checkForMultipleVersionsRunning = async () => {
if (getPlatform() !== PLATFORM_CHROME && getPlatform() !== PLATFORM_FIREFOX) {
return;
}
const buildIds =
getPlatform() === PLATFORM_CHROME ? CHROME_BUILD_IDS : FIREFOX_BUILD_IDS;
const thisBuild = browser.runtime.id;
for (const id of buildIds) {
if (id !== thisBuild) {
try {
await browser.runtime.sendMessage(id, MESSAGE_TEXT);
} catch (error) {
// Should do nothing if receiving end was not reached (no other instances running)
}
}
}
};

View File

@ -0,0 +1,104 @@
import { strict as assert } from 'assert';
import browser from 'webextension-polyfill';
import sinon from 'sinon';
import {
PLATFORM_CHROME,
PLATFORM_EDGE,
METAMASK_BETA_CHROME_ID,
METAMASK_PROD_CHROME_ID,
METAMASK_FLASK_CHROME_ID,
} from '../../shared/constants/app';
import {
checkForMultipleVersionsRunning,
onMessageReceived,
} from './detect-multiple-instances';
import * as util from './lib/util';
describe('multiple instances running detector', function () {
const PING_MESSAGE = 'isRunning';
let sendMessageStub = sinon.stub();
beforeEach(async function () {
sinon.replace(browser, 'runtime', {
sendMessage: sendMessageStub,
id: METAMASK_BETA_CHROME_ID,
});
sinon.stub(util, 'getPlatform').callsFake((_) => {
return PLATFORM_CHROME;
});
});
afterEach(function () {
sinon.restore();
});
describe('checkForMultipleVersionsRunning', function () {
it('should send ping message to multiple instances', async function () {
await checkForMultipleVersionsRunning();
assert(sendMessageStub.calledTwice);
assert(
sendMessageStub
.getCall(0)
.calledWithExactly(METAMASK_PROD_CHROME_ID, PING_MESSAGE),
);
assert(
sendMessageStub
.getCall(1)
.calledWithExactly(METAMASK_FLASK_CHROME_ID, PING_MESSAGE),
);
});
it('should not send ping message if platform is not Chrome or Firefox', async function () {
util.getPlatform.restore();
sendMessageStub = sinon.stub();
sinon.stub(util, 'getPlatform').callsFake((_) => {
return PLATFORM_EDGE;
});
await checkForMultipleVersionsRunning();
assert(sendMessageStub.notCalled);
});
it('should not expose an error outside if sendMessage throws', async function () {
sinon.restore();
sinon.replace(browser, 'runtime', {
sendMessage: sinon.stub().throws(),
id: METAMASK_BETA_CHROME_ID,
});
const spy = sinon.spy(checkForMultipleVersionsRunning);
await checkForMultipleVersionsRunning();
assert(!spy.threw());
});
});
describe('onMessageReceived', function () {
beforeEach(function () {
sinon.spy(console, 'warn');
});
it('should print warning message to on ping message received', async function () {
onMessageReceived(PING_MESSAGE);
assert(
console.warn.calledWithExactly(
'Warning! You have multiple instances of MetaMask running!',
),
);
});
it('should not print warning message if wrong message received', async function () {
onMessageReceived(PING_MESSAGE.concat('wrong'));
assert(console.warn.notCalled);
});
});
});

View File

@ -1,4 +1,4 @@
import { EVENT_NAMES } from '../../../shared/constants/metametrics';
import { EVENT, EVENT_NAMES } from '../../../shared/constants/metametrics';
import { SECOND } from '../../../shared/constants/time';
const USER_PROMPTED_EVENT_NAME_MAP = {
@ -46,7 +46,7 @@ export default function createRPCMethodTrackingMiddleware({
const userRejected = res.error?.code === 4001;
trackEvent({
event: USER_PROMPTED_EVENT_NAME_MAP[req.method],
category: 'inpage_provider',
category: EVENT.CATEGORIES.INPAGE_PROVIDER,
referrer: {
url: origin,
},
@ -62,7 +62,7 @@ export default function createRPCMethodTrackingMiddleware({
} else if (typeof samplingTimeouts[req.method] === 'undefined') {
trackEvent({
event: 'Provider Method Called',
category: 'inpage_provider',
category: EVENT.CATEGORIES.INPAGE_PROVIDER,
referrer: {
url: origin,
},

View File

@ -4,6 +4,7 @@ import { bufferToHex, stripHexPrefix } from 'ethereumjs-util';
import { ethErrors } from 'eth-rpc-errors';
import log from 'loglevel';
import { MESSAGE_TYPE } from '../../../shared/constants/app';
import { EVENT } from '../../../shared/constants/metametrics';
import { METAMASK_CONTROLLER_EVENTS } from '../metamask-controller';
import createId from '../../../shared/modules/random-id';
import { addHexPrefix } from './util';
@ -227,7 +228,7 @@ export default class DecryptMessageManager extends EventEmitter {
if (reason) {
this.metricsEvent({
event: reason,
category: 'Messages',
category: EVENT.CATEGORIES.MESSAGES,
properties: {
action: 'Decrypt Message Request',
},

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 { EVENT } from '../../../shared/constants/metametrics';
import { METAMASK_CONTROLLER_EVENTS } from '../metamask-controller';
import createId from '../../../shared/modules/random-id';
@ -216,7 +217,7 @@ export default class EncryptionPublicKeyManager extends EventEmitter {
if (reason) {
this.metricsEvent({
event: reason,
category: 'Messages',
category: EVENT.CATEGORIES.MESSAGES,
properties: {
action: 'Encryption public key Request',
},

View File

@ -5,6 +5,7 @@ import { ethErrors } from 'eth-rpc-errors';
import { MESSAGE_TYPE } from '../../../shared/constants/app';
import { METAMASK_CONTROLLER_EVENTS } from '../metamask-controller';
import createId from '../../../shared/modules/random-id';
import { EVENT } from '../../../shared/constants/metametrics';
/**
* Represents, and contains data about, an 'eth_sign' type signature request. These are created when a signature for
@ -211,7 +212,7 @@ export default class MessageManager extends EventEmitter {
const msg = this.getMsg(msgId);
this.metricsEvent({
event: reason,
category: 'Transactions',
category: EVENT.CATEGORIES.TRANSACTIONS,
properties: {
action: 'Sign Request',
type: msg.type,

View File

@ -6,6 +6,7 @@ import log from 'loglevel';
import { MESSAGE_TYPE } from '../../../shared/constants/app';
import { METAMASK_CONTROLLER_EVENTS } from '../metamask-controller';
import createId from '../../../shared/modules/random-id';
import { EVENT } from '../../../shared/constants/metametrics';
import { addHexPrefix } from './util';
const hexRe = /^[0-9A-Fa-f]+$/gu;
@ -231,7 +232,7 @@ export default class PersonalMessageManager extends EventEmitter {
const msg = this.getMsg(msgId);
this.metricsEvent({
event: reason,
category: 'Transactions',
category: EVENT.CATEGORIES.TRANSACTIONS,
properties: {
action: 'Sign Request',
type: msg.type,

View File

@ -2,6 +2,7 @@ import { ethErrors, errorCodes } from 'eth-rpc-errors';
import validUrl from 'valid-url';
import { omit } from 'lodash';
import { MESSAGE_TYPE } from '../../../../../shared/constants/app';
import { EVENT } from '../../../../../shared/constants/metametrics';
import {
isPrefixedFormattedHexString,
isSafeChainId,
@ -249,7 +250,7 @@ async function addEthereumChainHandler(
sendMetrics({
event: 'Custom Network Added',
category: 'Network',
category: EVENT.CATEGORIES.NETWORK,
referrer: {
url: origin,
},

View File

@ -1,4 +1,5 @@
import { MESSAGE_TYPE } from '../../../../../shared/constants/app';
import { EVENT } from '../../../../../shared/constants/metametrics';
/**
* This RPC method is called by the inpage provider whenever it detects the
@ -48,7 +49,7 @@ function logWeb3ShimUsageHandler(
sendMetrics(
{
event: `Website Accessed window.web3 Shim`,
category: 'inpage_provider',
category: EVENT.CATEGORIES.INPAGE_PROVIDER,
referrer: {
url: origin,
},

View File

@ -1,3 +1,4 @@
import { ethErrors } from 'eth-rpc-errors';
import { MESSAGE_TYPE } from '../../../../../shared/constants/app';
const watchAsset = {
@ -36,9 +37,14 @@ async function watchAssetHandler(
) {
try {
const { options: asset, type } = req.params;
res.result = await handleWatchAssetRequest(asset, type);
const handleWatchAssetResult = await handleWatchAssetRequest(asset, type);
await handleWatchAssetResult.result;
res.result = true;
return end();
} catch (error) {
if (error.message === 'User rejected to watch the asset.') {
return end(ethErrors.provider.userRejectedRequest());
}
return end(error);
}
}

View File

@ -8,6 +8,7 @@ import jsonschema from 'jsonschema';
import { MESSAGE_TYPE } from '../../../shared/constants/app';
import { METAMASK_CONTROLLER_EVENTS } from '../metamask-controller';
import createId from '../../../shared/modules/random-id';
import { EVENT } from '../../../shared/constants/metametrics';
import { isValidHexAddress } from '../../../shared/modules/hexstring-utils';
/**
@ -303,7 +304,7 @@ export default class TypedMessageManager extends EventEmitter {
const msg = this.getMsg(msgId);
this.metricsEvent({
event: reason,
category: 'Transactions',
category: EVENT.CATEGORIES.TRANSACTIONS,
properties: {
action: 'Sign Request',
version: msg.msgParams.version,

View File

@ -30,7 +30,7 @@ import {
ControllerMessenger,
CurrencyRateController,
PhishingController,
NotificationController,
AnnouncementController,
GasFeeController,
TokenListController,
TokensController,
@ -75,17 +75,23 @@ import { UI_NOTIFICATIONS } from '../../shared/notifications';
import { toChecksumHexAddress } from '../../shared/modules/hexstring-utils';
import { MILLISECOND } from '../../shared/constants/time';
import {
ORIGIN_METAMASK,
///: BEGIN:ONLY_INCLUDE_IN(flask)
MESSAGE_TYPE,
///: END:ONLY_INCLUDE_IN
POLLING_TOKEN_ENVIRONMENT_TYPES,
SUBJECT_TYPES,
} from '../../shared/constants/app';
import { EVENT } from '../../shared/constants/metametrics';
import { hexToDecimal } from '../../ui/helpers/utils/conversions.util';
import { getTokenValueParam } from '../../ui/helpers/utils/token-util';
import { isEqualCaseInsensitive } from '../../shared/modules/string-utils';
import { parseStandardTokenTransactionData } from '../../shared/modules/transaction.utils';
import {
onMessageReceived,
checkForMultipleVersionsRunning,
} from './detect-multiple-instances';
import ComposableObservableStore from './lib/ComposableObservableStore';
import AccountTracker from './lib/account-tracker';
import createLoggerMiddleware from './lib/createLoggerMiddleware';
@ -396,7 +402,10 @@ export default class MetamaskController extends EventEmitter {
this.currencyRateController = new CurrencyRateController({
includeUSDRate: true,
messenger: currencyRateMessenger,
state: initState.CurrencyController,
state: {
...initState.CurrencyController,
nativeCurrency: this.networkController.providerStore.getState().ticker,
},
});
const tokenListMessenger = this.controllerMessenger.getRestricted({
@ -449,9 +458,9 @@ export default class MetamaskController extends EventEmitter {
this.phishingController = new PhishingController();
this.notificationController = new NotificationController(
{ allNotifications: UI_NOTIFICATIONS },
initState.NotificationController,
this.announcementController = new AnnouncementController(
{ allAnnouncements: UI_NOTIFICATIONS },
initState.AnnouncementController,
);
// token exchange rate tracker
@ -628,7 +637,7 @@ export default class MetamaskController extends EventEmitter {
this.workerController = new IframeExecutionService({
onError: this.onExecutionEnvironmentError.bind(this),
iframeUrl: new URL(
'https://metamask.github.io/iframe-execution-environment/0.4.3',
'https://metamask.github.io/iframe-execution-environment/0.4.4',
),
messenger: this.controllerMessenger.getRestricted({
name: 'ExecutionService',
@ -649,6 +658,7 @@ export default class MetamaskController extends EventEmitter {
`${this.permissionController.name}:hasPermissions`,
`${this.permissionController.name}:requestPermissions`,
`${this.permissionController.name}:revokeAllPermissions`,
`${this.permissionController.name}:revokePermissionForAllSubjects`,
],
});
@ -848,7 +858,7 @@ export default class MetamaskController extends EventEmitter {
this.metaMetricsController.trackEvent(
{
event: 'Tx Status Update: On-Chain Failure',
category: 'Background',
category: EVENT.CATEGORIES.BACKGROUND,
properties: {
action: 'Transactions',
errorMessage: txMeta.simulationFails?.reason,
@ -979,7 +989,7 @@ export default class MetamaskController extends EventEmitter {
PermissionLogController: this.permissionLogController.store,
SubjectMetadataController: this.subjectMetadataController,
ThreeBoxController: this.threeBoxController.store,
NotificationController: this.notificationController,
AnnouncementController: this.announcementController,
GasFeeController: this.gasFeeController,
TokenListController: this.tokenListController,
TokensController: this.tokensController,
@ -1019,7 +1029,7 @@ export default class MetamaskController extends EventEmitter {
SwapsController: this.swapsController.store,
EnsController: this.ensController.store,
ApprovalController: this.approvalController,
NotificationController: this.notificationController,
AnnouncementController: this.announcementController,
GasFeeController: this.gasFeeController,
TokenListController: this.tokenListController,
TokensController: this.tokensController,
@ -1057,6 +1067,11 @@ export default class MetamaskController extends EventEmitter {
// TODO:LegacyProvider: Delete
this.publicConfigStore = this.createPublicConfigStore();
// Multiple MetaMask instances launched warning
this.extension.runtime.onMessageExternal.addListener(onMessageReceived);
// Fire a ping message to check if other extensions are running
checkForMultipleVersionsRunning();
}
///: BEGIN:ONLY_INCLUDE_IN(flask)
@ -1071,13 +1086,14 @@ export default class MetamaskController extends EventEmitter {
this.controllerMessenger,
'SnapController:add',
),
clearSnapState: (fromSubject) =>
this.controllerMessenger.call(
'SnapController:updateSnapState',
fromSubject,
null,
),
clearSnapState: this.controllerMessenger.call.bind(
this.controllerMessenger,
'SnapController:clearSnapState',
),
getMnemonic: this.getPrimaryKeyringMnemonic.bind(this),
getUnlockPromise: this.appStateController.getUnlockPromise.bind(
this.appStateController,
),
getSnap: this.controllerMessenger.call.bind(
this.controllerMessenger,
'SnapController:get',
@ -1201,7 +1217,7 @@ export default class MetamaskController extends EventEmitter {
(snapId) => {
this.metaMetricsController.trackEvent({
event: 'Snap Installed',
category: 'Snaps',
category: EVENT.CATEGORIES.SNAPS,
properties: {
snap_id: snapId,
},
@ -1224,7 +1240,7 @@ export default class MetamaskController extends EventEmitter {
version,
// account mgmt
getAccounts: async ({ origin }) => {
if (origin === 'metamask') {
if (origin === ORIGIN_METAMASK) {
const selectedAddress = this.preferencesController.getSelectedAddress();
return selectedAddress ? [selectedAddress] : [];
} else if (this.isUnlocked()) {
@ -1364,7 +1380,7 @@ export default class MetamaskController extends EventEmitter {
keyringController,
metaMetricsController,
networkController,
notificationController,
announcementController,
onboardingController,
permissionController,
preferencesController,
@ -1819,8 +1835,8 @@ export default class MetamaskController extends EventEmitter {
},
// Notifications
updateViewedNotifications: notificationController.updateViewed.bind(
notificationController,
updateViewedNotifications: announcementController.updateViewed.bind(
announcementController,
),
// GasFeeController
@ -3270,7 +3286,7 @@ export default class MetamaskController extends EventEmitter {
setupProviderConnection(outStream, sender, subjectType) {
let origin;
if (subjectType === SUBJECT_TYPES.INTERNAL) {
origin = 'metamask';
origin = ORIGIN_METAMASK;
}
///: BEGIN:ONLY_INCLUDE_IN(flask)
else if (subjectType === SUBJECT_TYPES.SNAP) {
@ -3513,6 +3529,10 @@ export default class MetamaskController extends EventEmitter {
return Object.values(approvedPermissions);
},
getPermissions: this.permissionController.getPermissions.bind(
this.permissionController,
origin,
),
getAccounts: this.getPermittedAccounts.bind(this, origin),
installSnaps: this.snapController.installSnaps.bind(
this.snapController,
@ -3572,7 +3592,7 @@ export default class MetamaskController extends EventEmitter {
* @returns {string} The connection's id (so that it can be deleted later)
*/
addConnection(origin, { engine }) {
if (origin === 'metamask') {
if (origin === ORIGIN_METAMASK) {
return null;
}

View File

@ -6,6 +6,7 @@ import { pubToAddress, bufferToHex } from 'ethereumjs-util';
import { obj as createThoughStream } from 'through2';
import EthQuery from 'eth-query';
import proxyquire from 'proxyquire';
import browser from 'webextension-polyfill';
import { TRANSACTION_STATUSES } from '../../shared/constants/transaction';
import createTxMeta from '../../test/lib/createTxMeta';
import { NETWORK_TYPE_RPC } from '../../shared/constants/network';
@ -71,6 +72,9 @@ const browserPolyfillMock = {
onInstalled: {
addListener: () => undefined,
},
onMessageExternal: {
addListener: () => undefined,
},
getPlatformInfo: async () => 'mac',
},
};
@ -131,6 +135,10 @@ describe('MetaMaskController', function () {
.get(/.*/u)
.reply(200, '{"JPY":12415.9}');
sandbox.replace(browser, 'runtime', {
sendMessage: sandbox.stub().rejects(),
});
metamaskController = new MetaMaskController({
showUserConfirmation: noop,
encryptor: {

View File

@ -0,0 +1,40 @@
import { cloneDeep } from 'lodash';
const version = 70;
/**
* Removes the `request` and `response` properties from
* `PermissionLogController.permissionActivityLog` objects.
*/
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 (Array.isArray(state?.PermissionLogController?.permissionActivityLog)) {
const {
PermissionLogController: { permissionActivityLog },
} = state;
// mutate activity log entries in place
permissionActivityLog.forEach((logEntry) => {
if (
logEntry &&
typeof logEntry === 'object' &&
!Array.isArray(logEntry)
) {
delete logEntry.request;
delete logEntry.response;
}
});
}
return state;
}

View File

@ -0,0 +1,273 @@
import migration70 from './070';
describe('migration #70', () => {
it('should update the version metadata', async () => {
const oldStorage = {
meta: {
version: 69,
},
data: {},
};
const newStorage = await migration70.migrate(oldStorage);
expect(newStorage.meta).toStrictEqual({
version: 70,
});
});
it('should migrate all data', async () => {
const oldStorage = {
meta: {
version: 69,
},
data: {
FooController: { a: 'b' },
PermissionLogController: {
permissionActivityLog: [
{
id: 522690215,
method: 'eth_accounts',
methodType: 'restricted',
origin: 'https://metamask.io',
request: {
method: 'eth_accounts',
params: [],
jsonrpc: '2.0',
id: 522690215,
origin: 'https://metamask.io',
tabId: 5,
},
requestTime: 1602643170686,
response: {
id: 522690215,
jsonrpc: '2.0',
result: [],
},
responseTime: 1602643170688,
success: true,
},
{
id: 1620464600,
method: 'eth_accounts',
methodType: 'restricted',
origin: 'https://widget.getacute.io',
request: {
method: 'eth_accounts',
params: [],
jsonrpc: '2.0',
id: 1620464600,
origin: 'https://widget.getacute.io',
tabId: 5,
},
requestTime: 1602643172935,
response: {
id: 1620464600,
jsonrpc: '2.0',
result: [],
},
responseTime: 1602643172935,
success: true,
},
{
id: 4279100021,
method: 'eth_accounts',
methodType: 'restricted',
origin: 'https://app.uniswap.org',
request: {
method: 'eth_accounts',
jsonrpc: '2.0',
id: 4279100021,
origin: 'https://app.uniswap.org',
tabId: 5,
},
requestTime: 1620710669962,
response: {
id: 4279100021,
jsonrpc: '2.0',
result: [],
},
responseTime: 1620710669963,
success: true,
},
{
id: 4279100022,
method: 'eth_requestAccounts',
methodType: 'restricted',
origin: 'https://app.uniswap.org',
request: {
method: 'eth_requestAccounts',
jsonrpc: '2.0',
id: 4279100022,
origin: 'https://app.uniswap.org',
tabId: 5,
},
requestTime: 1620710686872,
response: {
id: 4279100022,
jsonrpc: '2.0',
result: ['0x64a845a5b02460acf8a3d84503b0d68d028b4bb4'],
},
responseTime: 1620710693187,
success: true,
},
{
id: 4279100023,
method: 'eth_requestAccounts',
methodType: 'restricted',
origin: 'https://app.uniswap.org',
request: {
method: 'eth_requestAccounts',
jsonrpc: '2.0',
id: 4279100023,
origin: 'https://app.uniswap.org',
tabId: 5,
},
requestTime: 1620710693204,
response: {
id: 4279100023,
jsonrpc: '2.0',
result: ['0x64a845a5b02460acf8a3d84503b0d68d028b4bb4'],
},
responseTime: 1620710693213,
success: true,
},
{
id: 4279100034,
method: 'eth_accounts',
methodType: 'restricted',
origin: 'https://app.uniswap.org',
request: {
method: 'eth_accounts',
params: [],
jsonrpc: '2.0',
id: 4279100034,
origin: 'https://app.uniswap.org',
tabId: 5,
},
requestTime: 1620710712072,
response: {
id: 4279100034,
jsonrpc: '2.0',
result: ['0x64a845a5b02460acf8a3d84503b0d68d028b4bb4'],
},
responseTime: 1620710712075,
success: true,
},
],
},
},
};
const newStorage = await migration70.migrate(oldStorage);
expect(newStorage).toStrictEqual({
meta: {
version: 70,
},
data: {
FooController: { a: 'b' },
PermissionLogController: {
permissionActivityLog: [
{
id: 522690215,
method: 'eth_accounts',
methodType: 'restricted',
origin: 'https://metamask.io',
requestTime: 1602643170686,
responseTime: 1602643170688,
success: true,
},
{
id: 1620464600,
method: 'eth_accounts',
methodType: 'restricted',
origin: 'https://widget.getacute.io',
requestTime: 1602643172935,
responseTime: 1602643172935,
success: true,
},
{
id: 4279100021,
method: 'eth_accounts',
methodType: 'restricted',
origin: 'https://app.uniswap.org',
requestTime: 1620710669962,
responseTime: 1620710669963,
success: true,
},
{
id: 4279100022,
method: 'eth_requestAccounts',
methodType: 'restricted',
origin: 'https://app.uniswap.org',
requestTime: 1620710686872,
responseTime: 1620710693187,
success: true,
},
{
id: 4279100023,
method: 'eth_requestAccounts',
methodType: 'restricted',
origin: 'https://app.uniswap.org',
requestTime: 1620710693204,
responseTime: 1620710693213,
success: true,
},
{
id: 4279100034,
method: 'eth_accounts',
methodType: 'restricted',
origin: 'https://app.uniswap.org',
requestTime: 1620710712072,
responseTime: 1620710712075,
success: true,
},
],
},
},
});
});
it('should handle missing PermissionLogController', async () => {
const oldStorage = {
meta: {
version: 69,
},
data: {
FooController: { a: 'b' },
},
};
const newStorage = await migration70.migrate(oldStorage);
expect(newStorage).toStrictEqual({
meta: {
version: 70,
},
data: {
FooController: { a: 'b' },
},
});
});
it('should handle missing PermissionLogController.permissionActivityLog', async () => {
const oldStorage = {
meta: {
version: 69,
},
data: {
FooController: { a: 'b' },
PermissionLogController: {},
},
};
const newStorage = await migration70.migrate(oldStorage);
expect(newStorage).toStrictEqual({
meta: {
version: 70,
},
data: {
FooController: { a: 'b' },
PermissionLogController: {},
},
});
});
});

View File

@ -0,0 +1,28 @@
import { cloneDeep } from 'lodash';
const version = 71;
/**
* Renames NotificationController to AnnouncementController
*/
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.NotificationController) {
state.AnnouncementController = {
announcements: state.NotificationController.notifications,
};
delete state.NotificationController;
}
return state;
}

View File

@ -0,0 +1,107 @@
import migration71 from './071';
describe('migration #71', () => {
it('should update the version metadata', async () => {
const oldStorage = {
meta: {
version: 70,
},
data: {},
};
const newStorage = await migration71.migrate(oldStorage);
expect(newStorage.meta).toStrictEqual({
version: 71,
});
});
it('should rename NotificationController', async () => {
const oldStorage = {
meta: {
version: 70,
},
data: {
FooController: { a: 'b' },
NotificationController: {
notifications: [
{
date: '2021-03-17',
id: 1,
image: {
height: '230px',
placeImageBelowDescription: true,
src: 'images/mobile-link-qr.svg',
width: '230px',
},
isShown: false,
},
{ date: '2021-03-08', id: 3, isShown: false },
{
date: '2021-05-11',
id: 4,
image: { src: 'images/source-logos-bsc.svg', width: '100%' },
isShown: false,
},
{ date: '2021-06-09', id: 5, isShown: false },
{ date: '2021-05-26', id: 6, isShown: false },
{ date: '2021-09-17', id: 7, isShown: false },
{ date: '2021-11-01', id: 8, isShown: false },
{
date: '2021-12-07',
id: 9,
image: { src: 'images/txinsights.png', width: '80%' },
isShown: false,
},
{
date: '2022-04-18',
id: 10,
image: { src: 'images/token-detection.svg', width: '100%' },
isShown: false,
},
{ date: '2022-04-18', id: 11, isShown: false },
{
date: '2022-05-18',
id: 12,
image: { src: 'images/darkmode-banner.png', width: '100%' },
isShown: true,
},
],
},
},
};
const newStorage = await migration71.migrate(oldStorage);
expect(newStorage).toStrictEqual({
meta: {
version: 71,
},
data: {
FooController: { a: 'b' },
AnnouncementController: {
announcements: oldStorage.data.NotificationController.notifications,
},
},
});
});
it('should handle missing NotificationController', async () => {
const oldStorage = {
meta: {
version: 70,
},
data: {
FooController: { a: 'b' },
},
};
const newStorage = await migration71.migrate(oldStorage);
expect(newStorage).toStrictEqual({
meta: {
version: 71,
},
data: {
FooController: { a: 'b' },
},
});
});
});

View File

@ -73,6 +73,8 @@ import m066 from './066';
import m067 from './067';
import m068 from './068';
import m069 from './069';
import m070 from './070';
import m071 from './071';
const migrations = [
m002,
@ -143,6 +145,8 @@ const migrations = [
m067,
m068,
m069,
m070,
m071,
];
export default migrations;

View File

@ -51,6 +51,7 @@ require('eslint-plugin-node');
require('eslint-plugin-prettier');
require('eslint-plugin-react');
require('eslint-plugin-react-hooks');
require('eslint-plugin-jest');
defineAndRunBuildTasks();

View File

@ -473,21 +473,21 @@ function createFactoredBuild({
groupSet,
commonSet,
browserPlatforms,
useLavamoat: false,
useLavamoat: true,
});
renderHtmlFile({
htmlName: 'notification',
groupSet,
commonSet,
browserPlatforms,
useLavamoat: false,
useLavamoat: true,
});
renderHtmlFile({
htmlName: 'home',
groupSet,
commonSet,
browserPlatforms,
useLavamoat: false,
useLavamoat: true,
});
break;
}

View File

@ -55,6 +55,22 @@
"globals": {
"localStorage": true
}
},
"react-dom": {
"globals": {
"HTMLIFrameElement": true
}
},
"react-devtools": {
"packages": {
"react-devtools-core": true
}
},
"react-devtools-core": {
"globals": {
"setTimeout": true,
"WebSocket": true
}
}
}
}

View File

@ -733,8 +733,10 @@
"Worker": true,
"clearTimeout": true,
"console.error": true,
"console.info": true,
"console.log": true,
"console.warn": true,
"fetch": true,
"setTimeout": true
},
"packages": {
@ -747,7 +749,6 @@
"ajv": true,
"buffer": true,
"concat-stream": true,
"cross-fetch": true,
"crypto-browserify": true,
"eth-rpc-errors": true,
"fast-deep-equal": true,

View File

@ -50,6 +50,22 @@
"globals": {
"localStorage": true
}
},
"react-dom": {
"globals": {
"HTMLIFrameElement": true
}
},
"react-devtools": {
"packages": {
"react-devtools-core": true
}
},
"react-devtools-core": {
"globals": {
"setTimeout": true,
"WebSocket": true
}
}
}
}

View File

@ -751,8 +751,10 @@
"Worker": true,
"clearTimeout": true,
"console.error": true,
"console.info": true,
"console.log": true,
"console.warn": true,
"fetch": true,
"setTimeout": true
},
"packages": {
@ -765,7 +767,6 @@
"ajv": true,
"buffer": true,
"concat-stream": true,
"cross-fetch": true,
"crypto-browserify": true,
"eth-rpc-errors": true,
"fast-deep-equal": true,

View File

@ -55,6 +55,22 @@
"globals": {
"localStorage": true
}
},
"react-dom": {
"globals": {
"HTMLIFrameElement": true
}
},
"react-devtools": {
"packages": {
"react-devtools-core": true
}
},
"react-devtools-core": {
"globals": {
"setTimeout": true,
"WebSocket": true
}
}
}
}

View File

@ -733,8 +733,10 @@
"Worker": true,
"clearTimeout": true,
"console.error": true,
"console.info": true,
"console.log": true,
"console.warn": true,
"fetch": true,
"setTimeout": true
},
"packages": {
@ -747,7 +749,6 @@
"ajv": true,
"buffer": true,
"concat-stream": true,
"cross-fetch": true,
"crypto-browserify": true,
"eth-rpc-errors": true,
"fast-deep-equal": true,

View File

@ -2,20 +2,20 @@
"resources": {
"@babel/core": {
"packages": {
"<root>": true,
"$root$": true,
"@babel/preset-env": true,
"@babel/preset-react": true,
"@babel/plugin-transform-runtime": true,
"@babel/plugin-proposal-class-properties": true,
"@babel/plugin-proposal-nullish-coalescing-operator": true,
"@babel/plugin-proposal-object-rest-spread": true,
"@babel/plugin-proposal-optional-chaining": true,
"@babel/plugin-transform-runtime": true,
"@babel/preset-env": true,
"@babel/preset-react": true,
"@babel/preset-typescript": true
}
},
"@eslint/eslintrc": {
"eslint>@eslint/eslintrc": {
"packages": {
"<root>": true,
"$root$": true,
"@babel/eslint-parser": true,
"@babel/eslint-plugin": true,
"@metamask/eslint-config": true,
@ -32,58 +32,56 @@
"eslint-plugin-react-hooks": true
}
},
"@typescript-eslint/eslint-plugin": {
"eslint-plugin-jest": {
"packages": {
"@typescript-eslint/experimental-utils": true,
"@typescript-eslint/scope-manager": true,
"debug": true,
"eslint": true,
"ignore": true,
"regexpp": true,
"semver": true,
"tsutils": true,
"typescript": true
"eslint-plugin-jest>@typescript-eslint/experimental-utils": true,
"@typescript-eslint/eslint-plugin": true
}
},
"@typescript-eslint/experimental-utils": {
"eslint-plugin-jest>@typescript-eslint/experimental-utils": {
"builtin": {
"path": true
},
"packages": {
"@typescript-eslint/scope-manager": true,
"@typescript-eslint/types": true,
"eslint-plugin-jest>@typescript-eslint/experimental-utils>eslint-utils": true,
"@typescript-eslint/parser>@typescript-eslint/types": true,
"eslint": true,
"eslint-scope": true,
"eslint-utils": true
"@typescript-eslint/parser>@typescript-eslint/scope-manager": true,
"eslint>eslint-scope": true
}
},
"@typescript-eslint/scope-manager": {
"eslint-plugin-jest>@typescript-eslint/experimental-utils>eslint-utils": {
"packages": {
"@typescript-eslint/types": true,
"@typescript-eslint/visitor-keys": true
"eslint-plugin-jest>@typescript-eslint/experimental-utils>eslint-utils>eslint-visitor-keys": true
}
},
"@typescript-eslint/visitor-keys": {
"@typescript-eslint/eslint-plugin": {
"packages": {
"eslint-visitor-keys": true
"typescript": true,
"eslint-plugin-jest>@typescript-eslint/experimental-utils": true,
"@typescript-eslint/parser>@typescript-eslint/scope-manager": true,
"@typescript-eslint/eslint-plugin>tsutils": true,
"eslint>debug": true,
"eslint": true,
"semver": true,
"globby>ignore": true,
"eslint>regexpp": true
}
},
"eslint-module-utils": {
"eslint-plugin-import>eslint-module-utils": {
"packages": {
"@babel/eslint-parser": true,
"@typescript-eslint/parser": true,
"eslint-import-resolver-node": true,
"eslint-import-resolver-typescript": true
}
},
"module-deps": {
"packages": {
"loose-envify": true
"@babel/eslint-parser": true
}
},
"node-sass": {
"native": true
},
"browserify>module-deps": {
"packages": {
"loose-envify": true
}
},
"sass": {
"env": "unfrozen",
"builtin": {

File diff suppressed because it is too large Load Diff

View File

@ -114,22 +114,22 @@
"@keystonehq/metamask-airgapped-keyring": "0.2.1",
"@material-ui/core": "^4.11.0",
"@metamask/contract-metadata": "^1.31.0",
"@metamask/controllers": "^27.0.0",
"@metamask/controllers": "^28.0.0",
"@metamask/design-tokens": "^1.5.1",
"@metamask/eth-ledger-bridge-keyring": "^0.11.0",
"@metamask/eth-token-tracker": "^4.0.0",
"@metamask/etherscan-link": "^2.1.0",
"@metamask/iframe-execution-environment-service": "^0.10.7",
"@metamask/iframe-execution-environment-service": "^0.11.1",
"@metamask/jazzicon": "^2.0.0",
"@metamask/logo": "^3.1.1",
"@metamask/metamask-eth-abis": "^3.0.0",
"@metamask/obs-store": "^5.0.0",
"@metamask/post-message-stream": "^4.0.0",
"@metamask/providers": "^8.1.1",
"@metamask/rpc-methods": "^0.10.7",
"@metamask/rpc-methods": "^0.11.1",
"@metamask/slip44": "^2.0.0",
"@metamask/smart-transactions-controller": "^1.10.0",
"@metamask/snap-controllers": "^0.10.7",
"@metamask/snap-controllers": "^0.11.1",
"@ngraveio/bc-ur": "^1.1.6",
"@popperjs/core": "^2.4.0",
"@reduxjs/toolkit": "^1.6.2",
@ -174,10 +174,8 @@
"ethjs-query": "^0.3.4",
"extension-port-stream": "^2.0.0",
"fast-json-patch": "^2.2.1",
"fast-safe-stringify": "^2.0.7",
"fuse.js": "^3.2.0",
"globalthis": "^1.0.1",
"human-standard-collectible-abi": "^1.0.2",
"human-standard-token-abi": "^2.0.0",
"immer": "^9.0.6",
"json-rpc-engine": "^6.1.0",
@ -243,7 +241,7 @@
"@babel/preset-react": "^7.0.0",
"@babel/preset-typescript": "^7.16.7",
"@babel/register": "^7.5.5",
"@lavamoat/allow-scripts": "^2.0.0",
"@lavamoat/allow-scripts": "^2.0.3",
"@lavamoat/lavapack": "^2.0.4",
"@metamask/auto-changelog": "^2.1.0",
"@metamask/eslint-config": "^9.0.0",
@ -328,7 +326,7 @@
"jest-canvas-mock": "^2.3.1",
"jsdom": "^11.2.0",
"koa": "^2.7.0",
"lavamoat": "^5.3.5",
"lavamoat": "^6.1.2",
"lavamoat-browserify": "^14.1.0",
"lavamoat-viz": "^6.0.9",
"lockfile-lint": "^4.0.0",
@ -385,59 +383,62 @@
},
"lavamoat": {
"allowScripts": {
"3box>3box-orbitdb-plugins>ipfs-log>orbit-db-identity-provider>orbit-db-keystore>leveldown": false,
"3box>3box-orbitdb-plugins>ipfs-log>orbit-db-identity-provider>orbit-db-keystore>libp2p-crypto-secp256k1>secp256k1": false,
"3box>3box-orbitdb-plugins>ipfs-log>orbit-db-identity-provider>orbit-db-keystore>libp2p-crypto>libp2p-crypto-secp256k1>secp256k1": false,
"3box>3box-orbitdb-plugins>ipfs-log>orbit-db-identity-provider>orbit-db-keystore>libp2p-crypto>ursa-optional": false,
"@sentry/cli": true,
"chromedriver": true,
"geckodriver": true,
"react-devtools>electron": true,
"3box>ipfs-postmsg-proxy>peer-id>libp2p-crypto>libp2p-crypto-secp256k1>secp256k1": false,
"3box>ipfs>ipfs-mfs>ipfs-unixfs-exporter>ipfs-unixfs-importer>rabin-wasm>assemblyscript": false,
"3box>ipfs>ipfs-repo>datastore-level>leveldown": false,
"3box>ipfs>ipld-bitcoin>bitcoinjs-lib>bip32>tiny-secp256k1": false,
"3box>ipfs>ipfs-unixfs-importer>rabin-wasm>assemblyscript": false,
"3box>ipfs>ipld-ethereum>ethereumjs-account>ethereumjs-util>keccak": false,
"3box>ipfs>ipld-ethereum>ethereumjs-account>ethereumjs-util>secp256k1": false,
"3box>ipfs>ipld-ethereum>ethereumjs-block>ethereumjs-tx>ethereumjs-util>ethereum-cryptography>keccak": false,
"3box>ipfs>ipld-ethereum>ethereumjs-block>ethereumjs-tx>ethereumjs-util>ethereum-cryptography>secp256k1": false,
"3box>ipfs>ipld-ethereum>ethereumjs-block>ethereumjs-util>keccak": false,
"3box>ipfs>ipld-ethereum>ethereumjs-block>ethereumjs-util>secp256k1": false,
"3box>ipfs>ipld-ethereum>ethereumjs-block>merkle-patricia-tree>ethereumjs-util>keccak": false,
"3box>ipfs>ipld-ethereum>ethereumjs-block>merkle-patricia-tree>ethereumjs-util>secp256k1": false,
"3box>ipfs>ipld-ethereum>ethereumjs-tx>ethereumjs-util>keccak": false,
"3box>ipfs>ipld-ethereum>ethereumjs-tx>ethereumjs-util>secp256k1": false,
"3box>ipfs>ipld-ethereum>merkle-patricia-tree>ethereumjs-util>keccak": false,
"3box>ipfs>ipld-ethereum>merkle-patricia-tree>ethereumjs-util>secp256k1": false,
"3box>ipfs>libp2p-crypto>libp2p-crypto-secp256k1>secp256k1": false,
"3box>ipfs>libp2p-crypto>ursa-optional": false,
"3box>ipfs>prometheus-gc-stats>gc-stats": false,
"3box>orbit-db>orbit-db-cache>leveldown": false,
"@lavamoat/allow-scripts>@lavamoat/preinstall-always-fail": false,
"@metamask/controllers>babel-runtime>core-js": false,
"@metamask/controllers>eth-json-rpc-infura>eth-json-rpc-middleware>eth-sig-util>ethereumjs-util>keccak": false,
"@metamask/controllers>eth-json-rpc-infura>eth-json-rpc-middleware>eth-sig-util>ethereumjs-util>secp256k1": false,
"@metamask/controllers>eth-keyring-controller>eth-hd-keyring>eth-sig-util>ethereumjs-util>keccak": false,
"@metamask/controllers>eth-keyring-controller>eth-hd-keyring>eth-sig-util>ethereumjs-util>secp256k1": false,
"@metamask/controllers>web3-provider-engine>eth-json-rpc-filters>eth-json-rpc-middleware>eth-sig-util>ethereumjs-util>keccak": false,
"@metamask/controllers>web3-provider-engine>eth-json-rpc-filters>eth-json-rpc-middleware>eth-sig-util>ethereumjs-util>secp256k1": false,
"@metamask/controllers>web3-provider-engine>eth-json-rpc-middleware>eth-sig-util>ethereumjs-util>keccak": false,
"@metamask/controllers>web3-provider-engine>eth-json-rpc-middleware>eth-sig-util>ethereumjs-util>secp256k1": false,
"3box>orbit-db>orbit-db-keystore>leveldown": false,
"3box>orbit-db>orbit-db-keystore>libp2p-crypto-secp256k1>secp256k1": false,
"@eth-optimism/contracts>@ethersproject/hardware-wallets>@ledgerhq/hw-transport-node-hid>@ledgerhq/hw-transport-node-hid-noevents>node-hid": false,
"@eth-optimism/contracts>@ethersproject/hardware-wallets>@ledgerhq/hw-transport-node-hid>node-hid": false,
"@eth-optimism/contracts>@ethersproject/hardware-wallets>@ledgerhq/hw-transport-node-hid>usb": false,
"@metamask/controllers>web3-provider-engine>ethereumjs-util>keccak": false,
"@metamask/controllers>web3-provider-engine>ethereumjs-util>secp256k1": false,
"@metamask/controllers>web3-provider-engine>ethereumjs-vm>merkle-patricia-tree>ethereumjs-util>keccak": false,
"@metamask/controllers>web3-provider-engine>ethereumjs-vm>merkle-patricia-tree>ethereumjs-util>secp256k1": false,
"@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util>keccak": false,
"@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util>secp256k1": false,
"@sentry/cli": true,
"@storybook/addon-a11y>@storybook/addons>@storybook/api>@storybook/channels>core-js": false,
"@storybook/addon-essentials>@storybook/addon-docs>@storybook/builder-webpack4>@storybook/ui>core-js-pure": false,
"chromedriver": true,
"@metamask/eth-ledger-bridge-keyring>hdkey>secp256k1": false,
"@storybook/api>core-js": false,
"@storybook/core>@storybook/core-client>@storybook/ui>core-js-pure": false,
"eth-json-rpc-filters>eth-json-rpc-middleware>ethereumjs-util>keccak": false,
"eth-json-rpc-filters>eth-json-rpc-middleware>ethereumjs-util>secp256k1": false,
"eth-json-rpc-infura>eth-json-rpc-middleware>ethereumjs-util>keccak": false,
"eth-json-rpc-infura>eth-json-rpc-middleware>ethereumjs-util>secp256k1": false,
"eth-json-rpc-middleware>eth-sig-util>ethereumjs-util>keccak": false,
"eth-json-rpc-middleware>eth-sig-util>ethereumjs-util>secp256k1": false,
"eth-lattice-keyring>gridplus-sdk": false,
"eth-sig-util>ethereumjs-util>keccak": false,
"eth-sig-util>ethereumjs-util>secp256k1": false,
"eth-trezor-keyring>hdkey>secp256k1": false,
"eth-trezor-keyring>trezor-connect>@trezor/transport>protobufjs": false,
"eth-trezor-keyring>trezor-connect>@trezor/utxo-lib>blake-hash": false,
"eth-trezor-keyring>trezor-connect>trezor-link>protobufjs": false,
"eth-trezor-keyring>trezor-connect>@trezor/utxo-lib>tiny-secp256k1": false,
"ethereumjs-util>ethereum-cryptography>keccak": false,
"ethjs-query>babel-runtime>core-js": false,
"ganache>@trufflesuite/bigint-buffer": false,
"ganache>bufferutil": false,
"ganache>keccak": false,
"ganache>leveldown": false,
"geckodriver": true,
"react-devtools>electron": true,
"eth-trezor-keyring>trezor-connect>@trezor/transport>protobufjs": false,
"@metamask/iframe-execution-environment-service>@metamask/execution-environments": false,
"@metamask/snap-controllers>@metamask/execution-environments": false,
"@metamask/iframe-execution-environment-service>@metamask/snap-controllers>@metamask/execution-environments": false,
"@metamask/rpc-methods>@metamask/snap-controllers>@metamask/execution-environments": false
"ganache>secp256k1": false,
"ganache>utf-8-validate": false,
"gulp-watch>chokidar>fsevents": false,
"gulp>glob-watcher>chokidar>fsevents": false,
"webpack>watchpack>watchpack-chokidar2>chokidar>fsevents": false
}
}
}

View File

@ -0,0 +1,13 @@
diff --git a/node_modules/zxcvbn/lib/matching.js b/node_modules/zxcvbn/lib/matching.js
index 3940bad..748da8b 100644
--- a/node_modules/zxcvbn/lib/matching.js
+++ b/node_modules/zxcvbn/lib/matching.js
@@ -13,7 +13,7 @@ build_ranked_dict = function(ordered_list) {
i = 1;
for (o = 0, len1 = ordered_list.length; o < len1; o++) {
word = ordered_list[o];
- result[word] = i;
+ Reflect.defineProperty(result, word, { value: i, configurable: true, enumerable: true, writable: true });
i += 1;
}
return result;

View File

@ -70,3 +70,25 @@ export const POLLING_TOKEN_ENVIRONMENT_TYPES = {
[ENVIRONMENT_TYPE_NOTIFICATION]: 'notificationGasPollTokens',
[ENVIRONMENT_TYPE_FULLSCREEN]: 'fullScreenGasPollTokens',
};
export const ORIGIN_METAMASK = 'metamask';
export const METAMASK_BETA_CHROME_ID = 'pbbkamfgmaedccnfkmjcofcecjhfgldn';
export const METAMASK_PROD_CHROME_ID = 'nkbihfbeogaeaoehlefnkodbefgpgknn';
export const METAMASK_FLASK_CHROME_ID = 'ljfoeinjpaedjfecbmggjgodbgkmjkjk';
export const CHROME_BUILD_IDS = [
METAMASK_BETA_CHROME_ID,
METAMASK_PROD_CHROME_ID,
METAMASK_FLASK_CHROME_ID,
];
const METAMASK_BETA_FIREFOX_ID = 'webextension-beta@metamask.io';
const METAMASK_PROD_FIREFOX_ID = 'webextension@metamask.io';
const METAMASK_FLASK_FIREFOX_ID = 'webextension-flask@metamask.io';
export const FIREFOX_BUILD_IDS = [
METAMASK_BETA_FIREFOX_ID,
METAMASK_PROD_FIREFOX_ID,
METAMASK_FLASK_FIREFOX_ID,
];

View File

@ -170,6 +170,7 @@
* change, we identify the new number_of_accounts trait
* @property {'number_of_nft_collections'} NUMBER_OF_NFT_COLLECTIONS - user
* trait for number of unique NFT addresses
* @property {'number_of_nfts'} NUMBER_OF_NFTS - user trait for number of all NFT addresses
* @property {'number_of_tokens'} NUMBER_OF_TOKENS - when the number of tokens change, we
* identify the new number_of_tokens trait
* @property {'opensea_api_enabled'} OPENSEA_API_ENABLED - when the OpenSea API is enabled
@ -191,6 +192,7 @@ export const TRAITS = {
NFT_AUTODETECTION_ENABLED: 'nft_autodetection_enabled',
NUMBER_OF_ACCOUNTS: 'number_of_accounts',
NUMBER_OF_NFT_COLLECTIONS: 'number_of_nft_collections',
NUMBER_OF_NFTS: 'number_of_nfts',
NUMBER_OF_TOKENS: 'number_of_tokens',
OPENSEA_API_ENABLED: 'opensea_api_enabled',
THREE_BOX_ENABLED: 'three_box_enabled',
@ -211,6 +213,8 @@ export const TRAITS = {
* of identities(accounts) added to the user's MetaMask.
* @property {number} [number_of_nft_collections] - A number representing the
* amount of different NFT collections the user possesses an NFT from.
* @property {number} [number_of_nfts] - A number representing the
* amount of all NFTs the user possesses across all networks and accounts.
* @property {number} [number_of_tokens] - The total number of token contracts
* the user has across all networks and accounts.
* @property {boolean} [opensea_api_enabled] - does the user have the OpenSea
@ -256,9 +260,32 @@ export const REJECT_NOTFICIATION_CLOSE = 'Cancel Via Notification Close';
export const REJECT_NOTFICIATION_CLOSE_SIG =
'Cancel Sig Request Via Notification Close';
/**
* EVENTS
*/
export const EVENT_NAMES = {
SIGNATURE_REQUESTED: 'Signature Requested',
ENCRYPTION_PUBLIC_KEY_REQUESTED: 'Encryption Public Key Requested',
DECRYPTION_REQUESTED: 'Decryption Requested',
PERMISSIONS_REQUESTED: 'Permissions Requested',
};
export const EVENT = {
CATEGORIES: {
ACCOUNTS: 'Accounts',
AUTH: 'Auth',
BACKGROUND: 'Background',
INPAGE_PROVIDER: 'inpage_provider',
MESSAGES: 'Messages',
NAVIGATION: 'Navigation',
NETWORK: 'Network',
ONBOARDING: 'Onboarding',
RETENTION: 'Retention',
SETTINGS: 'Settings',
SNAPS: 'Snaps',
SWAPS: 'Swaps',
TRANSACTIONS: 'Transactions',
WALLET: 'Wallet',
},
};

View File

@ -1,3 +1,5 @@
import { capitalize } from 'lodash';
export const ROPSTEN = 'ropsten';
export const RINKEBY = 'rinkeby';
export const KOVAN = 'kovan';
@ -79,16 +81,45 @@ export const TEST_CHAINS = [
LOCALHOST_CHAIN_ID,
];
export const TEST_NETWORK_TICKER_MAP = {
[ROPSTEN]: `${capitalize(ROPSTEN)}${ETH_SYMBOL}`,
[RINKEBY]: `${capitalize(RINKEBY)}${ETH_SYMBOL}`,
[KOVAN]: `${capitalize(KOVAN)}${ETH_SYMBOL}`,
[GOERLI]: `${capitalize(GOERLI)}${ETH_SYMBOL}`,
};
/**
* Map of all build-in Infura networks to their network and chain IDs.
* Map of all build-in Infura networks to their network, ticker and chain IDs.
*/
export const NETWORK_TYPE_TO_ID_MAP = {
[ROPSTEN]: { networkId: ROPSTEN_NETWORK_ID, chainId: ROPSTEN_CHAIN_ID },
[RINKEBY]: { networkId: RINKEBY_NETWORK_ID, chainId: RINKEBY_CHAIN_ID },
[KOVAN]: { networkId: KOVAN_NETWORK_ID, chainId: KOVAN_CHAIN_ID },
[GOERLI]: { networkId: GOERLI_NETWORK_ID, chainId: GOERLI_CHAIN_ID },
[MAINNET]: { networkId: MAINNET_NETWORK_ID, chainId: MAINNET_CHAIN_ID },
[LOCALHOST]: { networkId: LOCALHOST_NETWORK_ID, chainId: LOCALHOST_CHAIN_ID },
[ROPSTEN]: {
networkId: ROPSTEN_NETWORK_ID,
chainId: ROPSTEN_CHAIN_ID,
ticker: TEST_NETWORK_TICKER_MAP[ROPSTEN],
},
[RINKEBY]: {
networkId: RINKEBY_NETWORK_ID,
chainId: RINKEBY_CHAIN_ID,
ticker: TEST_NETWORK_TICKER_MAP[RINKEBY],
},
[KOVAN]: {
networkId: KOVAN_NETWORK_ID,
chainId: KOVAN_CHAIN_ID,
ticker: TEST_NETWORK_TICKER_MAP[KOVAN],
},
[GOERLI]: {
networkId: GOERLI_NETWORK_ID,
chainId: GOERLI_CHAIN_ID,
ticker: TEST_NETWORK_TICKER_MAP[GOERLI],
},
[MAINNET]: {
networkId: MAINNET_NETWORK_ID,
chainId: MAINNET_CHAIN_ID,
},
[LOCALHOST]: {
networkId: LOCALHOST_NETWORK_ID,
chainId: LOCALHOST_CHAIN_ID,
},
};
export const NETWORK_TO_NAME_MAP = {
@ -135,6 +166,7 @@ export const CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP = {
[AVALANCHE_CHAIN_ID]: AVAX_TOKEN_IMAGE_URL,
[BSC_CHAIN_ID]: BNB_TOKEN_IMAGE_URL,
[POLYGON_CHAIN_ID]: MATIC_TOKEN_IMAGE_URL,
[ROPSTEN_CHAIN_ID]: TEST_ETH_TOKEN_IMAGE_URL,
};
export const CHAIN_ID_TO_NETWORK_ID_MAP = Object.values(
@ -207,19 +239,19 @@ export const BUYABLE_CHAINS_MAP = {
},
},
[ROPSTEN_CHAIN_ID]: {
nativeCurrency: ETH_SYMBOL,
nativeCurrency: TEST_NETWORK_TICKER_MAP[ROPSTEN],
network: BUYABLE_CHAIN_ETHEREUM_NETWORK_NAME,
},
[RINKEBY_CHAIN_ID]: {
nativeCurrency: ETH_SYMBOL,
nativeCurrency: TEST_NETWORK_TICKER_MAP[RINKEBY],
network: BUYABLE_CHAIN_ETHEREUM_NETWORK_NAME,
},
[GOERLI_CHAIN_ID]: {
nativeCurrency: ETH_SYMBOL,
nativeCurrency: TEST_NETWORK_TICKER_MAP[GOERLI],
network: BUYABLE_CHAIN_ETHEREUM_NETWORK_NAME,
},
[KOVAN_CHAIN_ID]: {
nativeCurrency: ETH_SYMBOL,
nativeCurrency: TEST_NETWORK_TICKER_MAP[KOVAN],
network: BUYABLE_CHAIN_ETHEREUM_NETWORK_NAME,
},
[BSC_CHAIN_ID]: {

View File

@ -19,7 +19,7 @@
"type": "0x2"
},
"origin": "metamask",
"type": "sentEther",
"type": "simpleSend",
"history": [
{
"id": 6854191329910881,
@ -39,7 +39,7 @@
"type": "0x2"
},
"origin": "metamask",
"type": "sentEther"
"type": "simpleSend"
},
[
{
@ -95,7 +95,7 @@
"type": "0x2"
},
"origin": "metamask",
"type": "sentEther",
"type": "simpleSend",
"history": [
{
"id": 6854191329910881,
@ -115,7 +115,7 @@
"type": "0x2"
},
"origin": "metamask",
"type": "sentEther"
"type": "simpleSend"
},
[
{
@ -172,7 +172,7 @@
"type": "0x2"
},
"origin": "metamask",
"type": "sentEther",
"type": "simpleSend",
"history": [
{
"id": 6854191329910881,
@ -192,7 +192,7 @@
"type": "0x2"
},
"origin": "metamask",
"type": "sentEther"
"type": "simpleSend"
},
[
{

View File

@ -386,6 +386,35 @@
}
}
},
"PermissionsController": {
"permissionsRequests": [],
"permissionsDescriptions": {},
"domains": {
"http://127.0.0.1:8080": {
"permissions": [
{
"@context": ["https://github.com/MetaMask/rpc-cap"],
"parentCapability": "eth_accounts",
"id": "f55a1c15-ea48-4088-968e-63be474d42fa",
"date": 1594348332268,
"invoker": "http://127.0.0.1:8080",
"caveats": [
{
"type": "limitResponseLength",
"value": 1,
"name": "primaryAccountOnly"
},
{
"type": "filterResponse",
"value": ["0x5cfe73b6021e818b776b421b1c4db2474086a7e1"],
"name": "exposedAccounts"
}
]
}
]
}
}
},
"config": {},
"firstTimeInfo": {
"date": 1575697234195,

View File

@ -555,11 +555,6 @@ describe('MetaMask', function () {
});
it('displays the token approval data', async function () {
await driver.clickElement(
'.confirm-approve-content__view-full-tx-button',
);
await driver.delay(regularDelayMs);
const functionType = await driver.findElement(
'.confirm-approve-content__data .confirm-approve-content__small-text',
);
@ -750,11 +745,6 @@ describe('MetaMask', function () {
});
it('shows the correct recipient', async function () {
await driver.clickElement(
'.confirm-approve-content__view-full-tx-button',
);
await driver.delay(regularDelayMs);
const permissionInfo = await driver.findElements(
'.confirm-approve-content__medium-text',
);

View File

@ -69,15 +69,19 @@ async function main() {
throw error;
}
let testTimeoutInMilliseconds = 60 * 1000;
if (leaveRunning) {
process.env.E2E_LEAVE_RUNNING = 'true';
testTimeoutInMilliseconds = 0;
}
await retry({ retries }, async () => {
await runInShell('yarn', [
'mocha',
'--no-config',
'--no-timeouts',
'--timeout',
testTimeoutInMilliseconds,
e2eTestPath,
]);
});

View File

@ -0,0 +1,94 @@
const { strict: assert } = require('assert');
const { withFixtures } = require('../helpers');
describe('Test Snap bip-44', function () {
it('can pop up bip-44 snap and get private key result', async function () {
const ganacheOptions = {
accounts: [
{
secretKey:
'0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC',
balance: 25000000000000000000,
},
],
};
await withFixtures(
{
fixtures: 'imported-account',
ganacheOptions,
title: this.test.title,
driverOptions: {
type: 'flask',
},
},
async ({ driver }) => {
await driver.navigate();
// enter pw into extension
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);
// navigate to test snaps page and connect
await driver.driver.get('https://metamask.github.io/test-snaps/0.1.3/');
await driver.fill('.snapId3', 'npm:@metamask/test-snap-bip44');
await driver.clickElement({
text: 'Connect BIP-44 Snap',
tag: 'button',
});
// switch to metamask extension and click connect
await driver.waitUntilXWindowHandles(2, 5000, 10000);
let windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle(
'MetaMask Notification',
windowHandles,
);
await driver.clickElement(
{
text: 'Connect',
tag: 'button',
},
10000,
);
await driver.delay(2000);
// approve install of snap
await driver.waitUntilXWindowHandles(2, 5000, 10000);
windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle(
'MetaMask Notification',
windowHandles,
);
await driver.clickElement({
text: 'Approve & Install',
tag: 'button',
});
// deal with permissions popover
await driver.delay(1000);
await driver.press('#warning-accept', driver.Key.SPACE);
await driver.clickElement({
text: 'Confirm',
tag: 'button',
});
// click send inputs on test snap page
await driver.waitUntilXWindowHandles(1, 5000, 10000);
windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle('Test Snaps', windowHandles);
await driver.clickElement({
text: 'Send Test to BIP-44 Snap',
tag: 'button',
});
// check the results of the public key test
await driver.delay(2000);
const bip44Result = await driver.findElement('.bip44Result');
assert.equal(
await bip44Result.getText(),
'Public key: "0x86debb44fb3a984d93f326131d4c1db0bc39644f1a67b673b3ab45941a1cea6a385981755185ac4594b6521e4d1e8d1"',
);
},
);
});
});

View File

@ -1,9 +1,5 @@
const { strict: assert } = require('assert');
const {
convertToHexValue,
withFixtures,
regularDelayMs,
} = require('../helpers');
const { convertToHexValue, withFixtures } = require('../helpers');
describe('Deploy contract and call contract methods', function () {
const ganacheOptions = {
@ -19,7 +15,7 @@ describe('Deploy contract and call contract methods', function () {
await withFixtures(
{
dapp: true,
fixtures: 'imported-account',
fixtures: 'connected-state',
ganacheOptions,
title: this.test.title,
},
@ -28,32 +24,17 @@ describe('Deploy contract and call contract methods', function () {
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);
// connects the dapp
// deploy contract
await driver.openNewPage('http://127.0.0.1:8080/');
await driver.clickElement({ text: 'Connect', tag: 'button' });
await driver.clickElement('#deployButton');
await driver.waitUntilXWindowHandles(3);
await driver.delay(5000);
let windowHandles = await driver.getAllWindowHandles();
const extension = windowHandles[0];
const dapp = await driver.switchToWindowWithTitle(
'E2E Test Dapp',
windowHandles,
);
const popup = windowHandles.find(
(handle) => handle !== extension && handle !== dapp,
);
await driver.switchToWindow(popup);
await driver.clickElement({ text: 'Next', tag: 'button' });
await driver.clickElement({ text: 'Connect', tag: 'button' });
await driver.waitUntilXWindowHandles(2);
// creates a deploy contract transaction
await driver.switchToWindow(dapp);
await driver.clickElement('#deployButton');
// displays the contract creation data
await driver.waitUntilXWindowHandles(3);
windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle(
'MetaMask Notification',
windowHandles,
@ -87,13 +68,15 @@ describe('Deploy contract and call contract methods', function () {
await driver.switchToWindow(dapp);
await driver.clickElement('#depositButton');
await driver.waitUntilXWindowHandles(3);
await driver.delay(5000);
windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle(
'MetaMask Notification',
windowHandles,
);
await driver.delay(regularDelayMs);
await driver.waitForSelector({
css: '.confirm-page-container-summary__action__name',
text: 'Deposit',
});
await driver.clickElement({ text: 'Confirm', tag: 'button' });
await driver.waitUntilXWindowHandles(2);
await driver.switchToWindow(extension);
@ -113,13 +96,15 @@ describe('Deploy contract and call contract methods', function () {
await driver.switchToWindow(dapp);
await driver.clickElement('#withdrawButton');
await driver.waitUntilXWindowHandles(3);
await driver.delay(5000);
windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle(
'MetaMask Notification',
windowHandles,
);
await driver.delay(regularDelayMs);
await driver.waitForSelector({
css: '.confirm-page-container-summary__action__name',
text: 'Withdraw',
});
await driver.clickElement({ text: 'Confirm', tag: 'button' });
await driver.waitUntilXWindowHandles(2);
await driver.switchToWindow(extension);

View File

@ -16,7 +16,7 @@ describe('Failing contract interaction ', function () {
await withFixtures(
{
dapp: true,
fixtures: 'imported-account',
fixtures: 'connected-state',
ganacheOptions,
title: this.test.title,
},
@ -25,9 +25,9 @@ describe('Failing contract interaction ', function () {
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);
// connects the dapp
// deploy contract
await driver.openNewPage('http://127.0.0.1:8080/');
await driver.clickElement({ text: 'Connect', tag: 'button' });
await driver.clickElement('#deployFailingButton');
await driver.waitUntilXWindowHandles(3);
let windowHandles = await driver.getAllWindowHandles();
const extension = windowHandles[0];
@ -35,19 +35,6 @@ describe('Failing contract interaction ', function () {
'E2E Test Dapp',
windowHandles,
);
const popup = windowHandles.find(
(handle) => handle !== extension && handle !== dapp,
);
await driver.switchToWindow(popup);
await driver.clickElement({ text: 'Next', tag: 'button' });
await driver.clickElement({ text: 'Connect', tag: 'button' });
await driver.waitUntilXWindowHandles(2);
// deploy contract
await driver.switchToWindow(dapp);
await driver.clickElement('#deployFailingButton');
await driver.waitUntilXWindowHandles(3);
windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle(
'MetaMask Notification',
windowHandles,

View File

@ -123,26 +123,12 @@ describe('Navigate transactions', function () {
'second transaction in focus',
);
// connects the dapp
// add transaction
await driver.openNewPage('http://127.0.0.1:8080/');
await driver.clickElement({ text: 'Connect', tag: 'button' });
await driver.clickElement({ text: 'Send', tag: 'button' });
await driver.waitUntilXWindowHandles(3);
const windowHandles = await driver.getAllWindowHandles();
const extension = windowHandles[0];
const dapp = await driver.switchToWindowWithTitle(
'E2E Test Dapp',
windowHandles,
);
const popup = windowHandles.find(
(handle) => handle !== extension && handle !== dapp,
);
await driver.switchToWindow(popup);
await driver.clickElement({ text: 'Next', tag: 'button' });
await driver.clickElement({ text: 'Connect', tag: 'button' });
// add transaction
await driver.switchToWindow(dapp);
await driver.clickElement({ text: 'Send', tag: 'button' });
await driver.switchToWindow(extension);
navigationElement = await driver.waitForSelector(
{

View File

@ -41,7 +41,6 @@ describe('Phishing Detection', function () {
await driver.navigate();
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);
await driver.navigate();
await driver.openNewPage('http://example.com');
await driver.waitForSelector({ text: 'continuing at your own risk' });
const header = await driver.findElement('h1');

View File

@ -1,9 +1,5 @@
const { strict: assert } = require('assert');
const {
convertToHexValue,
withFixtures,
regularDelayMs,
} = require('../helpers');
const { convertToHexValue, withFixtures } = require('../helpers');
describe('Send ETH from inside MetaMask using default gas', function () {
const ganacheOptions = {
@ -47,7 +43,12 @@ describe('Send ETH from inside MetaMask using default gas', function () {
await inputAmount.press(driver.Key.BACK_SPACE);
await inputAmount.press(driver.Key.BACK_SPACE);
await inputAmount.press(driver.Key.BACK_SPACE);
await driver.delay(regularDelayMs);
await driver.wait(async () => {
const sendDialogMsgs = await driver.findElements(
'.send-v2__form div.dialog',
);
return sendDialogMsgs.length === 1;
}, 10000);
await driver.assertElementNotPresent('.send-v2__error-amount');
@ -158,10 +159,6 @@ describe('Send ETH from inside MetaMask using advanced gas modal', function () {
});
describe('Send ETH from dapp using advanced gas controls', function () {
let windowHandles;
let extension;
let popup;
let dapp;
const ganacheOptions = {
accounts: [
{
@ -176,7 +173,7 @@ describe('Send ETH from dapp using advanced gas controls', function () {
await withFixtures(
{
dapp: true,
fixtures: 'imported-account',
fixtures: 'connected-state',
ganacheOptions,
title: this.test.title,
},
@ -200,52 +197,46 @@ describe('Send ETH from dapp using advanced gas controls', function () {
await driver.clickElement(
'[data-testid="advanced-setting-advanced-gas-inline"] .settings-page__content-item-col > label > div',
);
windowHandles = await driver.getAllWindowHandles();
extension = windowHandles[0];
await driver.closeAllWindowHandlesExcept([extension]);
await driver.clickElement('.app-header__logo-container');
// connects the dapp
await driver.openNewPage('http://127.0.0.1:8080/');
await driver.clickElement({ text: 'Connect', tag: 'button' });
await driver.waitUntilXWindowHandles(3);
windowHandles = await driver.getAllWindowHandles();
extension = windowHandles[0];
dapp = await driver.switchToWindowWithTitle(
'E2E Test Dapp',
windowHandles,
);
popup = windowHandles.find(
(handle) => handle !== extension && handle !== dapp,
);
await driver.switchToWindow(popup);
await driver.clickElement({ text: 'Next', tag: 'button' });
await driver.clickElement({ text: 'Connect', tag: 'button' });
await driver.waitUntilXWindowHandles(2);
await driver.switchToWindow(dapp);
// initiates a send from the dapp
await driver.openNewPage('http://127.0.0.1:8080/');
await driver.clickElement({ text: 'Send', tag: 'button' });
await driver.delay(2000);
windowHandles = await driver.getAllWindowHandles();
await driver.waitUntilXWindowHandles(3);
const windowHandles = await driver.getAllWindowHandles();
const extension = windowHandles[0];
await driver.switchToWindowWithTitle(
'MetaMask Notification',
windowHandles,
);
await driver.assertElementNotPresent({ text: 'Data', tag: 'li' });
await driver.clickElement({ text: 'Edit', tag: 'button' });
await driver.delay(1000);
await driver.waitForSelector({
css: '.transaction-total-banner',
text: '0.00021 ETH',
});
await driver.clickElement(
{ text: 'Edit suggested gas fee', tag: 'button' },
10000,
);
await driver.delay(1000);
await driver.waitForSelector({
css: '.transaction-total-banner',
text: '0.00021 ETH',
});
const inputs = await driver.findElements('input[type="number"]');
const gasPriceInput = inputs[1];
await gasPriceInput.press(driver.Key.BACK_SPACE);
await gasPriceInput.press(driver.Key.BACK_SPACE);
await gasPriceInput.fill('100');
await driver.delay(1000);
await driver.waitForSelector({
css: '.transaction-total-banner',
text: '0.0021 ETH',
});
await driver.clickElement({ text: 'Save', tag: 'button' });
await driver.delay(1000);
await driver.waitForSelector({
css: '.transaction-detail-item:nth-of-type(1) h6:nth-of-type(2)',
text: '0.0021 ETH',
});
await driver.clickElement({ text: 'Confirm', tag: 'button' });
await driver.waitUntilXWindowHandles(2);
await driver.switchToWindow(extension);

View File

@ -53,6 +53,7 @@ class Driver {
this.Key = {
BACK_SPACE: '\uE003',
ENTER: '\uE007',
SPACE: '\uE00D',
};
}

View File

@ -70,6 +70,10 @@ Object.assign(window, { fetch, Headers, Request, Response });
window.localStorage = {
removeItem: () => null,
};
// used for native dark/light mode detection
window.matchMedia = () => true;
// override @metamask/logo
window.requestAnimationFrame = () => undefined;

View File

@ -244,6 +244,7 @@ export const createSwapsMockStore = () => {
fetchTime: 1354,
aggregator: 'TEST_AGG_2',
aggType: 'AGG',
isBestQuote: true,
slippage: 2,
sourceTokenInfo: {
address: '0x6b175474e89094c44da98b954eedeac495271d0f',

View File

@ -5,6 +5,7 @@ import Fuse from 'fuse.js';
import InputAdornment from '@material-ui/core/InputAdornment';
import classnames from 'classnames';
import { ENVIRONMENT_TYPE_POPUP } from '../../../../shared/constants/app';
import { EVENT } from '../../../../shared/constants/metametrics';
import { getEnvironmentType } from '../../../../app/scripts/lib/util';
import Identicon from '../../ui/identicon';
import SiteIcon from '../../ui/site-icon';
@ -197,7 +198,7 @@ export default class AccountMenu extends Component {
className="account-menu__account account-menu__item--clickable"
onClick={() => {
this.context.trackEvent({
category: 'Navigation',
category: EVENT.CATEGORIES.NAVIGATION,
event: 'Switched Account',
properties: {
action: 'Main Menu',
@ -340,7 +341,7 @@ export default class AccountMenu extends Component {
onClick={() => {
toggleAccountMenu();
trackEvent({
category: 'Navigation',
category: EVENT.CATEGORIES.NAVIGATION,
event: 'Clicked Create Account',
properties: {
action: 'Main Menu',
@ -356,7 +357,7 @@ export default class AccountMenu extends Component {
onClick={() => {
toggleAccountMenu();
trackEvent({
category: 'Navigation',
category: EVENT.CATEGORIES.NAVIGATION,
event: 'Clicked Import Account',
properties: {
action: 'Main Menu',
@ -377,7 +378,7 @@ export default class AccountMenu extends Component {
onClick={() => {
toggleAccountMenu();
trackEvent({
category: 'Navigation',
category: EVENT.CATEGORIES.NAVIGATION,
event: 'Clicked Connect Hardware',
properties: {
action: 'Main Menu',
@ -417,7 +418,7 @@ export default class AccountMenu extends Component {
toggleAccountMenu();
history.push(SETTINGS_ROUTE);
this.context.trackEvent({
category: 'Navigation',
category: EVENT.CATEGORIES.NAVIGATION,
event: 'Opened Settings',
properties: {
action: 'Main Menu',

View File

@ -1,7 +1,7 @@
import React, { useContext } from 'react';
import { useSelector } from 'react-redux';
import PropTypes from 'prop-types';
import { I18nContext } from '../../../contexts/i18n';
import ActionableMessage from '../../ui/actionable-message';
import Box from '../../ui/box';
import Typography from '../../ui/typography';
import {
@ -10,10 +10,17 @@ import {
COLORS,
DISPLAY,
FLEX_DIRECTION,
FONT_WEIGHT,
TYPOGRAPHY,
JUSTIFY_CONTENT,
} from '../../../helpers/constants/design-system';
import Button from '../../ui/button';
import IconCaretLeft from '../../ui/icon/icon-caret-left';
import Tooltip from '../../ui/tooltip';
import IconWithFallback from '../../ui/icon-with-fallback';
import IconBorder from '../../ui/icon-border';
import { getTheme } from '../../../selectors';
import { THEME_TYPE } from '../../../pages/settings/experimental-tab/experimental-tab.constant';
const AddNetwork = ({
onBackClick,
@ -22,10 +29,13 @@ const AddNetwork = ({
featuredRPCS,
}) => {
const t = useContext(I18nContext);
const theme = useSelector(getTheme);
const infuraRegex = /infura.io/u;
const nets = featuredRPCS
.sort((a, b) => (a.ticker > b.ticker ? 1 : -1))
.slice(0, 5);
.slice(0, 8);
return (
<Box>
@ -63,28 +73,73 @@ const AddNetwork = ({
color={COLORS.TEXT_MUTED}
margin={[4, 0, 3, 0]}
>
{t('customNetworks')}
{t('popularCustomNetworks')}
</Typography>
{nets.map((item, index) => (
<Box
key={index}
display={DISPLAY.FLEX}
alignItems={ALIGN_ITEMS.CENTER}
justifyContent={JUSTIFY_CONTENT.SPACE_BETWEEN}
marginBottom={6}
>
<img
className="add-network__token-image"
src={item?.rpcPrefs?.imageUrl}
alt={t('logo', [item.ticker])}
/>
<Typography variant={TYPOGRAPHY.H7} color={COLORS.TEXT_DEFAULT}>
{item.ticker}
</Typography>
<i
className="fa fa-plus add-network__add-icon"
onClick={onAddNetworkClick}
title={`${t('add')} ${item.ticker}`}
/>
<Box display={DISPLAY.FLEX} alignItems={ALIGN_ITEMS.CENTER}>
<IconBorder size={24}>
<IconWithFallback
icon={item.rpcPrefs.imageUrl}
name={item.nickname}
size={24}
/>
</IconBorder>
<Typography
variant={TYPOGRAPHY.H7}
color={COLORS.TEXT_DEFAULT}
fontWeight={FONT_WEIGHT.BOLD}
boxProps={{ marginLeft: 2 }}
>
{item.nickname}
</Typography>
</Box>
<Box display={DISPLAY.FLEX} alignItems={ALIGN_ITEMS.CENTER}>
{
// Warning for the networks that doesn't use infura.io as the RPC
!infuraRegex.test(item.rpcUrl) && (
<Tooltip
className="add-network__warning-tooltip"
position="top"
interactive
html={
<Box margin={3} className="add-network__warning-tooltip">
{t('addNetworkTooltipWarning', [
<a
key="zendesk_page_link"
href="https://metamask.zendesk.com/hc/en-us/articles/4417500466971"
rel="noreferrer"
target="_blank"
>
{t('learnMoreUpperCase')}
</a>,
])}
</Box>
}
trigger="mouseenter"
theme={theme === THEME_TYPE.DEFAULT ? 'light' : 'dark'}
>
<i
className="fa fa-exclamation-triangle add-network__warning-icon"
title={t('warning')}
/>
</Tooltip>
)
}
<Button
type="inline"
className="add-network__add-button"
onClick={onAddNetworkClick}
>
{t('add')}
</Button>
</Box>
</Box>
))}
</Box>
@ -98,25 +153,6 @@ const AddNetwork = ({
{t('addANetworkManually')}
</Typography>
</Button>
<ActionableMessage
type="warning"
message={
<>
{t('onlyInteractWith')}
<a
href="https://metamask.zendesk.com/hc/en-us/articles/4417500466971"
target="_blank"
className="add-network__footer__link"
rel="noreferrer"
>
{t('endOfFlowMessage9')}
</a>
</>
}
iconFillColor="var(--color-warning-default)"
useIcon
withRightButton
/>
</Box>
</Box>
);

View File

@ -1,9 +1,12 @@
import React from 'react';
import AddNetwork from '.';
import AddNetwork from './add-network';
const MATIC_TOKEN_IMAGE_URL = './images/matic-token.png';
const ARBITRUM_IMAGE_URL = './images/arbitrum.svg';
const OPTIMISM_IMAGE_URL = './images/optimism.svg';
const AVALANCHE_IMAGE_URL = './images/avax-token.png';
const PALM_IMAGE_URL = './images/palm.svg';
const BSC_IMAGE_URL = './images/bsc-filled.svg';
export default {
title: 'Components/APP/AddNetwork',
@ -25,36 +28,83 @@ export default {
args: {
featuredRPCS: [
{
chainId: '0x89',
nickname: 'Polygon Mumbai',
rpcUrl:
'https://polygon-mainnet.infura.io/v3/2b6d4a83d89a438eb1b5d036788ab29c',
ticker: 'MATIC',
chainId: '42161',
nickname: 'Arbitrum One',
rpcUrl: 'https://arbitrum-mainnet.infura.io/v3/{INFURA_API_KEY}',
ticker: 'AETH',
rpcPrefs: {
blockExplorerUrl: 'https://mumbai.polygonscan.com/',
imageUrl: MATIC_TOKEN_IMAGE_URL,
blockExplorerUrl: 'https://explorer.arbitrum.io',
imageUrl: ARBITRUM_IMAGE_URL,
},
},
{
chainId: '0x99',
nickname: 'Optimism Testnet ',
rpcUrl:
'https://optimism-kovan.infura.io/v3/2b6d4a83d89a438eb1b5d036788ab29c',
chainId: '43114',
nickname: 'Avalanche Mainnet C-Chain',
rpcUrl: 'https://api.avax.network/ext/bc/C/rpc',
ticker: 'AVAX',
rpcPrefs: {
blockExplorerUrl: 'https://snowtrace.io/',
imageUrl: AVALANCHE_IMAGE_URL,
},
},
{
chainId: '56',
nickname: 'BNB Smart Chain',
rpcUrl: 'https://bsc-dataseed.binance.org/',
ticker: 'BNB',
rpcPrefs: {
blockExplorerUrl: 'https://bscscan.com/',
imageUrl: BSC_IMAGE_URL,
},
},
{
chainId: '250',
nickname: 'Fantom Opera',
rpcUrl: 'https://rpc.ftm.tools/',
ticker: 'FTM',
rpcPrefs: {
blockExplorerUrl: 'https://ftmscan.com/',
imageUrl: '',
},
},
{
chainId: '1666600000',
nickname: 'Harmony Mainnet Shard 0',
rpcUrl: 'https://api.harmony.one/',
ticker: 'ONE',
rpcPrefs: {
blockExplorerUrl: 'https://explorer.harmony.one/',
imageUrl: '',
},
},
{
chainId: '10',
nickname: 'Optimism',
rpcUrl: 'https://optimism-mainnet.infura.io/v3/{INFURA_API_KEY}',
ticker: 'KOR',
rpcPrefs: {
blockExplorerUrl: 'https://kovan-optimistic.etherscan.io/',
blockExplorerUrl: 'https://optimistic.etherscan.io/',
imageUrl: OPTIMISM_IMAGE_URL,
},
},
{
chainId: '0x66eeb',
nickname: 'Arbitrum Testnet',
rpcUrl:
'https://arbitrum-rinkeby.infura.io/v3/2b6d4a83d89a438eb1b5d036788ab29c',
ticker: 'ARETH',
chainId: '137',
nickname: 'Polygon Mainnet',
rpcUrl: 'https://polygon-mainnet.infura.io/v3/{INFURA_API_KEY}',
ticker: 'MATIC',
rpcPrefs: {
blockExplorerUrl: 'https://testnet.arbiscan.io/',
imageUrl: ARBITRUM_IMAGE_URL,
blockExplorerUrl: 'https://polygonscan.com/',
imageUrl: MATIC_TOKEN_IMAGE_URL,
},
},
{
chainId: '11297108109',
nickname: 'Palm',
rpcUrl: 'https://palm-mainnet.infura.io/v3/{INFURA_API_KEY}',
ticker: 'PALM',
rpcPrefs: {
blockExplorerUrl: 'https://explorer.palm.io/',
imageUrl: PALM_IMAGE_URL,
},
},
],

View File

@ -8,10 +8,17 @@
}
}
&__token-image {
margin-right: 7px;
height: 24px;
width: 24px;
&__warning-icon {
color: var(--color-icon-muted);
}
&__warning-tooltip {
color: var(--color-text-alternative);
width: 180px;
a {
color: var(--color-primary-default);
}
}
&__add-icon {
@ -21,6 +28,12 @@
cursor: pointer;
}
&__add-button.button {
color: var(--color-primary-default);
font-size: $font-size-h7;
margin-left: 24px;
}
&__footer {
border-top: 1px solid var(--color-border-muted);
@ -39,3 +52,5 @@
}
}
}

View File

@ -4,6 +4,7 @@ import classnames from 'classnames';
import Identicon from '../../ui/identicon';
import MetaFoxLogo from '../../ui/metafox-logo';
import { DEFAULT_ROUTE } from '../../../helpers/constants/routes';
import { EVENT } from '../../../../shared/constants/metametrics';
import NetworkDisplay from '../network-display';
export default class AppHeader extends PureComponent {
@ -45,7 +46,7 @@ export default class AppHeader extends PureComponent {
if (networkDropdownOpen === false) {
this.context.trackEvent({
category: 'Navigation',
category: EVENT.CATEGORIES.NAVIGATION,
event: 'Opened Network Menu',
properties: {
action: 'Home',
@ -77,7 +78,7 @@ export default class AppHeader extends PureComponent {
if (!disabled) {
!isAccountMenuOpen &&
this.context.trackEvent({
category: 'Navigation',
category: EVENT.CATEGORIES.NAVIGATION,
event: 'Opened Main Menu',
properties: {
action: 'Home',

View File

@ -13,6 +13,7 @@ import { updateSendAsset } from '../../../ducks/send';
import { SEND_ROUTE } from '../../../helpers/constants/routes';
import { SEVERITIES } from '../../../helpers/constants/design-system';
import { INVALID_ASSET_TYPE } from '../../../helpers/constants/error-keys';
import { EVENT } from '../../../../shared/constants/metametrics';
import { ASSET_TYPES } from '../../../../shared/constants/transaction';
import { MetaMetricsContext } from '../../../contexts/metametrics';
@ -65,7 +66,7 @@ const AssetListItem = ({
e.stopPropagation();
trackEvent({
event: 'Clicked Send: Token',
category: 'Navigation',
category: EVENT.CATEGORIES.NAVIGATION,
properties: {
action: 'Home',
legacy_event: true,

View File

@ -26,6 +26,7 @@ import {
} from '../../../helpers/constants/design-system';
import { useI18nContext } from '../../../hooks/useI18nContext';
import { MetaMetricsContext } from '../../../contexts/metametrics';
import { EVENT } from '../../../../shared/constants/metametrics';
const AssetList = ({ onClickAsset }) => {
const t = useI18nContext();
@ -83,7 +84,7 @@ const AssetList = ({ onClickAsset }) => {
onClickAsset(tokenAddress);
trackEvent({
event: 'Clicked Token',
category: 'Navigation',
category: EVENT.CATEGORIES.NAVIGATION,
properties: {
action: 'Token Menu',
legacy_event: true,
@ -107,7 +108,7 @@ const AssetList = ({ onClickAsset }) => {
history.push(IMPORT_TOKEN_ROUTE);
trackEvent({
event: 'Clicked "Add Token"',
category: 'Navigation',
category: EVENT.CATEGORIES.NAVIGATION,
properties: {
action: 'Token Menu',
legacy_event: true,

View File

@ -13,6 +13,7 @@ import InfoTooltip from '../../../../ui/info-tooltip';
import NicknamePopovers from '../../../modals/nickname-popovers';
import Typography from '../../../../ui/typography';
import { TYPOGRAPHY } from '../../../../../helpers/constants/design-system';
import { ORIGIN_METAMASK } from '../../../../../../shared/constants/app';
const ConfirmPageContainerSummary = (props) => {
const {
@ -83,7 +84,7 @@ const ConfirmPageContainerSummary = (props) => {
return (
<div className={classnames('confirm-page-container-summary', className)}>
{origin === 'metamask' ? null : (
{origin === ORIGIN_METAMASK ? null : (
<div className="confirm-page-container-summary__origin">{origin}</div>
)}
<div className="confirm-page-container-summary__action-row">

View File

@ -1,5 +1,6 @@
import React, { useCallback, useContext, useState } from 'react';
import PropTypes from 'prop-types';
import { EVENT } from '../../../../shared/constants/metametrics';
import { useI18nContext } from '../../../hooks/useI18nContext';
import { MetaMetricsContext } from '../../../contexts/metametrics';
import TextField from '../../ui/text-field';
@ -83,7 +84,7 @@ export default function CreateNewVault({
const toggleTermsCheck = useCallback(() => {
trackEvent({
category: 'Onboarding',
category: EVENT.CATEGORIES.ONBOARDING,
event: 'Check ToS',
properties: {
action: 'Import Seed Phrase',

View File

@ -17,6 +17,7 @@ import { COLORS, SIZES } from '../../../helpers/constants/design-system';
import { getShowTestNetworks } from '../../../selectors';
import { getEnvironmentType } from '../../../../app/scripts/lib/util';
import { ENVIRONMENT_TYPE_POPUP } from '../../../../shared/constants/app';
import { EVENT } from '../../../../shared/constants/metametrics';
import {
ADD_NETWORK_ROUTE,
ADVANCED_ROUTE,
@ -110,7 +111,7 @@ class NetworkDropdown extends Component {
const { trackEvent } = this.context;
trackEvent({
category: 'Navigation',
category: EVENT.CATEGORIES.NAVIGATION,
event: 'Switched Networks',
properties: {
action: 'Home',

View File

@ -9,6 +9,7 @@ import {
GAS_ESTIMATE_TYPES,
CUSTOM_GAS_ESTIMATE,
} from '../../../../shared/constants/gas';
import { EVENT } from '../../../../shared/constants/metametrics';
import Button from '../../ui/button';
import Typography from '../../ui/typography/typography';
@ -280,7 +281,7 @@ export default function EditGasDisplay({
setShowAdvancedForm(!showAdvancedForm);
trackEvent({
event: 'Clicked "Advanced Options"',
category: 'Transactions',
category: EVENT.CATEGORIES.TRANSACTIONS,
properties: {
action: 'Edit Screen',
legacy_event: true,

View File

@ -86,6 +86,8 @@ export default function ExperimentalArea({ redirectTo }) {
<p>{t('flaskWelcomeWarning2')}</p>
<br />
<p>{t('flaskWelcomeWarning3')}</p>
<br />
<p>{t('flaskWelcomeWarning4')}</p>
</div>
<Button type="primary" onClick={onClick}>
{t('flaskWelcomeWarningAcceptButton')}

View File

@ -8,6 +8,7 @@ import {
addPollingTokenToAppState,
removePollingTokenFromAppState,
} from '../../../../store/actions';
import { EVENT } from '../../../../../shared/constants/metametrics';
import AdvancedTabContent from './advanced-tab-content';
import BasicTabContent from './basic-tab-content';
@ -219,7 +220,7 @@ export default class GasModalPageContainer extends Component {
onSubmit={() => {
if (isSpeedUp) {
this.context.trackEvent({
category: 'Navigation',
category: EVENT.CATEGORIES.NAVIGATION,
event: 'Saved "Speed Up"',
properties: {
action: 'Activity Log',

View File

@ -8,6 +8,7 @@ import Box from '../../ui/box/box';
import { TEXT_ALIGN } from '../../../helpers/constants/design-system';
import { detectNewTokens } from '../../../store/actions';
import { MetaMetricsContext } from '../../../contexts/metametrics';
import { EVENT } from '../../../../shared/constants/metametrics';
export default function ImportTokenLink({ isMainnet }) {
const trackEvent = useContext(MetaMetricsContext);
@ -35,7 +36,7 @@ export default function ImportTokenLink({ isMainnet }) {
history.push(IMPORT_TOKEN_ROUTE);
trackEvent({
event: 'Clicked "Add Token"',
category: 'Navigation',
category: EVENT.CATEGORIES.NAVIGATION,
properties: {
action: 'Token Menu',
legacy_event: true,

View File

@ -17,6 +17,7 @@ import {
import { useI18nContext } from '../../../hooks/useI18nContext';
import { getEnvironmentType } from '../../../../app/scripts/lib/util';
import { ENVIRONMENT_TYPE_FULLSCREEN } from '../../../../shared/constants/app';
import { EVENT } from '../../../../shared/constants/metametrics';
import { MetaMetricsContext } from '../../../contexts/metametrics';
export default function AccountOptionsMenu({ anchorElement, onClose }) {
@ -46,7 +47,7 @@ export default function AccountOptionsMenu({ anchorElement, onClose }) {
onClick={() => {
trackEvent({
event: 'Clicked Block Explorer Link',
category: 'Navigation',
category: EVENT.CATEGORIES.NAVIGATION,
properties: {
link_type: 'Account Tracker',
action: 'Account Options',
@ -76,7 +77,7 @@ export default function AccountOptionsMenu({ anchorElement, onClose }) {
onClick={() => {
trackEvent({
event: 'Clicked Expand View',
category: 'Navigation',
category: EVENT.CATEGORIES.NAVIGATION,
properties: {
action: 'Account Options',
legacy_event: true,
@ -96,7 +97,7 @@ export default function AccountOptionsMenu({ anchorElement, onClose }) {
dispatch(showModal({ name: 'ACCOUNT_DETAILS' }));
trackEvent({
event: 'Viewed Account Details',
category: 'Navigation',
category: EVENT.CATEGORIES.NAVIGATION,
properties: {
action: 'Account Options',
legacy_event: true,
@ -113,7 +114,7 @@ export default function AccountOptionsMenu({ anchorElement, onClose }) {
onClick={() => {
trackEvent({
event: 'Opened Connected Sites',
category: 'Navigation',
category: EVENT.CATEGORIES.NAVIGATION,
properties: {
action: 'Account Options',
legacy_event: true,

View File

@ -6,6 +6,7 @@ import SelectedAccount from '../selected-account';
import ConnectedStatusIndicator from '../connected-status-indicator';
import { getEnvironmentType } from '../../../../app/scripts/lib/util';
import { ENVIRONMENT_TYPE_POPUP } from '../../../../shared/constants/app';
import { EVENT } from '../../../../shared/constants/metametrics';
import { CONNECTED_ACCOUNTS_ROUTE } from '../../../helpers/constants/routes';
import { useI18nContext } from '../../../hooks/useI18nContext';
import { getOriginOfCurrentTab } from '../../../selectors';
@ -46,7 +47,7 @@ export default function MenuBar() {
onClick={() => {
trackEvent({
event: 'Opened Account Options',
category: 'Navigation',
category: EVENT.CATEGORIES.NAVIGATION,
properties: {
action: 'Home',
legacy_event: true,

View File

@ -8,6 +8,7 @@ import Box from '../../ui/box';
import MetaMaskTranslation from '../metamask-translation';
import NetworkDisplay from '../network-display';
import TextArea from '../../ui/textarea/textarea';
import ConfirmationNetworkSwitch from '../../../pages/confirmation/components/confirmation-network-switch';
export const safeComponentList = {
MetaMaskTranslation,
@ -25,4 +26,5 @@ export const safeComponentList = {
Box,
NetworkDisplay,
TextArea,
ConfirmationNetworkSwitch,
};

View File

@ -8,6 +8,7 @@ import EditableLabel from '../../../ui/editable-label';
import Button from '../../../ui/button';
import { getURLHostName } from '../../../../helpers/utils/util';
import { isHardwareKeyring } from '../../../../helpers/utils/hardware';
import { EVENT } from '../../../../../shared/constants/metametrics';
export default class AccountDetailsModal extends Component {
static propTypes = {
@ -76,7 +77,7 @@ export default class AccountDetailsModal extends Component {
onClick={() => {
const accountLink = getAccountLink(address, chainId, rpcPrefs);
this.context.trackEvent({
category: 'Navigation',
category: EVENT.CATEGORIES.NAVIGATION,
event: 'Clicked Block Explorer Link',
properties: {
link_type: 'Account Tracker',

View File

@ -4,6 +4,7 @@ import { getAccountLink } from '@metamask/etherscan-link';
import Modal from '../../modal';
import { addressSummary, getURLHostName } from '../../../../helpers/utils/util';
import Identicon from '../../../ui/identicon';
import { EVENT } from '../../../../../shared/constants/metametrics';
export default class ConfirmRemoveAccount extends Component {
static propTypes = {
@ -60,7 +61,7 @@ export default class ConfirmRemoveAccount extends Component {
rpcPrefs,
);
this.context.trackEvent({
category: 'Accounts',
category: EVENT.CATEGORIES.ACCOUNTS,
event: 'Clicked Block Explorer Link',
properties: {
link_type: 'Account Tracker',

View File

@ -4,6 +4,7 @@ import {
NETWORK_TO_NAME_MAP,
BUYABLE_CHAINS_MAP,
} from '../../../../../shared/constants/network';
import { EVENT } from '../../../../../shared/constants/metametrics';
import Button from '../../../ui/button';
import LogoMoonPay from '../../../ui/logo/logo-moonpay';
import LogoWyre from '../../../ui/logo/logo-wyre';
@ -137,7 +138,7 @@ export default class DepositEtherModal extends Component {
buttonLabel: t('continueToTransak'),
onButtonClick: () => {
this.context.trackEvent({
category: 'Accounts',
category: EVENT.CATEGORIES.ACCOUNTS,
event: 'Click buy Ether via Transak',
properties: {
action: 'Deposit Ether',
@ -155,7 +156,7 @@ export default class DepositEtherModal extends Component {
buttonLabel: t('continueToMoonPay'),
onButtonClick: () => {
this.context.trackEvent({
category: 'Accounts',
category: EVENT.CATEGORIES.ACCOUNTS,
event: 'Click buy Ether via MoonPay',
properties: {
action: 'Deposit Ether',
@ -173,7 +174,7 @@ export default class DepositEtherModal extends Component {
buttonLabel: t('continueToWyre'),
onButtonClick: () => {
this.context.trackEvent({
category: 'Accounts',
category: EVENT.CATEGORIES.ACCOUNTS,
event: 'Click buy Ether via Wyre',
properties: {
action: 'Deposit Ether',

Some files were not shown because too many files have changed in this diff Show More