diff --git a/.circleci/config.yml b/.circleci/config.yml index 52702030dfb..ed48900b8e1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -20,7 +20,7 @@ jobs: prep-deps: <<: *defaults docker: - - image: circleci/node:10 + - image: circleci/node:14 steps: - checkout - run: @@ -33,7 +33,7 @@ jobs: prep-node-deps: <<: *defaults docker: - - image: circleci/node:10 + - image: circleci/node:14 steps: - checkout - restore_cache: *restore-node-cache @@ -49,7 +49,7 @@ jobs: lint: <<: *defaults docker: - - image: circleci/node:10 + - image: circleci/node:14 steps: - checkout - attach_workspace: @@ -60,7 +60,7 @@ jobs: <<: *defaults parallelism: 3 docker: - - image: circleci/node:10 + - image: circleci/node:14 steps: - checkout - attach_workspace: @@ -74,7 +74,7 @@ jobs: test-deps: <<: *defaults docker: - - image: circleci/node:10 + - image: circleci/node:14 steps: - checkout - attach_workspace: @@ -106,7 +106,7 @@ jobs: upload-coverage: <<: *defaults docker: - - image: circleci/node:10 + - image: circleci/node:14 steps: - checkout - attach_workspace: @@ -117,7 +117,7 @@ jobs: all-tests-pass: <<: *defaults docker: - - image: circleci/node:10 + - image: circleci/node:14 steps: - run: name: All Tests Passed @@ -189,6 +189,9 @@ jobs: - store_artifacts: path: sourcemaps/ios destination: sourcemaps-ios + - store_artifacts: + path: ios/output/gym + destination: output workflows: full_test: jobs: diff --git a/.eslintrc.js b/.eslintrc.js index bcf2b8df859..2b876e8b391 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,3 +1,4 @@ +// eslint-disable-next-line import/no-commonjs module.exports = { root: true, parser: 'babel-eslint', diff --git a/.nvmrc b/.nvmrc index e338b86593f..958b5a36e1f 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v10 +v14 diff --git a/CHANGELOG.md b/CHANGELOG.md index fb592ef25a1..a5e846579f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,49 @@ # Changelog +## v.2.3.0 - May 5 2021 +- [#2674](https://github.com/MetaMask/metamask-mobile/pull/2674): Fix deploy contract and create token testnets +- [#2669](https://github.com/MetaMask/metamask-mobile/pull/2669): Key off accounts +- [#2670](https://github.com/MetaMask/metamask-mobile/pull/2670): Bump hosted-git-info from 2.8.8 to 2.8.9 +- [#2667](https://github.com/MetaMask/metamask-mobile/pull/2667): added export of iOS artifacts +- [#2664](https://github.com/MetaMask/metamask-mobile/pull/2664): updated version code and change logs +- [#2663](https://github.com/MetaMask/metamask-mobile/pull/2663): Load video over the network +- [#2656](https://github.com/MetaMask/metamask-mobile/pull/2656): Fix Balance undefined for deeplink payment requests +- [#2657](https://github.com/MetaMask/metamask-mobile/pull/2657): Fix missing seed phrase updates +- [#2645](https://github.com/MetaMask/metamask-mobile/pull/2645): Safe navbar for iphone 12 +- [#2643](https://github.com/MetaMask/metamask-mobile/pull/2643): Fix undefined is not an object identities[selectedAddress].importTime +- [#2639](https://github.com/MetaMask/metamask-mobile/pull/2639): Exclude native asset from hiding when balance is zero +- [#2631](https://github.com/MetaMask/metamask-mobile/pull/2631): updated change log +- [#2633](https://github.com/MetaMask/metamask-mobile/pull/2633): Address yarn audit +- [#2625](https://github.com/MetaMask/metamask-mobile/pull/2625): Fix isZero is undefined +- [#2444](https://github.com/MetaMask/metamask-mobile/pull/2444): Implement 'hide zero balance token' setting for token balances on home screen +- [#2621](https://github.com/MetaMask/metamask-mobile/pull/2621): RC v2.3.0 +- [#2605](https://github.com/MetaMask/metamask-mobile/pull/2605): Feature/update seed phrase wording +- [#2564](https://github.com/MetaMask/metamask-mobile/pull/2564): Improve rpc errors logging and removing user rejected errors +- [#2556](https://github.com/MetaMask/metamask-mobile/pull/2556): Fix/respect custom spend limit on dapp approve modal +- [#2614](https://github.com/MetaMask/metamask-mobile/pull/2614): updated lock files +- [#2586](https://github.com/MetaMask/metamask-mobile/pull/2586): Upgrade swaps-controller v4 +- [#2613](https://github.com/MetaMask/metamask-mobile/pull/2613): remove typo +- [#2603](https://github.com/MetaMask/metamask-mobile/pull/2603): Bugfix/android anr +- [#2565](https://github.com/MetaMask/metamask-mobile/pull/2565): This will fix sentry errors with no title by using the extra info as a title +- [#2552](https://github.com/MetaMask/metamask-mobile/pull/2552): Upgrade wallet connect +- [#2607](https://github.com/MetaMask/metamask-mobile/pull/2607): Detox/Fix failing tests +- [#2604](https://github.com/MetaMask/metamask-mobile/pull/2604): Don't hide url modal on emulator +- [#2529](https://github.com/MetaMask/metamask-mobile/pull/2529): Move some errors to analytics instead of sentry +- [#2446](https://github.com/MetaMask/metamask-mobile/pull/2446): Add New Zealand Dollar to currency options +- [#2464](https://github.com/MetaMask/metamask-mobile/pull/2464): Feature/confusables +- [#2610](https://github.com/MetaMask/metamask-mobile/pull/2610): fix typeface on login text field +- [#2416](https://github.com/MetaMask/metamask-mobile/pull/2416): Replace controller context +- [#2590](https://github.com/MetaMask/metamask-mobile/pull/2590): Fix adding custom token in custom network +- [#2470](https://github.com/MetaMask/metamask-mobile/pull/2470): only add custom tokens if not in mainnet +- [#2524](https://github.com/MetaMask/metamask-mobile/pull/2524): Address yarn lints +- [#2588](https://github.com/MetaMask/metamask-mobile/pull/2588): Upgrade .nvmrc to node v14 +- [#2514](https://github.com/MetaMask/metamask-mobile/pull/2514): Swaps: Add cache thresholds configuration +- [#2468](https://github.com/MetaMask/metamask-mobile/pull/2468): Swaps: BSC Support +- [#2539](https://github.com/MetaMask/metamask-mobile/pull/2539): Use node 14 +- [#2568](https://github.com/MetaMask/metamask-mobile/pull/2568): resolve isENS without case sensitivity (#2545) +- [#2566](https://github.com/MetaMask/metamask-mobile/pull/2566): Revert "resolve isENS without case sensitivity (#2545)" +- [#2545](https://github.com/MetaMask/metamask-mobile/pull/2545): resolve isENS without case sensitivity + ## v2.2.0 - Apr 21 2021 - [#2547](https://github.com/MetaMask/metamask-mobile/pull/2547): Include decimalsToShow in balanceToFiatNumber - [#2554](https://github.com/MetaMask/metamask-mobile/pull/2554): Bug fix/sync import time diff --git a/android/app/build.gradle b/android/app/build.gradle index 87caea76f7b..838e4c4277b 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -166,8 +166,8 @@ android { applicationId "io.metamask" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 54 - versionName "2.2.0" + versionCode 715 + versionName "2.3.0" multiDexEnabled true testBuildType System.getProperty('testBuildType', 'debug') missingDimensionStrategy "minReactNative", "minReactNative46" diff --git a/app/__mocks__/react-native-device-info.js b/app/__mocks__/react-native-device-info.js new file mode 100644 index 00000000000..5d6f3a8f2e9 --- /dev/null +++ b/app/__mocks__/react-native-device-info.js @@ -0,0 +1,4 @@ +export const getModel = () => Promise.resolve('iPhone 12'); +export const getApplicationName = () => Promise.resolve('applicationName'); +export const getVersion = () => Promise.resolve('version'); +export const getBuildNumber = () => Promise.resolve('500'); diff --git a/app/__mocks__/rn-fetch-blob.js b/app/__mocks__/rn-fetch-blob.js index 2e08193f590..efdd761092c 100644 --- a/app/__mocks__/rn-fetch-blob.js +++ b/app/__mocks__/rn-fetch-blob.js @@ -9,6 +9,7 @@ export default { config: noop, session: noop, fs: { + writeFile: () => Promise.resolve(), exists: () => Promise.resolve(), dirs: { CacheDir: noop, diff --git a/app/actions/settings/index.js b/app/actions/settings/index.js index 8bec8eda1f1..eca421a12c9 100644 --- a/app/actions/settings/index.js +++ b/app/actions/settings/index.js @@ -19,6 +19,13 @@ export function setShowCustomNonce(showCustomNonce) { }; } +export function setHideZeroBalanceTokens(hideZeroBalanceTokens) { + return { + type: 'SET_HIDE_ZERO_BALANCE_TOKENS', + hideZeroBalanceTokens + }; +} + export function setLockTime(lockTime) { return { type: 'SET_LOCK_TIME', diff --git a/app/components/Base/Text.js b/app/components/Base/Text.js index 906ba541319..1478cb60198 100644 --- a/app/components/Base/Text.js +++ b/app/components/Base/Text.js @@ -16,10 +16,13 @@ const style = StyleSheet.create({ right: { textAlign: 'right' }, - bold: fontStyles.bold, + red: { + color: colors.red + }, black: { color: colors.black }, + bold: fontStyles.bold, blue: { color: colors.blue }, @@ -66,6 +69,7 @@ const Text = ({ green, black, blue, + red, primary, small, upper, @@ -87,6 +91,8 @@ const Text = ({ green && style.green, black && style.black, blue && style.blue, + red && style.red, + black && style.black, primary && style.primary, disclaimer && [style.small, style.disclaimer], small && style.small, @@ -110,6 +116,7 @@ Text.defaultProps = { green: false, black: false, blue: false, + red: false, primary: false, disclaimer: false, modal: false, @@ -150,6 +157,10 @@ Text.propTypes = { * Makes text blue */ blue: PropTypes.bool, + /** + * Makes text red + */ + red: PropTypes.bool, /** * Makes text fontPrimary color */ diff --git a/app/components/Nav/App/index.js b/app/components/Nav/App/index.js index 12309f49053..bfecc8ddaf9 100644 --- a/app/components/Nav/App/index.js +++ b/app/components/Nav/App/index.js @@ -28,6 +28,7 @@ import Engine from '../../../core/Engine'; import Logger from '../../../util/Logger'; import Branch from 'react-native-branch'; import AppConstants from '../../../core/AppConstants'; +import { trackErrorAsAnalytics } from '../../../util/analyticsV2'; /** * Stack navigator responsible for the onboarding process @@ -186,7 +187,11 @@ class App extends PureComponent { handleDeeplinks = async ({ error, params, uri }) => { if (error) { - Logger.error(error, 'Deeplink: Error from Branch'); + if (error === 'Trouble reaching the Branch servers, please try again shortly.') { + trackErrorAsAnalytics('Branch: Trouble reaching servers', error); + } else { + Logger.error(error, 'Deeplink: Error from Branch'); + } } const deeplink = params['+non_branch_link'] || uri || null; try { diff --git a/app/components/Nav/Main/index.js b/app/components/Nav/Main/index.js index 096f0f05d88..219c9c299d0 100644 --- a/app/components/Nav/Main/index.js +++ b/app/components/Nav/Main/index.js @@ -38,7 +38,6 @@ import { decodeApproveData } from '../../../util/transactions'; import { BN } from 'ethereumjs-util'; -import { safeToChecksumAddress } from '../../../util/address'; import Logger from '../../../util/Logger'; import contractMap from '@metamask/contract-metadata'; import MessageSign from '../../UI/MessageSign'; @@ -58,7 +57,8 @@ import AccountApproval from '../../UI/AccountApproval'; import ProtectYourWalletModal from '../../UI/ProtectYourWalletModal'; import MainNavigator from './MainNavigator'; import SkipAccountSecurityModal from '../../UI/SkipAccountSecurityModal'; -import { swapsUtils, util } from '@estebanmino/controllers'; +import { swapsUtils } from '@metamask/swaps-controller'; +import { util } from '@metamask/controllers'; import SwapsLiveness from '../../UI/Swaps/SwapsLiveness'; import Analytics from '../../../core/Analytics'; import { ANALYTICS_EVENT_OPTS } from '../../../util/analytics'; @@ -67,7 +67,8 @@ import { setInfuraAvailabilityBlocked, setInfuraAvailabilityNotBlocked } from '. const styles = StyleSheet.create({ flex: { - flex: 1 + flex: 1, + marginTop: Device.isIphone12() ? 20 : 0 }, loader: { backgroundColor: colors.white, @@ -238,12 +239,12 @@ const Main = props => { .div(gasEstimate) .times(100) .toFixed(2)}%`; - const quoteVsExecutionRatio = `${util + const quoteVsExecutionRatio = `${swapsUtils .calcTokenAmount(tokensReceived || '0x0', swapTransaction.destinationTokenDecimals) .div(swapTransaction.destinationAmount) .times(100) .toFixed(2)}%`; - const tokenToAmountReceived = util.calcTokenAmount( + const tokenToAmountReceived = swapsUtils.calcTokenAmount( tokensReceived, swapTransaction.destinationToken.decimals ); @@ -310,16 +311,18 @@ const Main = props => { async transactionMeta => { if (transactionMeta.origin === TransactionTypes.MMM) return; - const to = safeToChecksumAddress(transactionMeta.transaction.to); + const to = transactionMeta.transaction.to?.toLowerCase(); const { data } = transactionMeta.transaction; // if approval data includes metaswap contract // if destination address is metaswap contract if ( - to === safeToChecksumAddress(swapsUtils.SWAPS_CONTRACT_ADDRESS) || - (data && - data.substr(0, 10) === APPROVE_FUNCTION_SIGNATURE && - decodeApproveData(data).spenderAddress === swapsUtils.SWAPS_CONTRACT_ADDRESS) + to && + (to === swapsUtils.getSwapsContractAddress(props.chainId) || + (data && + data.substr(0, 10) === APPROVE_FUNCTION_SIGNATURE && + decodeApproveData(data).spenderAddress?.toLowerCase() === + swapsUtils.getSwapsContractAddress(props.chainId))) ) { if (transactionMeta.origin === process.env.MM_FOX_CODE) { autoSign(transactionMeta); @@ -388,6 +391,7 @@ const Main = props => { }, [ props.tokens, + props.chainId, setEtherTransaction, setTransactionObject, toggleApproveModal, @@ -568,6 +572,14 @@ const Main = props => { } }); + // unapprovedTransaction effect + useEffect(() => { + Engine.context.TransactionController.hub.on('unapprovedTransaction', onUnapprovedTransaction); + return () => { + Engine.context.TransactionController.hub.removeListener('unapprovedTransaction', onUnapprovedTransaction); + }; + }, [onUnapprovedTransaction]); + useEffect(() => { initializeWalletConnect(); AppState.addEventListener('change', handleAppStateChange); @@ -596,8 +608,6 @@ const Main = props => { } }); - Engine.context.TransactionController.hub.on('unapprovedTransaction', onUnapprovedTransaction); - Engine.context.MessageManager.hub.on('unapprovedMessage', messageParams => onUnapprovedMessage(messageParams, 'eth') ); @@ -628,7 +638,6 @@ const Main = props => { lockManager.current.stopListening(); Engine.context.PersonalMessageManager.hub.removeAllListeners(); Engine.context.TypedMessageManager.hub.removeAllListeners(); - Engine.context.TransactionController.hub.removeListener('unapprovedTransaction', onUnapprovedTransaction); WalletConnect.hub.removeAllListeners(); removeConnectionStatusListener.current && removeConnectionStatusListener.current(); }; @@ -737,6 +746,10 @@ Main.propTypes = { * Selected address */ selectedAddress: PropTypes.string, + /** + * Chain id + */ + chainId: PropTypes.string, /** * Network provider type */ @@ -755,6 +768,7 @@ const mapStateToProps = state => ({ lockTime: state.settings.lockTime, thirdPartyApiMode: state.privacy.thirdPartyApiMode, selectedAddress: state.engine.backgroundState.PreferencesController.selectedAddress, + chainId: state.engine.backgroundState.NetworkController.provider.chainId, tokens: state.engine.backgroundState.AssetsController.tokens, isPaymentRequest: state.transaction.paymentRequest, dappTransactionModalVisible: state.modals.dappTransactionModalVisible, diff --git a/app/components/UI/AccountOverview/index.js b/app/components/UI/AccountOverview/index.js index 9b11b04316f..101bafc2f3e 100644 --- a/app/components/UI/AccountOverview/index.js +++ b/app/components/UI/AccountOverview/index.js @@ -2,7 +2,7 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import { ScrollView, TextInput, StyleSheet, Text, View, TouchableOpacity, InteractionManager } from 'react-native'; import Clipboard from '@react-native-community/clipboard'; -import { swapsUtils } from '@estebanmino/controllers'; +import { swapsUtils } from '@metamask/swaps-controller'; import { connect } from 'react-redux'; import Engine from '../../../core/Engine'; import Analytics from '../../../core/Analytics'; @@ -21,6 +21,7 @@ import { renderFiat } from '../../../util/number'; import { renderAccountName } from '../../../util/address'; import { isMainNet } from '../../../util/networks'; import { getEther } from '../../../util/transactions'; +import { isSwapsAllowed } from '../Swaps/utils'; import Identicon from '../Identicon'; import AssetActionButton from '../AssetActionButton'; @@ -257,7 +258,7 @@ class AccountOverview extends PureComponent { goToSwaps = () => this.props.navigation.navigate('Swaps', { - sourceToken: swapsUtils.ETH_SWAPS_TOKEN_ADDRESS + sourceToken: swapsUtils.NATIVE_SWAPS_TOKEN_ADDRESS }); render() { @@ -325,13 +326,14 @@ class AccountOverview extends PureComponent { : { borderColor: colors.white } ]} numberOfLines={1} + testID={'edit-account-label'} > {name} )} - {fiatBalance} + {isMainNet(chainId) && {fiatBalance}} @@ -358,7 +360,7 @@ class AccountOverview extends PureComponent { {AppConstants.SWAPS.ACTIVE && ( diff --git a/app/components/UI/AddCustomNetwork/__snapshots__/index.test.js.snap b/app/components/UI/AddCustomNetwork/__snapshots__/index.test.js.snap index 19c45c50f29..7d82f7e0e3d 100644 --- a/app/components/UI/AddCustomNetwork/__snapshots__/index.test.js.snap +++ b/app/components/UI/AddCustomNetwork/__snapshots__/index.test.js.snap @@ -26,6 +26,7 @@ exports[`AddCustomNetwork should render correctly 1`] = ` modal={false} noMargin={true} primary={true} + red={false} reset={false} right={false} small={false} @@ -55,6 +56,7 @@ exports[`AddCustomNetwork should render correctly 1`] = ` modal={false} noMargin={true} primary={true} + red={false} reset={false} right={false} small={false} @@ -83,6 +85,7 @@ exports[`AddCustomNetwork should render correctly 1`] = ` modal={false} noMargin={true} primary={true} + red={false} reset={false} right={false} small={false} @@ -109,6 +112,7 @@ exports[`AddCustomNetwork should render correctly 1`] = ` modal={false} noMargin={true} primary={true} + red={false} reset={false} right={false} small={false} @@ -132,6 +136,7 @@ exports[`AddCustomNetwork should render correctly 1`] = ` noMargin={true} onPress={[Function]} primary={true} + red={false} reset={false} right={false} small={false} @@ -174,6 +179,7 @@ exports[`AddCustomNetwork should render correctly 1`] = ` modal={false} noMargin={true} primary={true} + red={false} reset={false} right={false} small={true} @@ -199,6 +205,7 @@ exports[`AddCustomNetwork should render correctly 1`] = ` modal={false} noMargin={true} primary={true} + red={false} reset={false} right={true} small={false} @@ -232,6 +239,7 @@ exports[`AddCustomNetwork should render correctly 1`] = ` modal={false} noMargin={true} primary={true} + red={false} reset={false} right={false} small={true} @@ -257,6 +265,7 @@ exports[`AddCustomNetwork should render correctly 1`] = ` modal={false} noMargin={true} primary={true} + red={false} reset={false} right={true} small={false} @@ -289,6 +298,7 @@ exports[`AddCustomNetwork should render correctly 1`] = ` modal={false} noMargin={true} primary={true} + red={false} reset={false} right={false} small={true} @@ -314,6 +324,7 @@ exports[`AddCustomNetwork should render correctly 1`] = ` modal={false} noMargin={true} primary={true} + red={false} reset={false} right={true} small={false} @@ -350,6 +361,7 @@ exports[`AddCustomNetwork should render correctly 1`] = ` modal={false} noMargin={true} primary={false} + red={false} reset={false} right={false} small={false} diff --git a/app/components/UI/AddCustomToken/index.js b/app/components/UI/AddCustomToken/index.js index 1e6a08cd346..d1e01408d02 100644 --- a/app/components/UI/AddCustomToken/index.js +++ b/app/components/UI/AddCustomToken/index.js @@ -125,7 +125,9 @@ export default class AddCustomToken extends PureComponent { let validated = true; const address = this.state.address; const isValidTokenAddress = isValidAddress(address); - const toSmartContract = isValidTokenAddress && (await isSmartContractAddress(address)); + const { NetworkController } = Engine.context; + const { chainId } = NetworkController?.state?.provider || {}; + const toSmartContract = isValidTokenAddress && (await isSmartContractAddress(address, chainId)); if (address.length === 0) { this.setState({ warningAddress: strings('token.address_cant_be_empty') }); validated = false; diff --git a/app/components/UI/ApproveTransactionReview/EditPermission/index.js b/app/components/UI/ApproveTransactionReview/EditPermission/index.js index 992eb40781b..f460bdbedcc 100644 --- a/app/components/UI/ApproveTransactionReview/EditPermission/index.js +++ b/app/components/UI/ApproveTransactionReview/EditPermission/index.js @@ -1,4 +1,4 @@ -import React, { useCallback, useMemo, useState, useEffect } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import PropTypes from 'prop-types'; import { View, StyleSheet, TouchableOpacity, TextInput } from 'react-native'; import { colors, fontStyles } from '../../../../styles/common'; @@ -9,6 +9,8 @@ import ConnectHeader from '../../ConnectHeader'; import Device from '../../../../util/Device'; import ErrorMessage from '../../../Views/SendFlow/ErrorMessage'; +export const MINIMUM_VALUE = '1'; + const styles = StyleSheet.create({ wrapper: { paddingHorizontal: 24, @@ -106,15 +108,14 @@ function EditPermission({ onPressSpendLimitCustomSelected, toggleEditPermission }) { - const [approvalSet, setApprovalSet] = useState(false); + const [initialState] = useState({ spendLimitUnlimitedSelected, spendLimitCustomValue }); const displayErrorMessage = useMemo( - () => !spendLimitUnlimitedSelected && minimumSpendLimit > spendLimitCustomValue, + () => !spendLimitUnlimitedSelected && Number(minimumSpendLimit) > spendLimitCustomValue, [spendLimitUnlimitedSelected, spendLimitCustomValue, minimumSpendLimit] ); const onSetApprovalAmount = useCallback(() => { - setApprovalSet(true); if (!spendLimitUnlimitedSelected && !spendLimitCustomValue) { onPressSpendLimitUnlimitedSelected(); } else { @@ -122,17 +123,26 @@ function EditPermission({ } }, [spendLimitUnlimitedSelected, spendLimitCustomValue, onPressSpendLimitUnlimitedSelected, setApprovalAmount]); - useEffect( - () => - function cleanup() { - if (!spendLimitUnlimitedSelected && !approvalSet) onPressSpendLimitUnlimitedSelected(); - }, - [spendLimitUnlimitedSelected, approvalSet, onPressSpendLimitUnlimitedSelected] - ); + const onBackPress = useCallback(() => { + const { spendLimitUnlimitedSelected, spendLimitCustomValue } = initialState; + if (spendLimitUnlimitedSelected) { + onPressSpendLimitUnlimitedSelected(); + } else { + onPressSpendLimitCustomSelected(); + } + onSpendLimitCustomValueChange(spendLimitCustomValue); + toggleEditPermission(); + }, [ + initialState, + onPressSpendLimitCustomSelected, + onPressSpendLimitUnlimitedSelected, + onSpendLimitCustomValueChange, + toggleEditPermission + ]); return ( - + {strings('spend_limit_edition.spend_limit')} @@ -225,7 +235,7 @@ function EditPermission({ ); } EditPermission.defaultProps = { - minimumSpendLimit: '1' + minimumSpendLimit: MINIMUM_VALUE }; EditPermission.propTypes = { diff --git a/app/components/UI/ApproveTransactionReview/index.js b/app/components/UI/ApproveTransactionReview/index.js index 35873b19479..3131af86ef2 100644 --- a/app/components/UI/ApproveTransactionReview/index.js +++ b/app/components/UI/ApproveTransactionReview/index.js @@ -38,7 +38,8 @@ import { withNavigation } from 'react-navigation'; import { getNetworkName, isMainNet } from '../../../util/networks'; import scaling from '../../../util/scaling'; import { capitalize } from '../../../util/general'; -import EditPermission from './EditPermission'; +import EditPermission, { MINIMUM_VALUE } from './EditPermission'; +import Logger from '../../../util/Logger'; const { hexToBN } = util; const styles = StyleSheet.create({ @@ -369,7 +370,6 @@ class ApproveTransactionReview extends PureComponent { toggleEditPermission = () => { const { editPermissionVisible } = this.state; - this.onPressSpendLimitUnlimitedSelected(); !editPermissionVisible && this.trackApproveEvent(ANALYTICS_EVENT_OPTS.DAPP_APPROVE_SCREEN_EDIT_PERMISSION); this.setState({ editPermissionVisible: !editPermissionVisible }); }; @@ -419,16 +419,22 @@ class ApproveTransactionReview extends PureComponent { spendLimitCustomValue, transaction } = this.state; - const uint = toTokenMinimalUnit( - spendLimitUnlimitedSelected ? originalApproveAmount : spendLimitCustomValue, - token.decimals - ).toString(); - const approvalData = generateApproveData({ - spender: spenderAddress, - value: Number(uint).toString(16) - }); - const newApprovalTransaction = { ...transaction, data: approvalData }; - setTransactionObject(newApprovalTransaction); + + try { + const uint = toTokenMinimalUnit( + spendLimitUnlimitedSelected ? originalApproveAmount : spendLimitCustomValue, + token.decimals + ).toString(10); + + const approvalData = generateApproveData({ + spender: spenderAddress, + value: Number(uint).toString(16) + }); + const newApprovalTransaction = { ...transaction, data: approvalData }; + setTransactionObject(newApprovalTransaction); + } catch (err) { + Logger.log('Failed to setTransactionObject', err); + } this.toggleEditPermission(); AnalyticsV2.trackEvent(AnalyticsV2.ANALYTICS_EVENTS.APPROVAL_PERMISSION_UPDATED, this.getAnalyticsParams()); }; @@ -442,12 +448,14 @@ class ApproveTransactionReview extends PureComponent { originalApproveAmount } = this.state; + const _spendLimitCustomValue = spendLimitCustomValue ?? MINIMUM_VALUE; + return ( { + const { host, tokenSymbol, totalGas, totalGasFiat, ticker, spenderAddress } = this.state; + + const { + primaryCurrency, + currentCurrency, + gasError, + activeTabUrl, + transaction: { origin }, + network, + over, + warningGasPriceHigh + } = this.props; + const is_main_net = isMainNet(network); + const isFiat = primaryCurrency.toLowerCase() === 'fiat'; + const currencySymbol = currencySymbols[currentCurrency]; + const totalGasFiatRounded = Math.round(totalGasFiat * 100) / 100; + const originIsDeeplink = origin === ORIGIN_DEEPLINK || origin === ORIGIN_QR_CODE; + const errorPress = is_main_net ? this.buyEth : this.gotoFaucet; + const networkName = capitalize(getNetworkName(network)); + const errorLinkText = is_main_net + ? strings('transaction.buy_more_eth') + : strings('transaction.get_ether', { networkName }); + + return ( + <> + + + + {strings( + `spend_limit_edition.${originIsDeeplink ? 'allow_to_address_access' : 'allow_to_access'}`, + { tokenSymbol } + )} + + + {`${strings( + `spend_limit_edition.${originIsDeeplink ? 'you_trust_this_address' : 'you_trust_this_site'}` + )}`} + + + + + + + {strings('spend_limit_edition.edit_permission')} + + + + + + + + + {strings('transaction.transaction_fee')} + + + {isFiat && currencySymbol} + {isFiat ? totalGasFiatRounded : totalGas} {!isFiat && ticker} + + + + + + + {gasError && ( + + + {gasError} + {/* only show buy more on mainnet */} + {over && is_main_net && ( + + {errorLinkText} + + )} + + + )} + {!!warningGasPriceHigh && ( + + {warningGasPriceHigh} + + )} + {!gasError && ( + + + + {strings('spend_limit_edition.view_details')} + + + + )} + + + + + + + + ); + }; + renderTransactionReview = () => { const { host, @@ -465,8 +589,11 @@ class ApproveTransactionReview extends PureComponent { viewData, tokenSymbol, originalApproveAmount, + spendLimitUnlimitedSelected, + spendLimitCustomValue, transaction: { to, data } } = this.state; + const allowance = (!spendLimitUnlimitedSelected && spendLimitCustomValue) || originalApproveAmount; return ( { - const { - host, - tokenSymbol, - viewDetails, - totalGas, - totalGasFiat, - editPermissionVisible, - ticker, - spenderAddress - } = this.state; - - const { - primaryCurrency, - currentCurrency, - gasError, - activeTabUrl, - transaction: { origin }, - network, - over, - warningGasPriceHigh - } = this.props; - const is_main_net = isMainNet(network); - const isFiat = primaryCurrency.toLowerCase() === 'fiat'; - const currencySymbol = currencySymbols[currentCurrency]; - const totalGasFiatRounded = Math.round(totalGasFiat * 100) / 100; - const originIsDeeplink = origin === ORIGIN_DEEPLINK || origin === ORIGIN_QR_CODE; - const errorPress = is_main_net ? this.buyEth : this.gotoFaucet; - const networkName = capitalize(getNetworkName(network)); - const errorLinkText = is_main_net - ? strings('transaction.buy_more_eth') - : strings('transaction.get_ether', { networkName }); + const { viewDetails, editPermissionVisible } = this.state; return ( - {viewDetails ? ( - this.renderTransactionReview() - ) : editPermissionVisible ? ( - this.renderEditPermission() - ) : ( - <> - - - - {strings( - `spend_limit_edition.${ - originIsDeeplink ? 'allow_to_address_access' : 'allow_to_access' - }`, - { tokenSymbol } - )} - - - {`${strings( - `spend_limit_edition.${ - originIsDeeplink ? 'you_trust_this_address' : 'you_trust_this_site' - }` - )}`} - - - - - - - {strings('spend_limit_edition.edit_permission')} - - - - - - - - - {strings('transaction.transaction_fee')} - - - {isFiat && currencySymbol} - {isFiat ? totalGasFiatRounded : totalGas}{' '} - {!isFiat && ticker} - - - - - - - {gasError && ( - - - {gasError} - {/* only show buy more on mainnet */} - {over && is_main_net && ( - - {errorLinkText} - - )} - - - )} - {!!warningGasPriceHigh && ( - - {warningGasPriceHigh} - - )} - {!gasError && ( - - - - {strings('spend_limit_edition.view_details')} - - - - )} - - - - - - - - )} + {viewDetails + ? this.renderTransactionReview() + : editPermissionVisible + ? this.renderEditPermission() + : this.renderDetails()} ); }; diff --git a/app/components/UI/AssetActionButton/__snapshots__/index.test.js.snap b/app/components/UI/AssetActionButton/__snapshots__/index.test.js.snap index 7e4369c5da1..08125e83109 100644 --- a/app/components/UI/AssetActionButton/__snapshots__/index.test.js.snap +++ b/app/components/UI/AssetActionButton/__snapshots__/index.test.js.snap @@ -40,6 +40,7 @@ exports[`AssetActionButtons should render correctly 1`] = ` modal={false} numberOfLines={1} primary={false} + red={false} reset={false} right={false} small={false} @@ -97,6 +98,7 @@ exports[`AssetActionButtons should render type add correctly 1`] = ` modal={false} numberOfLines={1} primary={false} + red={false} reset={false} right={false} small={false} @@ -154,6 +156,7 @@ exports[`AssetActionButtons should render type information correctly 1`] = ` modal={false} numberOfLines={1} primary={false} + red={false} reset={false} right={false} small={false} @@ -211,6 +214,7 @@ exports[`AssetActionButtons should render type receive correctly 1`] = ` modal={false} numberOfLines={1} primary={false} + red={false} reset={false} right={false} small={false} @@ -268,6 +272,7 @@ exports[`AssetActionButtons should render type send correctly 1`] = ` modal={false} numberOfLines={1} primary={false} + red={false} reset={false} right={false} small={false} @@ -325,6 +330,7 @@ exports[`AssetActionButtons should render type swap correctly 1`] = ` modal={false} numberOfLines={1} primary={false} + red={false} reset={false} right={false} small={false} diff --git a/app/components/UI/AssetOverview/index.js b/app/components/UI/AssetOverview/index.js index 87fd44b11df..b4b56e83529 100644 --- a/app/components/UI/AssetOverview/index.js +++ b/app/components/UI/AssetOverview/index.js @@ -1,7 +1,7 @@ import React, { PureComponent } from 'react'; import { InteractionManager, StyleSheet, Text, View, TouchableOpacity } from 'react-native'; import PropTypes from 'prop-types'; -import { swapsUtils } from '@estebanmino/controllers'; +import { swapsUtils } from '@metamask/swaps-controller'; import AssetIcon from '../AssetIcon'; import Identicon from '../Identicon'; import AssetActionButton from '../AssetActionButton'; @@ -12,9 +12,10 @@ import { toggleReceiveModal } from '../../../actions/modals'; import { connect } from 'react-redux'; import { renderFromTokenMinimalUnit, balanceToFiat, renderFromWei, weiToFiat, hexToBN } from '../../../util/number'; import { safeToChecksumAddress } from '../../../util/address'; +import { isMainNet } from '../../../util/networks'; import { getEther } from '../../../util/transactions'; import { newAssetTransaction } from '../../../actions/transaction'; -import { isMainNet } from '../../../util/networks'; +import { isSwapsAllowed } from '../Swaps/utils'; import { swapsLivenessSelector, swapsTokensObjectSelector } from '../../../reducers/swaps'; import Engine from '../../../core/Engine'; import Logger from '../../../util/Logger'; @@ -178,7 +179,7 @@ class AssetOverview extends PureComponent { goToSwaps = () => { this.props.navigation.navigate('Swaps', { - sourceToken: this.props.asset.isETH ? swapsUtils.ETH_SWAPS_TOKEN_ADDRESS : this.props.asset.address + sourceToken: this.props.asset.isETH ? swapsUtils.NATIVE_SWAPS_TOKEN_ADDRESS : this.props.asset.address }); }; @@ -208,7 +209,7 @@ class AssetOverview extends PureComponent { try { await SwapsController.fetchTokenWithCache(); } catch (error) { - Logger.error(error, 'Swaps: error while fetching tokens with catche in AssetOverview'); + Logger.error(error, 'Swaps: error while fetching tokens with cache in AssetOverview'); } }; @@ -249,12 +250,16 @@ class AssetOverview extends PureComponent { let balance, balanceFiat; if (isETH) { balance = renderFromWei(accounts[selectedAddress] && accounts[selectedAddress].balance); - balanceFiat = weiToFiat(hexToBN(accounts[selectedAddress].balance), conversionRate, currentCurrency); + balanceFiat = isMainNet(chainId) + ? weiToFiat(hexToBN(accounts[selectedAddress].balance), conversionRate, currentCurrency) + : null; } else { const exchangeRate = itemAddress in tokenExchangeRates ? tokenExchangeRates[itemAddress] : undefined; balance = itemAddress in tokenBalances ? renderFromTokenMinimalUnit(tokenBalances[itemAddress], decimals) : 0; - balanceFiat = balanceToFiat(balance, conversionRate, exchangeRate, currentCurrency); + balanceFiat = isMainNet(chainId) + ? balanceToFiat(balance, conversionRate, exchangeRate, currentCurrency) + : null; } // choose balances depending on 'primaryCurrency' if (primaryCurrency === 'ETH') { @@ -275,7 +280,7 @@ class AssetOverview extends PureComponent { {mainBalance} - {secondaryBalance} + {secondaryBalance && {secondaryBalance}} )} @@ -303,7 +308,7 @@ class AssetOverview extends PureComponent { {AppConstants.SWAPS.ACTIVE && ( diff --git a/app/components/UI/ComponentErrorBoundary/index.js b/app/components/UI/ComponentErrorBoundary/index.js index a18a1b2da03..7193cb7346a 100644 --- a/app/components/UI/ComponentErrorBoundary/index.js +++ b/app/components/UI/ComponentErrorBoundary/index.js @@ -1,6 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import Logger from '../../../util/Logger'; +import { trackErrorAsAnalytics } from '../../../util/analyticsV2'; class ComponentErrorBoundary extends React.Component { state = { error: null }; @@ -17,7 +18,11 @@ class ComponentErrorBoundary extends React.Component { /** * Function to be called when there is an error */ - onError: PropTypes.func + onError: PropTypes.func, + /** + * Will not track as an error, but still log to analytics + */ + dontTrackAsError: PropTypes.bool }; static getDerivedStateFromError(error) { @@ -28,6 +33,11 @@ class ComponentErrorBoundary extends React.Component { // eslint-disable-next-line no-unused-expressions this.props.onError?.(); + const { componentLabel, dontTrackAsError } = this.props; + + if (dontTrackAsError) { + return trackErrorAsAnalytics(`Component Error Boundary: ${componentLabel}`, error?.message); + } Logger.error(error, { View: this.props.componentLabel, ...errorInfo }); } diff --git a/app/components/UI/CustomGas/index.js b/app/components/UI/CustomGas/index.js index 3103e63b99c..be6d5ab7a29 100644 --- a/app/components/UI/CustomGas/index.js +++ b/app/components/UI/CustomGas/index.js @@ -252,6 +252,10 @@ class CustomGas extends PureComponent { * Object BN containing gas price */ gasPrice: PropTypes.object, + /** + * Object BN containing mininum gas price + */ + minimumGasPrice: PropTypes.object, /** * Callback to modify parent state */ @@ -488,6 +492,11 @@ class CustomGas extends PureComponent { const warningSufficientFunds = this.hasSufficientFunds(customGasLimitBN, gasPriceBNWei); let warningGasPrice; let warningGasPriceHigh = ''; + if (this.onlyAdvanced() && this.props.minimumGasPrice) { + if (parseInt(gasPrice) < parseInt(fromWei(this.props.minimumGasPrice, 'gwei'))) { + warningGasPrice = strings('transaction.low_gas_price'); + } + } if (this.props.basicGasEstimates) { if (parseInt(gasPrice) < parseInt(this.props.basicGasEstimates.safeLowGwei)) warningGasPrice = strings('transaction.low_gas_price'); diff --git a/app/components/UI/CustomNonceModal/__snapshots__/index.test.js.snap b/app/components/UI/CustomNonceModal/__snapshots__/index.test.js.snap index 5177dd71ee4..79962ceefa0 100644 --- a/app/components/UI/CustomNonceModal/__snapshots__/index.test.js.snap +++ b/app/components/UI/CustomNonceModal/__snapshots__/index.test.js.snap @@ -88,6 +88,7 @@ exports[`CustomNonceModal should render correctly 1`] = ` link={false} modal={false} primary={false} + red={false} reset={false} right={false} small={false} @@ -150,6 +151,7 @@ exports[`CustomNonceModal should render correctly 1`] = ` link={false} modal={false} primary={false} + red={false} reset={false} right={false} small={false} @@ -176,6 +178,7 @@ exports[`CustomNonceModal should render correctly 1`] = ` link={false} modal={false} primary={false} + red={false} reset={false} right={false} small={false} @@ -280,6 +283,7 @@ exports[`CustomNonceModal should render correctly 1`] = ` link={false} modal={false} primary={false} + red={false} reset={false} right={false} small={false} @@ -309,6 +313,7 @@ exports[`CustomNonceModal should render correctly 1`] = ` link={false} modal={false} primary={false} + red={false} reset={false} right={false} small={false} @@ -336,6 +341,7 @@ exports[`CustomNonceModal should render correctly 1`] = ` link={false} modal={false} primary={false} + red={false} reset={false} right={false} small={false} diff --git a/app/components/UI/DrawerView/index.js b/app/components/UI/DrawerView/index.js index 4590ab75626..5f348aab50f 100644 --- a/app/components/UI/DrawerView/index.js +++ b/app/components/UI/DrawerView/index.js @@ -8,7 +8,7 @@ import Icon from 'react-native-vector-icons/FontAwesome'; import FeatherIcon from 'react-native-vector-icons/Feather'; import MaterialIcon from 'react-native-vector-icons/MaterialCommunityIcons'; import { colors, fontStyles } from '../../../styles/common'; -import { hasBlockExplorer, findBlockExplorerForRpc, getBlockExplorerName } from '../../../util/networks'; +import { hasBlockExplorer, findBlockExplorerForRpc, getBlockExplorerName, isMainNet } from '../../../util/networks'; import Identicon from '../Identicon'; import StyledButton from '../StyledButton'; import AccountList from '../AccountList'; @@ -338,6 +338,10 @@ class DrawerView extends PureComponent { * Wizard onboarding state */ wizard: PropTypes.object, + /** + * Chain Id + */ + chainId: PropTypes.string, /** * Current provider ticker */ @@ -830,6 +834,7 @@ class DrawerView extends PureComponent { selectedAddress, keyrings, currentCurrency, + chainId, ticker, seedphraseBackedUp } = this.props; @@ -876,7 +881,7 @@ class DrawerView extends PureComponent { - {fiatBalanceStr} + {isMainNet(chainId) && {fiatBalanceStr}} ({ receiveModalVisible: state.modals.receiveModalVisible, passwordSet: state.user.passwordSet, wizard: state.wizard, + chainId: state.engine.backgroundState.NetworkController.provider.chainId, ticker: state.engine.backgroundState.NetworkController.provider.ticker, tokens: state.engine.backgroundState.AssetsController.tokens, tokenBalances: state.engine.backgroundState.TokenBalancesController.contractBalances, diff --git a/app/components/UI/HintModal/__snapshots__/index.test.js.snap b/app/components/UI/HintModal/__snapshots__/index.test.js.snap index 48abffbe4e1..72fc7341aea 100644 --- a/app/components/UI/HintModal/__snapshots__/index.test.js.snap +++ b/app/components/UI/HintModal/__snapshots__/index.test.js.snap @@ -83,7 +83,7 @@ exports[`HintModal should render correctly 1`] = ` } } > - Do not use this to write your seed phrase. + Do not use this to write your Secret Recovery phrase. + {backButtonText} ) : ( - + ) diff --git a/app/components/UI/NetworkList/__snapshots__/index.test.js.snap b/app/components/UI/NetworkList/__snapshots__/index.test.js.snap index f9a971fd11f..e93a2b0ff95 100644 --- a/app/components/UI/NetworkList/__snapshots__/index.test.js.snap +++ b/app/components/UI/NetworkList/__snapshots__/index.test.js.snap @@ -42,6 +42,7 @@ exports[`NetworkList should render correctly 1`] = ` "flex": 1, } } + testID="other-networks-scroll" > - + {this.renderMainnet()} diff --git a/app/components/UI/Notification/TransactionNotification/index.js b/app/components/UI/Notification/TransactionNotification/index.js index 0cff2f48f06..043e60f817f 100644 --- a/app/components/UI/Notification/TransactionNotification/index.js +++ b/app/components/UI/Notification/TransactionNotification/index.js @@ -94,7 +94,6 @@ function TransactionNotification(props) { accounts, currentNotification, isInBrowserView, - navigation, notificationAnimated, onClose, transactions, @@ -179,6 +178,7 @@ function TransactionNotification(props) { const { selectedAddress, ticker, + chainId, conversionRate, currentCurrency, exchangeRate, @@ -194,6 +194,7 @@ function TransactionNotification(props) { tx, selectedAddress, ticker, + chainId, conversionRate, currentCurrency, exchangeRate, @@ -252,7 +253,6 @@ function TransactionNotification(props) { ({ selectedAddress: state.engine.backgroundState.PreferencesController.selectedAddress, transactions: state.engine.backgroundState.TransactionController.transactions, ticker: state.engine.backgroundState.NetworkController.provider.ticker, + chainId: state.engine.backgroundState.NetworkController.provider.chainId, tokens: state.engine.backgroundState.AssetsController.tokens.reduce((tokens, token) => { tokens[token.address] = token; return tokens; diff --git a/app/components/UI/ReceiveRequest/__snapshots__/index.test.js.snap b/app/components/UI/ReceiveRequest/__snapshots__/index.test.js.snap index 71e670559c0..84e57f358d0 100644 --- a/app/components/UI/ReceiveRequest/__snapshots__/index.test.js.snap +++ b/app/components/UI/ReceiveRequest/__snapshots__/index.test.js.snap @@ -28,6 +28,7 @@ exports[`ReceiveRequest should render correctly 1`] = ` link={false} modal={false} primary={false} + red={false} reset={false} right={false} small={false} @@ -70,6 +71,7 @@ exports[`ReceiveRequest should render correctly 1`] = ` link={false} modal={false} primary={false} + red={false} reset={false} right={false} small={false} @@ -104,6 +106,7 @@ exports[`ReceiveRequest should render correctly 1`] = ` link={false} modal={false} primary={false} + red={false} reset={false} right={false} small={false} @@ -126,6 +129,7 @@ exports[`ReceiveRequest should render correctly 1`] = ` link={false} modal={false} primary={false} + red={false} reset={false} right={false} small={true} diff --git a/app/components/UI/SeedPhraseVideo/index.js b/app/components/UI/SeedPhraseVideo/index.js new file mode 100644 index 00000000000..526c35be22e --- /dev/null +++ b/app/components/UI/SeedPhraseVideo/index.js @@ -0,0 +1,122 @@ +import React, { useState } from 'react'; +import PropTypes from 'prop-types'; +import { StyleSheet, View, Image, TouchableOpacity } from 'react-native'; +import VideoPlayer from 'react-native-video-controls'; +import { colors, fontStyles } from '../../../styles/common'; +import Logger from '../../../util/Logger'; +import scaling from '../../../util/scaling'; +import Svg, { Circle, Path } from 'react-native-svg'; +import Text from '../../Base/Text'; +import { strings } from '../../../../locales/i18n'; + +const styles = StyleSheet.create({ + videoContainer: { + justifyContent: 'center', + alignItems: 'center', + position: 'relative', + borderRadius: 8, + overflow: 'hidden', + width: '100%', + height: 180 + }, + image: { + zIndex: 0, + width: scaling.scale(138), + height: scaling.scale(162) + }, + cover: { + justifyContent: 'center', + alignItems: 'center', + zIndex: 1, + position: 'absolute', + left: 0, + top: 0, + backgroundColor: colors.grey, + opacity: 0.2, + width: '100%', + height: '100%' + }, + errorWrapper: { + justifyContent: 'center', + alignItems: 'center' + }, + errorText: { + ...fontStyles.normal, + color: colors.red + } +}); + +const FAILED_TO_LOAD_MSG = strings('app_settings.video_failed'); + +// eslint-disable-next-line import/no-commonjs +const explain_backup_seedphrase = require('../../../images/explain-backup-seedphrase.png'); + +const video_source_uri = + 'https://github.com/MetaMask/metamask-mobile/blob/develop/app/videos/recovery-phrase.mp4?raw=true'; + +const SeedPhraseVideo = ({ style }) => { + const [isPlaying, setPlaying] = useState(false); + const [isError, setError] = useState(false); + + const onError = e => { + Logger.error(e, FAILED_TO_LOAD_MSG); + setError(true); + setPlaying(false); + }; + const onPlay = () => { + Logger.log('User clicked play'); + setPlaying(true); + }; + + return ( + + {!isPlaying ? ( + <> + {isError ? ( + + {FAILED_TO_LOAD_MSG} + + ) : ( + <> + + {isError ? <> : <>} + + + + + + + + )} + + ) : ( + + )} + + ); +}; + +SeedPhraseVideo.propTypes = { + style: PropTypes.object +}; + +export default SeedPhraseVideo; diff --git a/app/components/UI/SeedphraseModal/__snapshots__/index.test.js.snap b/app/components/UI/SeedphraseModal/__snapshots__/index.test.js.snap index 42a61c7aa8e..35cba44fe08 100644 --- a/app/components/UI/SeedphraseModal/__snapshots__/index.test.js.snap +++ b/app/components/UI/SeedphraseModal/__snapshots__/index.test.js.snap @@ -57,7 +57,7 @@ exports[`SeedphraseModal should render correctly 1`] = ` } } > - What is a ‘Seed phrase’ + What is a ‘Secret recovery phrase’ - A seed phrase is a set of twelve words that contains all the information about your wallet, including your funds. It’s like a secret code used to access your entire wallet. + A Secret Recovery phrase is a set of twelve words that contains all the information about your wallet, including your funds. It’s like a secret code used to access your entire wallet. - You must keep your seed phrase secret and safe. If someone gets your seed phrase, they’ll gain control over your accounts. + You must keep your Secret Recovery phrase secret and safe. If someone gets your Secret Recovery phrase, they’ll gain control over your accounts. diff --git a/app/components/UI/SkipAccountSecurityModal/__snapshots__/index.test.js.snap b/app/components/UI/SkipAccountSecurityModal/__snapshots__/index.test.js.snap index c6f209c10d7..a5ee0e1963e 100644 --- a/app/components/UI/SkipAccountSecurityModal/__snapshots__/index.test.js.snap +++ b/app/components/UI/SkipAccountSecurityModal/__snapshots__/index.test.js.snap @@ -130,7 +130,7 @@ exports[`HintModal should render correctly 1`] = ` } testID="skip-backup-text" > - I understand that if I lose my seed phrase I will not be able to access my wallet. + I understand that if I lose my Secret Recovery phrase I will not be able to access my wallet. diff --git a/app/components/UI/Swaps/QuotesView.js b/app/components/UI/Swaps/QuotesView.js index e50bc23b3c7..c9c438cd366 100644 --- a/app/components/UI/Swaps/QuotesView.js +++ b/app/components/UI/Swaps/QuotesView.js @@ -7,8 +7,8 @@ import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityI import FAIcon from 'react-native-vector-icons/FontAwesome'; import BigNumber from 'bignumber.js'; import { NavigationContext } from 'react-navigation'; -import { swapsUtils, util } from '@estebanmino/controllers'; -import { WalletDevice } from '@metamask/controllers/'; +import { swapsUtils } from '@metamask/swaps-controller'; +import { WalletDevice, util } from '@metamask/controllers/'; import { BNToHex, @@ -20,8 +20,9 @@ import { toWei, weiToFiat } from '../../../util/number'; +import { isMainNet } from '../../../util/networks'; import { safeToChecksumAddress } from '../../../util/address'; -import { getErrorMessage, getFetchParams, getQuotesNavigationsParams, isSwapsETH } from './utils'; +import { getErrorMessage, getFetchParams, getQuotesNavigationsParams, isSwapsNativeAsset } from './utils'; import { colors } from '../../../styles/common'; import { strings } from '../../../../locales/i18n'; @@ -49,8 +50,8 @@ import InfoModal from './components/InfoModal'; import useModalHandler from '../../Base/hooks/useModalHandler'; import useBalance from './utils/useBalance'; import useGasPrice from './utils/useGasPrice'; -import { decodeApproveData } from '../../../util/transactions'; -import Logger from '../../../util/Logger'; +import { trackErrorAsAnalytics } from '../../../util/analyticsV2'; +import { decodeApproveData, getTicker } from '../../../util/transactions'; const POLLING_INTERVAL = AppConstants.SWAPS.POLLING_INTERVAL; const EDIT_MODE_GAS = 'EDIT_MODE_GAS'; @@ -198,6 +199,9 @@ const styles = StyleSheet.create({ termsButton: { marginTop: 10, marginBottom: 6 + }, + text: { + lineHeight: 20 } }); @@ -209,7 +213,7 @@ async function resetAndStartPolling({ slippage, sourceToken, destinationToken, s const contractExchangeRates = TokenRatesController.state.contractExchangeRates; // ff the token is not in the wallet, we'll add it if ( - destinationToken.address !== swapsUtils.ETH_SWAPS_TOKEN_ADDRESS && + !isSwapsNativeAsset(destinationToken) && !contractExchangeRates[safeToChecksumAddress(destinationToken.address)] ) { const { address, symbol, decimals } = destinationToken; @@ -253,6 +257,8 @@ function SwapsQuotesView({ selectedAddress, currentCurrency, conversionRate, + chainId, + ticker, isInPolling, quotesLastFetched, pollingCyclesLeft, @@ -280,7 +286,7 @@ function SwapsQuotesView({ const hasConversionRate = Boolean(destinationToken) && - (isSwapsETH(destinationToken) || + (isSwapsNativeAsset(destinationToken) || Boolean( Engine.context.TokenRatesController.state.contractExchangeRates?.[ safeToChecksumAddress(destinationToken.address) @@ -373,7 +379,7 @@ function SwapsQuotesView({ const hasEnoughTokenBalance = tokenBalanceBN.gte(sourceBN); const missingTokenBalance = hasEnoughTokenBalance ? null : sourceBN.minus(tokenBalanceBN); - const ethAmountBN = sourceToken.address === swapsUtils.ETH_SWAPS_TOKEN_ADDRESS ? sourceBN : new BigNumber(0); + const ethAmountBN = isSwapsNativeAsset(sourceToken) ? sourceBN : new BigNumber(0); const ethBalanceBN = new BigNumber(accounts[selectedAddress].balance); const gasBN = toWei(selectedQuoteValue?.maxEthFee || '0'); const hasEnoughEthBalance = ethBalanceBN.gte(ethAmountBN.plus(gasBN)); @@ -681,7 +687,7 @@ function SwapsQuotesView({ speed_set: details.mode === 'advanced' ? undefined : details.mode, gas_mode: details.mode === 'advanced' ? 'Advanced' : 'Basic', gas_fees: weiToFiat( - toWei(util.calcTokenAmount(newGasLimit.times(newGasPrice), 18)), + toWei(swapsUtils.calcTokenAmount(newGasLimit.times(newGasPrice), 18)), conversionRate, currentCurrency ) @@ -772,7 +778,6 @@ function SwapsQuotesView({ slippage, custom_slippage: slippage !== AppConstants.SWAPS.DEFAULT_SLIPPAGE }; - Logger.error(error?.description, `Swaps: ${error?.key}`); if (error?.key === swapsUtils.SwapsError.QUOTES_EXPIRED_ERROR) { InteractionManager.runAfterInteractions(() => { const parameters = { @@ -788,6 +793,8 @@ function SwapsQuotesView({ Analytics.trackEventWithParameters(ANALYTICS_EVENT_OPTS.NO_QUOTES_AVAILABLE, {}); Analytics.trackEventWithParameters(ANALYTICS_EVENT_OPTS.NO_QUOTES_AVAILABLE, parameters, true); }); + } else { + trackErrorAsAnalytics(`Swaps: ${error?.key}`, error?.description); } }, [sourceToken, sourceAmount, destinationToken, hasEnoughTokenBalance, slippage] @@ -1022,22 +1029,23 @@ function SwapsQuotesView({ {`${strings('swaps.you_need')} `} - {!hasEnoughTokenBalance && sourceToken.address !== swapsUtils.ETH_SWAPS_TOKEN_ADDRESS + {!hasEnoughTokenBalance && !isSwapsNativeAsset(sourceToken) ? `${renderFromTokenMinimalUnit(missingTokenBalance, sourceToken.decimals)} ${ sourceToken.symbol // eslint-disable-next-line no-mixed-spaces-and-tabs } ` - : `${renderFromWei(missingEthBalance)} ETH `} + : `${renderFromWei(missingEthBalance)} ${getTicker(ticker)} `} {!hasEnoughTokenBalance ? `${strings('swaps.more_to_complete')} ` : `${strings('swaps.more_gas_to_complete')} `} - {(sourceToken.address === swapsUtils.ETH_SWAPS_TOKEN_ADDRESS || - (hasEnoughTokenBalance && !hasEnoughEthBalance)) && ( - - {strings('swaps.buy_more_eth')} - - )} + {isMainNet(chainId) && + (isSwapsNativeAsset(sourceToken) || + (hasEnoughTokenBalance && !hasEnoughEthBalance)) && ( + + {strings('swaps.buy_more_eth')} + + )} )} @@ -1209,7 +1217,7 @@ function SwapsQuotesView({ - {renderFromWei(toWei(selectedQuoteValue?.ethFee))} ETH + {renderFromWei(toWei(selectedQuoteValue?.ethFee))} {getTicker(ticker)} {` ${weiToFiat( @@ -1233,7 +1241,10 @@ function SwapsQuotesView({ - {renderFromWei(toWei(selectedQuoteValue?.maxEthFee || '0x0'))} ETH + + {renderFromWei(toWei(selectedQuoteValue?.maxEthFee || '0x0'))}{' '} + {getTicker(ticker)} + {` ${weiToFiat( toWei(selectedQuoteValue?.maxEthFee), @@ -1296,20 +1307,20 @@ function SwapsQuotesView({ isVisible={isUpdateModalVisible} toggleModal={toggleUpdateModal} title={strings('swaps.quotes_update_often')} - body={{strings('swaps.quotes_update_often_text')}} + body={{strings('swaps.quotes_update_often_text')}} /> {strings('swaps.price_difference_body')}} + body={{strings('swaps.price_difference_body')}} /> + {strings('swaps.fee_text.get_the')} {strings('swaps.fee_text.best_price')}{' '} {strings('swaps.fee_text.from_the')} {strings('swaps.fee_text.top_liquidity')}{' '} {strings('swaps.fee_text.fee_is_applied', { @@ -1326,6 +1337,7 @@ function SwapsQuotesView({ destinationToken={destinationToken} selectedQuote={selectedQuoteId} showOverallValue={hasConversionRate} + ticker={getTicker(ticker)} /> ); @@ -1374,6 +1387,14 @@ SwapsQuotesView.propTypes = { * A string that represents the selected address */ selectedAddress: PropTypes.string, + /** + * Chain Id + */ + chainId: PropTypes.string, + /** + * Native asset ticker + */ + ticker: PropTypes.string, isInPolling: PropTypes.bool, quotesLastFetched: PropTypes.number, topAggId: PropTypes.string, @@ -1392,6 +1413,8 @@ SwapsQuotesView.propTypes = { const mapStateToProps = state => ({ accounts: state.engine.backgroundState.AccountTrackerController.accounts, + chainId: state.engine.backgroundState.NetworkController.provider.chainId, + ticker: state.engine.backgroundState.NetworkController.provider.ticker, selectedAddress: state.engine.backgroundState.PreferencesController.selectedAddress, balances: state.engine.backgroundState.TokenBalancesController.contractBalances, conversionRate: state.engine.backgroundState.CurrencyRateController.conversionRate, diff --git a/app/components/UI/Swaps/SwapsLiveness.js b/app/components/UI/Swaps/SwapsLiveness.js index 27ba0a6fb07..16d3324a1d8 100644 --- a/app/components/UI/Swaps/SwapsLiveness.js +++ b/app/components/UI/Swaps/SwapsLiveness.js @@ -1,4 +1,4 @@ -import { swapsUtils } from '@estebanmino/controllers'; +import { swapsUtils } from '@metamask/swaps-controller'; import { useCallback, useEffect, useState } from 'react'; import { AppState } from 'react-native'; import { connect } from 'react-redux'; @@ -10,18 +10,18 @@ import useInterval from '../../hooks/useInterval'; const SWAPS_ACTIVE = AppConstants.SWAPS.ACTIVE; const POLLING_FREQUENCY = AppConstants.SWAPS.LIVENESS_POLLING_FREQUENCY; -function SwapLiveness({ isLive, setLiveness }) { +function SwapLiveness({ isLive, chainId, setLiveness }) { const [hasMountChecked, setHasMountChecked] = useState(false); const checkLiveness = useCallback(async () => { try { - const { mobile_active: liveness } = await swapsUtils.fetchSwapsFeatureLiveness(); - setLiveness(liveness); + const { mobile_active: liveness } = await swapsUtils.fetchSwapsFeatureLiveness(chainId); + setLiveness(liveness, chainId); } catch (error) { Logger.error(error, 'Swaps: error while fetching swaps liveness'); - setLiveness(false); + setLiveness(false, chainId); } - }, [setLiveness]); + }, [setLiveness, chainId]); // Check on mount useEffect(() => { @@ -62,11 +62,12 @@ function SwapLiveness({ isLive, setLiveness }) { } const mapStateToProps = state => ({ - isLive: swapsLivenessSelector(state) + isLive: swapsLivenessSelector(state), + chainId: state.engine.backgroundState.NetworkController.provider.chainId }); const mapDispatchToProps = dispatch => ({ - setLiveness: liveness => dispatch(setSwapsLiveness(liveness)) + setLiveness: (liveness, chainId) => dispatch(setSwapsLiveness(liveness, chainId)) }); export default connect( diff --git a/app/components/UI/Swaps/components/QuotesModal.js b/app/components/UI/Swaps/components/QuotesModal.js index 9078cd8ca13..514245d3497 100644 --- a/app/components/UI/Swaps/components/QuotesModal.js +++ b/app/components/UI/Swaps/components/QuotesModal.js @@ -137,7 +137,8 @@ function QuotesModal({ conversionRate, currentCurrency, quoteValues, - showOverallValue + showOverallValue, + ticker }) { const bestOverallValue = quoteValues[quotes[0].aggregator].overallValueOfQuote; const [displayDetails, setDisplayDetails] = useState(false); @@ -290,7 +291,7 @@ function QuotesModal({ {renderFromWei(toWei(selectedDetailsQuoteValues.ethFee))}{' '} - ETH + {ticker} {' '} (~ @@ -466,6 +467,10 @@ QuotesModal.propTypes = { * Currency code of the currently-active currency */ currentCurrency: PropTypes.string, + /** + * Native asset ticker + */ + ticker: PropTypes.string, quoteValues: PropTypes.object, showOverallValue: PropTypes.bool }; diff --git a/app/components/UI/Swaps/components/TokenIcon.js b/app/components/UI/Swaps/components/TokenIcon.js index d0ef90f26b9..b857ef30c93 100644 --- a/app/components/UI/Swaps/components/TokenIcon.js +++ b/app/components/UI/Swaps/components/TokenIcon.js @@ -6,8 +6,10 @@ import RemoteImage from '../../../Base/RemoteImage'; import Text from '../../../Base/Text'; import { colors } from '../../../../styles/common'; -// eslint-disable-next-line import/no-commonjs +/* eslint-disable import/no-commonjs */ const ethLogo = require('../../../../images/eth-logo.png'); +const bnbLogo = require('../../../../images/bnb-logo.png'); +/* eslint-enable import/no-commonjs */ const REGULAR_SIZE = 24; const REGULAR_RADIUS = 12; @@ -79,11 +81,11 @@ EmptyIcon.propTypes = { }; function TokenIcon({ symbol, icon, medium, big, biggest, style }) { - if (symbol === 'ETH') { + if (symbol === 'ETH' || symbol === 'BNB') { return ( ({ diff --git a/app/components/UI/Swaps/components/__snapshots__/TokenIcon.test.js.snap b/app/components/UI/Swaps/components/__snapshots__/TokenIcon.test.js.snap index db85e8e40db..2e5a01e3d1d 100644 --- a/app/components/UI/Swaps/components/__snapshots__/TokenIcon.test.js.snap +++ b/app/components/UI/Swaps/components/__snapshots__/TokenIcon.test.js.snap @@ -38,6 +38,7 @@ exports[`TokenIcon component should Render correctly 3`] = ` link={false} modal={false} primary={false} + red={false} reset={false} right={false} small={false} @@ -134,6 +135,7 @@ exports[`TokenIcon component should Render correctly 7`] = ` link={false} modal={false} primary={false} + red={false} reset={false} right={false} small={false} diff --git a/app/components/UI/Swaps/components/__snapshots__/TokenSelectButton.test.js.snap b/app/components/UI/Swaps/components/__snapshots__/TokenSelectButton.test.js.snap index 60cadcd3f84..a609ae4002a 100644 --- a/app/components/UI/Swaps/components/__snapshots__/TokenSelectButton.test.js.snap +++ b/app/components/UI/Swaps/components/__snapshots__/TokenSelectButton.test.js.snap @@ -34,6 +34,7 @@ exports[`TokenSelectButton component should Render correctly 1`] = ` link={false} modal={false} primary={true} + red={false} reset={false} right={false} small={false} @@ -96,6 +97,7 @@ exports[`TokenSelectButton component should Render correctly 2`] = ` link={false} modal={false} primary={true} + red={false} reset={false} right={false} small={false} @@ -158,6 +160,7 @@ exports[`TokenSelectButton component should Render correctly 3`] = ` link={false} modal={false} primary={true} + red={false} reset={false} right={false} small={false} @@ -221,6 +224,7 @@ exports[`TokenSelectButton component should Render correctly 4`] = ` link={false} modal={false} primary={true} + red={false} reset={false} right={false} small={false} @@ -286,6 +290,7 @@ exports[`TokenSelectButton component should Render correctly 5`] = ` link={false} modal={false} primary={true} + red={false} reset={false} right={false} small={false} diff --git a/app/components/UI/Swaps/index.js b/app/components/UI/Swaps/index.js index f244c074dce..ab6b62f733a 100644 --- a/app/components/UI/Swaps/index.js +++ b/app/components/UI/Swaps/index.js @@ -15,7 +15,7 @@ import { weiToFiat } from '../../../util/number'; import { safeToChecksumAddress } from '../../../util/address'; -import { swapsUtils } from '@estebanmino/controllers'; +import { swapsUtils } from '@metamask/swaps-controller'; import { ANALYTICS_EVENT_OPTS } from '../../../util/analytics'; import { @@ -30,10 +30,9 @@ import Device from '../../../util/Device'; import Engine from '../../../core/Engine'; import AppConstants from '../../../core/AppConstants'; -import { getEtherscanAddressUrl } from '../../../util/etherscan'; import { strings } from '../../../../locales/i18n'; import { colors } from '../../../styles/common'; -import { setQuotesNavigationsParams, isSwapsETH } from './utils'; +import { setQuotesNavigationsParams, isSwapsNativeAsset } from './utils'; import { getSwapsAmountNavbar } from '../Navbar'; import Onboarding from './components/Onboarding'; @@ -47,6 +46,7 @@ import TokenSelectButton from './components/TokenSelectButton'; import TokenSelectModal from './components/TokenSelectModal'; import SlippageModal from './components/SlippageModal'; import useBalance from './utils/useBalance'; +import useBlockExplorer from './utils/useBlockExplorer'; import InfoModal from './components/InfoModal'; const styles = StyleSheet.create({ @@ -130,7 +130,7 @@ const styles = StyleSheet.create({ } }); -const SWAPS_ETH_ADDRESS = swapsUtils.ETH_SWAPS_TOKEN_ADDRESS; +const SWAPS_NATIVE_ADDRESS = swapsUtils.NATIVE_SWAPS_TOKEN_ADDRESS; const TOKEN_MINIMUM_SOURCES = 1; const MAX_TOP_ASSETS = 20; @@ -138,6 +138,9 @@ function SwapsAmountView({ swapsTokens, accounts, selectedAddress, + chainId, + provider, + frequentRpcList, balances, tokensWithBalance, tokensTopAssets, @@ -149,7 +152,8 @@ function SwapsAmountView({ setLiveness }) { const navigation = useContext(NavigationContext); - const initialSource = navigation.getParam('sourceToken', SWAPS_ETH_ADDRESS); + const explorer = useBlockExplorer(provider, frequentRpcList); + const initialSource = navigation.getParam('sourceToken', SWAPS_NATIVE_ADDRESS); const [amount, setAmount] = useState('0'); const [slippage, setSlippage] = useState(AppConstants.SWAPS.DEFAULT_SLIPPAGE); const [isInitialLoadingTokens, setInitialLoadingTokens] = useState(false); @@ -179,13 +183,13 @@ function SwapsAmountView({ useEffect(() => { (async () => { try { - const { mobile_active: liveness } = await swapsUtils.fetchSwapsFeatureLiveness(); - setLiveness(liveness); + const { mobile_active: liveness } = await swapsUtils.fetchSwapsFeatureLiveness(chainId); + setLiveness(liveness, chainId); if (liveness) { // Triggered when a user enters the MetaMask Swap feature InteractionManager.runAfterInteractions(() => { const parameters = { - source: initialSource === SWAPS_ETH_ADDRESS ? 'MainView' : 'TokenView', + source: initialSource === SWAPS_NATIVE_ADDRESS ? 'MainView' : 'TokenView', activeCurrency: swapsTokens?.find( token => token.address?.toLowerCase() === initialSource.toLowerCase() )?.symbol @@ -198,12 +202,12 @@ function SwapsAmountView({ } } catch (error) { Logger.error(error, 'Swaps: error while fetching swaps liveness'); - setLiveness(false); + setLiveness(false, chainId); navigation.pop(); } })(); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [initialSource, navigation, setLiveness]); + }, [initialSource, chainId, navigation, setLiveness]); const keypadViewRef = useRef(null); @@ -251,11 +255,13 @@ function SwapsAmountView({ }, [destinationToken]); const isTokenInBalances = - sourceToken && !isSwapsETH(sourceToken) ? safeToChecksumAddress(sourceToken.address) in balances : false; + sourceToken && !isSwapsNativeAsset(sourceToken) + ? safeToChecksumAddress(sourceToken.address) in balances + : false; useEffect(() => { (async () => { - if (sourceToken && !isSwapsETH(sourceToken) && !isTokenInBalances) { + if (sourceToken && !isSwapsNativeAsset(sourceToken) && !isTokenInBalances) { setContractBalance(null); setContractBalanceAsUnits(numberToBN(0)); const { AssetsContractController } = Engine.context; @@ -285,9 +291,9 @@ function SwapsAmountView({ const controllerBalance = useBalance(accounts, balances, selectedAddress, sourceToken); const controllerBalanceAsUnits = useBalance(accounts, balances, selectedAddress, sourceToken, { asUnits: true }); - const balance = isSwapsETH(sourceToken) || isTokenInBalances ? controllerBalance : contractBalance; + const balance = isSwapsNativeAsset(sourceToken) || isTokenInBalances ? controllerBalance : contractBalance; const balanceAsUnits = - isSwapsETH(sourceToken) || isTokenInBalances ? controllerBalanceAsUnits : contractBalanceAsUnits; + isSwapsNativeAsset(sourceToken) || isTokenInBalances ? controllerBalanceAsUnits : contractBalanceAsUnits; const hasBalance = useMemo(() => { if (!balanceAsUnits || !sourceToken) { return false; @@ -308,7 +314,7 @@ function SwapsAmountView({ return undefined; } let balanceFiat; - if (isSwapsETH(sourceToken)) { + if (isSwapsNativeAsset(sourceToken)) { balanceFiat = weiToFiat(toTokenMinimalUnit(amount, sourceToken?.decimals), conversionRate, currentCurrency); } else { const sourceAddress = safeToChecksumAddress(sourceToken.address); @@ -319,7 +325,7 @@ function SwapsAmountView({ }, [amount, conversionRate, currentCurrency, hasInvalidDecimals, sourceToken, tokenExchangeRates]); const destinationTokenHasEnoughOcurrances = useMemo(() => { - if (!destinationToken || isSwapsETH(destinationToken)) { + if (!destinationToken || isSwapsNativeAsset(destinationToken)) { return true; } return destinationToken?.occurances > TOKEN_MINIMUM_SOURCES; @@ -330,7 +336,7 @@ function SwapsAmountView({ if (hasInvalidDecimals) { return; } - if (!isSwapsETH(sourceToken) && !isTokenInBalances && !balanceAsUnits?.isZero()) { + if (!isSwapsNativeAsset(sourceToken) && !isTokenInBalances && !balanceAsUnits?.isZero()) { const { AssetsController } = Engine.context; const { address, symbol, decimals } = sourceToken; await AssetsController.addToken(address, symbol, decimals); @@ -404,10 +410,10 @@ function SwapsAmountView({ } hideTokenVerificationModal(); navigation.navigate('Webview', { - url: getEtherscanAddressUrl('mainnet', destinationToken.address), + url: explorer.token(destinationToken.address), title: strings('swaps.verify') }); - }, [destinationToken, hideTokenVerificationModal, navigation]); + }, [explorer, destinationToken, hideTokenVerificationModal, navigation]); const handleAmountPress = useCallback(() => keypadViewRef?.current?.shake?.(), []); @@ -481,7 +487,7 @@ function SwapsAmountView({ strings('swaps.available_to_swap', { asset: `${balance} ${sourceToken.symbol}` })} - {!isSwapsETH(sourceToken) && hasBalance && ( + {!isSwapsNativeAsset(sourceToken) && hasBalance && ( {' '} {strings('swaps.use_max')} @@ -519,23 +525,33 @@ function SwapsAmountView({ dismiss={toggleDestinationModal} title={strings('swaps.convert_to')} tokens={swapsTokens} - initialTokens={[swapsUtils.ETH_SWAPS_TOKEN_OBJECT, ...tokensTopAssets.slice(0, MAX_TOP_ASSETS)]} + initialTokens={[ + swapsUtils.getNativeSwapsToken(chainId), + ...tokensTopAssets.slice(0, MAX_TOP_ASSETS) + ]} onItemPress={handleDestinationTokenPress} excludeAddresses={[sourceToken?.address]} /> - {Boolean(destinationToken) && !isSwapsETH(destinationToken) ? ( + {Boolean(destinationToken) && !isSwapsNativeAsset(destinationToken) ? ( destinationTokenHasEnoughOcurrances ? ( - + {strings('swaps.verified_on_sources', { sources: destinationToken.occurances })} {` ${strings('swaps.verify_on')} `} - - Etherscan - + {explorer.isValid ? ( + + {explorer.name} + + ) : ( + strings('swaps.a_block_explorer') + )} . @@ -548,7 +564,7 @@ function SwapsAmountView({ onInfoPress={toggleTokenVerificationModal} > {textStyle => ( - + {strings('swaps.only_verified_on', { symbol: destinationToken.symbol, @@ -557,9 +573,13 @@ function SwapsAmountView({ {`${strings('swaps.verify_address_on')} `} - - Etherscan - + {explorer.isValid ? ( + + {explorer.name} + + ) : ( + strings('swaps.a_block_explorer') + )} . @@ -617,9 +637,13 @@ function SwapsAmountView({ {strings('swaps.token_multiple')} {` ${strings('swaps.token_check')} `} - - Etherscan - + {explorer.isValid ? ( + + {explorer.name} + + ) : ( + strings('swaps.a_block_explorer') + )} {` ${strings('swaps.token_to_verify')}`} } @@ -672,6 +696,18 @@ SwapsAmountView.propTypes = { * Function to set hasOnboarded */ setHasOnboarded: PropTypes.func, + /** + * Current Network provider + */ + provider: PropTypes.object, + /** + * Chain Id + */ + chainId: PropTypes.string, + /** + * Frequent RPC list from PreferencesController + */ + frequentRpcList: PropTypes.array, /** * Function to set liveness */ @@ -686,6 +722,9 @@ const mapStateToProps = state => ({ conversionRate: state.engine.backgroundState.CurrencyRateController.conversionRate, tokenExchangeRates: state.engine.backgroundState.TokenRatesController.contractExchangeRates, currentCurrency: state.engine.backgroundState.CurrencyRateController.currentCurrency, + provider: state.engine.backgroundState.NetworkController.provider, + frequentRpcList: state.engine.backgroundState.PreferencesController.frequentRpcList, + chainId: state.engine.backgroundState.NetworkController.provider.chainId, tokensWithBalance: swapsTokensWithBalanceSelector(state), tokensTopAssets: swapsTopAssetsSelector(state), userHasOnboarded: swapsHasOnboardedSelector(state) @@ -693,7 +732,7 @@ const mapStateToProps = state => ({ const mapDispatchToProps = dispatch => ({ setHasOnboarded: hasOnboarded => dispatch(setSwapsHasOnboarded(hasOnboarded)), - setLiveness: liveness => dispatch(setSwapsLiveness(liveness)) + setLiveness: (liveness, chainId) => dispatch(setSwapsLiveness(liveness, chainId)) }); export default connect( diff --git a/app/components/UI/Swaps/utils/index.js b/app/components/UI/Swaps/utils/index.js index 31bf2a6f10c..46baab4f9fe 100644 --- a/app/components/UI/Swaps/utils/index.js +++ b/app/components/UI/Swaps/utils/index.js @@ -1,10 +1,25 @@ import { useMemo } from 'react'; import BigNumber from 'bignumber.js'; -import { swapsUtils } from '@estebanmino/controllers'; +import { swapsUtils } from '@metamask/swaps-controller'; import { strings } from '../../../../../locales/i18n'; +import AppConstants from '../../../../core/AppConstants'; -export function isSwapsETH(token) { - return Boolean(token) && token?.address === swapsUtils.ETH_SWAPS_TOKEN_ADDRESS; +const { ETH_CHAIN_ID, BSC_CHAIN_ID, SWAPS_TESTNET_CHAIN_ID } = swapsUtils; + +const allowedChainIds = [ETH_CHAIN_ID, BSC_CHAIN_ID]; + +export function isSwapsAllowed(chainId) { + if (!AppConstants.SWAPS.ACTIVE) { + return false; + } + if (!AppConstants.SWAPS.ONLY_MAINNET) { + allowedChainIds.push(SWAPS_TESTNET_CHAIN_ID); + } + return allowedChainIds.includes(chainId); +} + +export function isSwapsNativeAsset(token) { + return Boolean(token) && token?.address === swapsUtils.NATIVE_SWAPS_TOKEN_ADDRESS; } /** diff --git a/app/components/UI/Swaps/utils/useBalance.js b/app/components/UI/Swaps/utils/useBalance.js index 4d4e09bfee1..d006be26a8f 100644 --- a/app/components/UI/Swaps/utils/useBalance.js +++ b/app/components/UI/Swaps/utils/useBalance.js @@ -1,6 +1,6 @@ -import { swapsUtils } from '@estebanmino/controllers'; import { useMemo } from 'react'; import numberToBN from 'number-to-bn'; +import { isSwapsNativeAsset } from '.'; import { renderFromTokenMinimalUnit, renderFromWei } from '../../../../util/number'; import { safeToChecksumAddress } from '../../../../util/address'; @@ -9,7 +9,7 @@ function useBalance(accounts, balances, selectedAddress, sourceToken, { asUnits if (!sourceToken) { return null; } - if (sourceToken.address === swapsUtils.ETH_SWAPS_TOKEN_ADDRESS) { + if (isSwapsNativeAsset(sourceToken)) { if (asUnits) { // Controller stores balances in hex for ETH return numberToBN((accounts[selectedAddress] && accounts[selectedAddress].balance) || 0); diff --git a/app/components/UI/Swaps/utils/useBlockExplorer.js b/app/components/UI/Swaps/utils/useBlockExplorer.js new file mode 100644 index 00000000000..193c7fb1a8b --- /dev/null +++ b/app/components/UI/Swaps/utils/useBlockExplorer.js @@ -0,0 +1,76 @@ +import { useCallback, useEffect, useState } from 'react'; +import etherscanLink from '@metamask/etherscan-link'; +import { RPC } from '../../../../constants/network'; +import { findBlockExplorerForRpc, getBlockExplorerName } from '../../../../util/networks'; +import { strings } from '../../../../../locales/i18n'; + +function useBlockExplorer(provider, frequentRpcList) { + const [explorer, setExplorer] = useState({ name: '', value: null, isValid: false, isRPC: false }); + + useEffect(() => { + if (provider.type === RPC) { + try { + const blockExplorer = findBlockExplorerForRpc(provider.rpcTarget, frequentRpcList); + if (!blockExplorer) { + throw new Error('No block explorer url'); + } + const url = new URL(blockExplorer); + if (!['http:', 'https:'].includes(url.protocol)) { + throw new Error('Block explorer URL is not a valid http(s) protocol'); + } + + const name = getBlockExplorerName(blockExplorer) || strings('swaps.block_explorer'); + setExplorer({ name, value: blockExplorer, isValid: true, isRPC: true }); + } catch { + setExplorer({ name: '', value: null, isValid: false, isRPC: false }); + } + } else { + setExplorer({ name: 'Etherscan', value: provider.chainId, isValid: true, isRPC: false }); + } + }, [frequentRpcList, provider]); + + const tx = useCallback( + hash => { + if (!explorer.isValid) { + return ''; + } + + const create = explorer.isRPC ? etherscanLink.createCustomExplorerLink : etherscanLink.createExplorerLink; + return create(hash, explorer.value); + }, + [explorer] + ); + const account = useCallback( + address => { + if (!explorer.isValid) { + return ''; + } + + const create = explorer.isRPC ? etherscanLink.createCustomAccountLink : etherscanLink.createAccountLink; + return create(address, explorer.value); + }, + [explorer] + ); + const token = useCallback( + address => { + if (!explorer.isValid) { + return ''; + } + + const create = explorer.isRPC + ? etherscanLink.createCustomTokenTrackerLink + : etherscanLink.createTokenTrackerLink; + return create(address, explorer.value); + }, + [explorer] + ); + + return { + ...explorer, + tx, + account, + token + }; +} + +export default useBlockExplorer; diff --git a/app/components/UI/SwitchCustomNetwork/__snapshots__/index.test.js.snap b/app/components/UI/SwitchCustomNetwork/__snapshots__/index.test.js.snap index cab6e467afd..b1c7d1a29d4 100644 --- a/app/components/UI/SwitchCustomNetwork/__snapshots__/index.test.js.snap +++ b/app/components/UI/SwitchCustomNetwork/__snapshots__/index.test.js.snap @@ -24,6 +24,7 @@ exports[`SwitchCustomNetwork should render correctly 1`] = ` modal={false} noMargin={true} primary={true} + red={false} reset={false} right={false} small={false} @@ -53,6 +54,7 @@ exports[`SwitchCustomNetwork should render correctly 1`] = ` modal={false} noMargin={true} primary={true} + red={false} reset={false} right={false} small={false} @@ -79,6 +81,7 @@ exports[`SwitchCustomNetwork should render correctly 1`] = ` link={false} modal={false} primary={false} + red={false} reset={false} right={false} small={false} @@ -97,6 +100,7 @@ exports[`SwitchCustomNetwork should render correctly 1`] = ` modal={false} noMargin={true} primary={true} + red={false} reset={false} right={false} small={false} @@ -117,6 +121,7 @@ exports[`SwitchCustomNetwork should render correctly 1`] = ` modal={false} noMargin={true} primary={false} + red={false} reset={false} right={false} small={false} diff --git a/app/components/UI/Tokens/__snapshots__/index.test.js.snap b/app/components/UI/Tokens/__snapshots__/index.test.js.snap index b2c6ecffe93..08ad2070218 100644 --- a/app/components/UI/Tokens/__snapshots__/index.test.js.snap +++ b/app/components/UI/Tokens/__snapshots__/index.test.js.snap @@ -1,5 +1,219 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Tokens should hide zero balance tokens when setting is on 1`] = ` + + + + + + + 0 ETH + + + + + + + + < 0.00001 BAT + + + + + + Buy ETH + + + + + + + ADD TOKENS + + + + + + +`; + exports[`Tokens should render correctly 1`] = ` `; + +exports[`Tokens should show all balance tokens when hideZeroBalanceTokens setting is off 1`] = ` + + + + + + + 0 ETH + + + + + + + + < 0.00001 BAT + + + + + + + + 0 LINK + + + + + + Buy ETH + + + + + + + ADD TOKENS + + + + + + +`; diff --git a/app/components/UI/Tokens/index.js b/app/components/UI/Tokens/index.js index cd1f46b10d5..7903889d0e5 100644 --- a/app/components/UI/Tokens/index.js +++ b/app/components/UI/Tokens/index.js @@ -17,6 +17,7 @@ import { ANALYTICS_EVENT_OPTS } from '../../../util/analytics'; import StyledButton from '../StyledButton'; import { allowedToBuy } from '../FiatOrders'; import NetworkMainAssetLogo from '../NetworkMainAssetLogo'; +import { isMainNet } from '../../../util/networks'; const styles = StyleSheet.create({ wrapper: { @@ -135,7 +136,11 @@ class Tokens extends PureComponent { /** * Chain id */ - chainId: PropTypes.string + chainId: PropTypes.string, + /** + * A bool that represents if the user wants to hide zero balance token + */ + hideZeroBalanceTokens: PropTypes.bool }; actionSheet = null; @@ -162,14 +167,23 @@ class Tokens extends PureComponent { ); renderItem = asset => { - const { conversionRate, currentCurrency, tokenBalances, tokenExchangeRates, primaryCurrency } = this.props; + const { + chainId, + conversionRate, + currentCurrency, + tokenBalances, + tokenExchangeRates, + primaryCurrency + } = this.props; const itemAddress = safeToChecksumAddress(asset.address); const logo = asset.logo || ((contractMap[itemAddress] && contractMap[itemAddress].logo) || undefined); const exchangeRate = itemAddress in tokenExchangeRates ? tokenExchangeRates[itemAddress] : undefined; const balance = asset.balance || (itemAddress in tokenBalances ? renderFromTokenMinimalUnit(tokenBalances[itemAddress], asset.decimals) : 0); - const balanceFiat = asset.balanceFiat || balanceToFiat(balance, conversionRate, exchangeRate, currentCurrency); + const balanceFiat = isMainNet(chainId) + ? asset.balanceFiat || balanceToFiat(balance, conversionRate, exchangeRate, currentCurrency) + : null; const balanceValue = `${balance} ${asset.symbol}`; // render balances according to primary currency @@ -232,7 +246,7 @@ class Tokens extends PureComponent { const hasTokensBalance = hasTokens && tokens.some( - token => !token.isETH && tokenBalances[token.address] && !tokenBalances[token.address].isZero() + token => !token.isETH && tokenBalances[token.address] && !tokenBalances[token.address]?.isZero?.() ); return ( @@ -248,11 +262,18 @@ class Tokens extends PureComponent { } renderList() { - const { tokens } = this.props; + const { tokens, hideZeroBalanceTokens, tokenBalances } = this.props; + const tokensToDisplay = hideZeroBalanceTokens + ? tokens.filter(token => { + const { address, isETH } = token; + return (tokenBalances[address] && !tokenBalances[address]?.isZero?.()) || isETH; + // eslint-disable-next-line no-mixed-spaces-and-tabs + }) + : tokens; return ( - {tokens.map(item => this.renderItem(item))} + {tokensToDisplay.map(item => this.renderItem(item))} {this.renderBuyEth()} {this.renderFooter()} @@ -307,7 +328,8 @@ const mapStateToProps = state => ({ conversionRate: state.engine.backgroundState.CurrencyRateController.conversionRate, primaryCurrency: state.settings.primaryCurrency, tokenBalances: state.engine.backgroundState.TokenBalancesController.contractBalances, - tokenExchangeRates: state.engine.backgroundState.TokenRatesController.contractExchangeRates + tokenExchangeRates: state.engine.backgroundState.TokenRatesController.contractExchangeRates, + hideZeroBalanceTokens: state.settings.hideZeroBalanceTokens }); export default connect(mapStateToProps)(Tokens); diff --git a/app/components/UI/Tokens/index.test.js b/app/components/UI/Tokens/index.test.js index 6865c592143..9ede7381491 100644 --- a/app/components/UI/Tokens/index.test.js +++ b/app/components/UI/Tokens/index.test.js @@ -2,6 +2,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import Tokens from './'; import configureMockStore from 'redux-mock-store'; +import { BN } from 'ethereumjs-util'; const mockStore = configureMockStore(); @@ -38,4 +39,88 @@ describe('Tokens', () => { }); expect(wrapper.dive()).toMatchSnapshot(); }); + + it('should hide zero balance tokens when setting is on', () => { + const initialState = { + engine: { + backgroundState: { + AssetsController: { + tokens: [ + { symbol: 'ETH', address: '0x0', decimals: 18, isETH: true }, + { symbol: 'BAT', address: '0x01', decimals: 18 }, + { symbol: 'LINK', address: '0x02', decimals: 18 } + ] + }, + TokenRatesController: { + contractExchangeRates: {} + }, + CurrencyRateController: { + currentCurrency: 'USD', + conversionRate: 1 + }, + TokenBalancesController: { + contractBalances: { + '0x01': new BN(2), + '0x02': new BN(0) + } + }, + NetworkController: { + provider: { chainId: '1' } + } + } + }, + settings: { + primaryCurrency: 'usd', + hideZeroBalanceTokens: true + } + }; + + const wrapper = shallow(, { + context: { store: mockStore(initialState) } + }); + // ETH and BAT should display + expect(wrapper.dive()).toMatchSnapshot(); + }); + + it('should show all balance tokens when hideZeroBalanceTokens setting is off', () => { + const initialState = { + engine: { + backgroundState: { + AssetsController: { + tokens: [ + { symbol: 'ETH', address: '0x0', decimals: 18, isETH: true }, + { symbol: 'BAT', address: '0x01', decimals: 18 }, + { symbol: 'LINK', address: '0x02', decimals: 18 } + ] + }, + TokenRatesController: { + contractExchangeRates: {} + }, + CurrencyRateController: { + currentCurrency: 'USD', + conversionRate: 1 + }, + TokenBalancesController: { + contractBalances: { + '0x01': new BN(2), + '0x02': new BN(0) + } + }, + NetworkController: { + provider: { chainId: '1' } + } + } + }, + settings: { + primaryCurrency: 'usd', + hideZeroBalanceTokens: false + } + }; + + const wrapper = shallow(, { + context: { store: mockStore(initialState) } + }); + // All three should display + expect(wrapper.dive()).toMatchSnapshot(); + }); }); diff --git a/app/components/UI/TransactionElement/TransactionDetails/__snapshots__/index.test.js.snap b/app/components/UI/TransactionElement/TransactionDetails/__snapshots__/index.test.js.snap index 554bf0e3fc0..fae6798ed89 100644 --- a/app/components/UI/TransactionElement/TransactionDetails/__snapshots__/index.test.js.snap +++ b/app/components/UI/TransactionElement/TransactionDetails/__snapshots__/index.test.js.snap @@ -1,122 +1,37 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`TransactionDetails should render correctly 1`] = ` - - - - - Status - - - - - - Date - - - [missing "en.date.months.NaN" translation] NaN at NaN:NaNam - - - - - - - From - - - - - - - - To - - - - - - - - - - + } + transactionDetails={ + Object { + "renderFrom": "0x0", + "renderGas": "21000", + "renderGasPrice": "2", + "renderTo": "0x1", + "renderTotalValue": "2 TKN / 0.001 ETH", + "renderTotalValueFiat": "", + "renderValue": "2 TKN", + "transactionHash": "0x2", + } + } + transactionObject={ + Object { + "networkID": "1", + "status": "confirmed", + "transaction": Object { + "nonce": "", + }, + } + } +/> `; diff --git a/app/components/UI/TransactionElement/TransactionDetails/index.js b/app/components/UI/TransactionElement/TransactionDetails/index.js index 647bb168250..4a704a823b7 100644 --- a/app/components/UI/TransactionElement/TransactionDetails/index.js +++ b/app/components/UI/TransactionElement/TransactionDetails/index.js @@ -3,7 +3,12 @@ import PropTypes from 'prop-types'; import { TouchableOpacity, StyleSheet, View } from 'react-native'; import { colors, fontStyles } from '../../../../styles/common'; import { strings } from '../../../../../locales/i18n'; -import { getNetworkTypeById, findBlockExplorerForRpc, getBlockExplorerName } from '../../../../util/networks'; +import { + getNetworkTypeById, + findBlockExplorerForRpc, + getBlockExplorerName, + isMainNet +} from '../../../../util/networks'; import { getEtherscanTransactionUrl, getEtherscanBaseUrl } from '../../../../util/etherscan'; import Logger from '../../../../util/Logger'; import { connect } from 'react-redux'; @@ -16,6 +21,7 @@ import StatusText from '../../../Base/StatusText'; import Text from '../../../Base/Text'; import DetailsModal from '../../../Base/DetailsModal'; import { RPC } from '../../../../constants/network'; +import { withNavigation } from 'react-navigation'; const styles = StyleSheet.create({ viewOnEtherscan: { @@ -61,6 +67,10 @@ class TransactionDetails extends PureComponent { /* navigation object required to push new views */ navigation: PropTypes.object, + /** + * Chain Id + */ + chainId: PropTypes.string, /** * Object representing the selected the selected network */ @@ -166,6 +176,7 @@ class TransactionDetails extends PureComponent { render = () => { const { + chainId, transactionDetails, transactionObject, transactionObject: { @@ -223,7 +234,9 @@ class TransactionDetails extends PureComponent { amount={transactionDetails.summaryAmount} fee={transactionDetails.summaryFee} totalAmount={transactionDetails.summaryTotalAmount} - secondaryTotalAmount={transactionDetails.summarySecondaryTotalAmount} + secondaryTotalAmount={ + isMainNet(chainId) ? transactionDetails.summarySecondaryTotalAmount : undefined + } gasEstimationReady transactionType={transactionDetails.transactionType} /> @@ -247,6 +260,7 @@ class TransactionDetails extends PureComponent { const mapStateToProps = state => ({ network: state.engine.backgroundState.NetworkController, + chainId: state.engine.backgroundState.NetworkController.provider.chainId, frequentRpcList: state.engine.backgroundState.PreferencesController.frequentRpcList }); -export default connect(mapStateToProps)(TransactionDetails); +export default connect(mapStateToProps)(withNavigation(TransactionDetails)); diff --git a/app/components/UI/TransactionElement/TransactionDetails/index.test.js b/app/components/UI/TransactionElement/TransactionDetails/index.test.js index b644919a622..a198e801c93 100644 --- a/app/components/UI/TransactionElement/TransactionDetails/index.test.js +++ b/app/components/UI/TransactionElement/TransactionDetails/index.test.js @@ -52,6 +52,6 @@ describe('TransactionDetails', () => { context: { store: mockStore(initialState) } } ); - expect(wrapper.dive()).toMatchSnapshot(); + expect(wrapper).toMatchSnapshot(); }); }); diff --git a/app/components/UI/TransactionElement/index.js b/app/components/UI/TransactionElement/index.js index c08e11a7506..c8bee8401a4 100644 --- a/app/components/UI/TransactionElement/index.js +++ b/app/components/UI/TransactionElement/index.js @@ -15,6 +15,7 @@ import { TRANSACTION_TYPES } from '../../../util/transactions'; import ListItem from '../../Base/ListItem'; import StatusText from '../../Base/StatusText'; import DetailsModal from '../../Base/DetailsModal'; +import { isMainNet } from '../../../util/networks'; import { WalletDevice } from '@metamask/controllers/'; const styles = StyleSheet.create({ @@ -81,10 +82,6 @@ const transactionIconReceivedFailed = require('../../../images/transaction-icons class TransactionElement extends PureComponent { static propTypes = { assetSymbol: PropTypes.string, - /** - /* navigation object required to push new views - */ - navigation: PropTypes.object, /** * Asset object (in this case ERC721 token) */ @@ -114,7 +111,11 @@ class TransactionElement extends PureComponent { */ onCancelAction: PropTypes.func, swapsTransactions: PropTypes.object, - swapsTokens: PropTypes.arrayOf(PropTypes.object) + swapsTokens: PropTypes.arrayOf(PropTypes.object), + /** + * Chain Id + */ + chainId: PropTypes.string }; state = { @@ -173,7 +174,7 @@ class TransactionElement extends PureComponent { 'transactions.from_device_label' // eslint-disable-next-line no-mixed-spaces-and-tabs )}` - : `${toDateFormat(tx.time)} + : `${toDateFormat(tx.time)} ` }`; }; @@ -184,7 +185,8 @@ class TransactionElement extends PureComponent { */ renderImportTime = () => { const { tx, identities, selectedAddress } = this.props; - if (tx.insertImportTime && identities[selectedAddress].importTime) { + const accountImportTime = identities[selectedAddress]?.importTime; + if (tx.insertImportTime && accountImportTime) { return ( <> @@ -192,7 +194,7 @@ class TransactionElement extends PureComponent { {`${strings('transactions.import_wallet_row')} `} - {toDateFormat(identities[selectedAddress].importTime)} + {toDateFormat(accountImportTime)} ); @@ -233,12 +235,13 @@ class TransactionElement extends PureComponent { renderTxElement = transactionElement => { const { identities, + chainId, selectedAddress, tx: { time, status } } = this.props; const { value, fiatValue = false, actionKey } = transactionElement; const renderTxActions = status === 'submitted' || status === 'approved'; - const accountImportTime = identities[selectedAddress].importTime; + const accountImportTime = identities[selectedAddress]?.importTime; return ( <> {accountImportTime > time && this.renderImportTime()} @@ -253,7 +256,7 @@ class TransactionElement extends PureComponent { {Boolean(value) && ( {value} - {fiatValue} + {isMainNet(chainId) && {fiatValue}} )} @@ -341,7 +344,6 @@ class TransactionElement extends PureComponent { @@ -371,11 +373,12 @@ class TransactionElement extends PureComponent { } const mapStateToProps = state => ({ + ticker: state.engine.backgroundState.NetworkController.provider.ticker, + chainId: state.engine.backgroundState.NetworkController.provider.chainId, identities: state.engine.backgroundState.PreferencesController.identities, primaryCurrency: state.settings.primaryCurrency, selectedAddress: state.engine.backgroundState.PreferencesController.selectedAddress, swapsTransactions: state.engine.backgroundState.TransactionController.swapsTransactions || {}, - swapsTokens: state.engine.backgroundState.SwapsController.tokens, - ticker: state.engine.backgroundState.NetworkController.provider.ticker + swapsTokens: state.engine.backgroundState.SwapsController.tokens }); export default connect(mapStateToProps)(TransactionElement); diff --git a/app/components/UI/TransactionElement/utils.js b/app/components/UI/TransactionElement/utils.js index 88a08654d9f..8c7753326cc 100644 --- a/app/components/UI/TransactionElement/utils.js +++ b/app/components/UI/TransactionElement/utils.js @@ -23,9 +23,10 @@ import { } from '../../../util/transactions'; import contractMap from '@metamask/contract-metadata'; import { toChecksumAddress } from 'ethereumjs-util'; -import { swapsUtils } from '@estebanmino/controllers'; +import { swapsUtils } from '@metamask/swaps-controller'; +import { isSwapsNativeAsset } from '../Swaps/utils'; -const { ETH_SWAPS_TOKEN_ADDRESS, SWAPS_CONTRACT_ADDRESS } = swapsUtils; +const { getSwapsContractAddress } = swapsUtils; function calculateTotalGas(gas, gasPrice) { const gasBN = hexToBN(gas); @@ -604,16 +605,14 @@ function decodeSwapsTx(args) { ); } - const sourceExchangeRate = - sourceToken.address === ETH_SWAPS_TOKEN_ADDRESS - ? 1 - : contractExchangeRates[safeToChecksumAddress(sourceToken.address)]; + const sourceExchangeRate = isSwapsNativeAsset(sourceToken) + ? 1 + : contractExchangeRates[safeToChecksumAddress(sourceToken.address)]; const renderSourceTokenFiatNumber = balanceToFiatNumber(decimalSourceAmount, conversionRate, sourceExchangeRate); - const destinationExchangeRate = - destinationToken.address === ETH_SWAPS_TOKEN_ADDRESS - ? 1 - : contractExchangeRates[safeToChecksumAddress(destinationToken.address)]; + const destinationExchangeRate = isSwapsNativeAsset(destinationToken) + ? 1 + : contractExchangeRates[safeToChecksumAddress(destinationToken.address)]; const renderDestinationTokenFiatNumber = balanceToFiatNumber( decimalDestinationAmount, conversionRate, @@ -682,13 +681,13 @@ function decodeSwapsTx(args) { * currentCurrency, exchangeRate, contractExchangeRates, collectibleContracts, tokens */ export default async function decodeTransaction(args) { - const { tx, selectedAddress, ticker, swapsTransactions = {} } = args; + const { tx, selectedAddress, ticker, chainId, swapsTransactions = {} } = args; const { isTransfer } = tx || {}; - const actionKey = await getActionKey(tx, selectedAddress, ticker); + const actionKey = await getActionKey(tx, selectedAddress, ticker, chainId); let transactionElement, transactionDetails; - if (tx.transaction.to === SWAPS_CONTRACT_ADDRESS || swapsTransactions[tx.id]) { + if (tx.transaction.to?.toLowerCase() === getSwapsContractAddress(chainId) || swapsTransactions[tx.id]) { const [transactionElement, transactionDetails] = decodeSwapsTx({ ...args, actionKey }); if (transactionElement && transactionDetails) return [transactionElement, transactionDetails]; } diff --git a/app/components/UI/TransactionReview/TransactionReviewFeeCard/__snapshots__/index.test.js.snap b/app/components/UI/TransactionReview/TransactionReviewFeeCard/__snapshots__/index.test.js.snap index c4aede01781..e0a24358079 100644 --- a/app/components/UI/TransactionReview/TransactionReviewFeeCard/__snapshots__/index.test.js.snap +++ b/app/components/UI/TransactionReview/TransactionReviewFeeCard/__snapshots__/index.test.js.snap @@ -20,6 +20,7 @@ exports[`TransactionReviewFeeCard should render correctly 1`] = ` link={false} modal={false} primary={true} + red={false} reset={false} right={false} small={false} @@ -39,6 +40,7 @@ exports[`TransactionReviewFeeCard should render correctly 1`] = ` link={false} modal={false} primary={true} + red={false} reset={false} right={false} small={false} @@ -59,6 +61,7 @@ exports[`TransactionReviewFeeCard should render correctly 1`] = ` link={false} modal={false} primary={true} + red={false} reset={false} right={false} small={false} @@ -81,6 +84,7 @@ exports[`TransactionReviewFeeCard should render correctly 1`] = ` link={true} modal={false} primary={false} + red={false} reset={false} right={false} small={false} @@ -121,6 +125,7 @@ exports[`TransactionReviewFeeCard should render correctly 1`] = ` link={false} modal={false} primary={true} + red={false} reset={false} right={false} small={false} diff --git a/app/components/UI/TransactionReview/index.js b/app/components/UI/TransactionReview/index.js index c6d6461ef81..e8c7520ee74 100644 --- a/app/components/UI/TransactionReview/index.js +++ b/app/components/UI/TransactionReview/index.js @@ -124,6 +124,10 @@ class TransactionReview extends PureComponent { * Current provider ticker */ ticker: PropTypes.string, + /** + * Chain id + */ + chainId: PropTypes.string, /** * ETH or fiat, depending on user setting */ @@ -174,14 +178,15 @@ class TransactionReview extends PureComponent { validate, transaction, transaction: { data, to }, - tokens + tokens, + chainId } = this.props; let { showHexData } = this.props; let assetAmount, conversionRate, fiatValue; showHexData = showHexData || data; const approveTransaction = data && data.substr(0, 10) === APPROVE_FUNCTION_SIGNATURE; const error = validate && (await validate()); - const actionKey = await getTransactionReviewActionKey(transaction); + const actionKey = await getTransactionReviewActionKey(transaction, chainId); if (approveTransaction) { let contract = contractMap[safeToChecksumAddress(to)]; if (!contract) { @@ -358,6 +363,7 @@ const mapStateToProps = state => ({ contractExchangeRates: state.engine.backgroundState.TokenRatesController.contractExchangeRates, conversionRate: state.engine.backgroundState.CurrencyRateController.conversionRate, ticker: state.engine.backgroundState.NetworkController.provider.ticker, + chainId: state.engine.backgroundState.NetworkController.provider.chainId, showHexData: state.settings.showHexData, transaction: getNormalizedTxState(state), browser: state.browser, diff --git a/app/components/Views/AccountBackupStep1/__snapshots__/index.test.js.snap b/app/components/Views/AccountBackupStep1/__snapshots__/index.test.js.snap index 0f5b8221dc3..244e8059602 100644 --- a/app/components/Views/AccountBackupStep1/__snapshots__/index.test.js.snap +++ b/app/components/Views/AccountBackupStep1/__snapshots__/index.test.js.snap @@ -40,7 +40,7 @@ exports[`AccountBackupStep1 should render correctly 1`] = ` Array [ "Create password", "Secure wallet", - "Confirm seed phrase", + "Confirm Secret Recovery phrase", ] } /> @@ -68,21 +68,7 @@ exports[`AccountBackupStep1 should render correctly 1`] = ` > Secure your wallet - + - seed phrase + Secret Recovery phrase in a place you trust. diff --git a/app/components/Views/AccountBackupStep1/index.js b/app/components/Views/AccountBackupStep1/index.js index a163643acd8..da71f756c23 100644 --- a/app/components/Views/AccountBackupStep1/index.js +++ b/app/components/Views/AccountBackupStep1/index.js @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react'; -import { ScrollView, TouchableOpacity, Text, View, SafeAreaView, StyleSheet, Image, BackHandler } from 'react-native'; +import { ScrollView, TouchableOpacity, Text, View, SafeAreaView, StyleSheet, BackHandler } from 'react-native'; import PropTypes from 'prop-types'; import AsyncStorage from '@react-native-community/async-storage'; import { colors, fontStyles } from '../../../styles/common'; @@ -15,8 +15,7 @@ import Engine from '../../../core/Engine'; import { ONBOARDING_WIZARD, METRICS_OPT_IN } from '../../../constants/storage'; import { CHOOSE_PASSWORD_STEPS } from '../../../constants/onboarding'; import SkipAccountSecurityModal from '../../UI/SkipAccountSecurityModal'; - -const explain_backup_seedphrase = require('../../../images/explain-backup-seedphrase.png'); // eslint-disable-line +import SeedPhraseVideo from '../../UI/SeedPhraseVideo'; const styles = StyleSheet.create({ mainWrapper: { @@ -96,10 +95,6 @@ const styles = StyleSheet.create({ }, ctaContainer: { marginBottom: 30 - }, - image: { - width: scaling.scale(138), - height: scaling.scale(162) } }); @@ -186,12 +181,7 @@ const AccountBackupStep1 = props => { {strings('account_backup_step_1.title')} - + {strings('account_backup_step_1.info_text_1_1')}{' '} diff --git a/app/components/Views/AccountBackupStep1B/__snapshots__/index.test.js.snap b/app/components/Views/AccountBackupStep1B/__snapshots__/index.test.js.snap index 236bb92852d..462498c3646 100644 --- a/app/components/Views/AccountBackupStep1B/__snapshots__/index.test.js.snap +++ b/app/components/Views/AccountBackupStep1B/__snapshots__/index.test.js.snap @@ -40,7 +40,7 @@ exports[`AccountBackupStep1B should render correctly 1`] = ` Array [ "Create password", "Secure wallet", - "Confirm seed phrase", + "Confirm Secret Recovery phrase", ] } /> @@ -107,7 +107,7 @@ exports[`AccountBackupStep1B should render correctly 1`] = ` } } > - seed phrase. + Secret Recovery phrase. @@ -189,7 +189,7 @@ exports[`AccountBackupStep1B should render correctly 1`] = ` } } > - Write down your seed phrase on a piece of paper and store in a safe place. + Write down your Secret Recovery phrase on a piece of paper and store in a safe place. - Don’t risk losing your funds. Protect your wallet by saving your seed phrase in a place you trust. + Don’t risk losing your funds. Protect your wallet by saving your Secret Recovery phrase in a place you trust. getNetworkNavbarOptions('add_asset.title', true, navigation); state = { @@ -45,7 +47,11 @@ export default class AddAsset extends PureComponent { /** /* navigation object required to push new views */ - navigation: PropTypes.object + navigation: PropTypes.object, + /** + * Chain id + */ + chainId: PropTypes.string }; renderTabBar() { @@ -74,11 +80,13 @@ export default class AddAsset extends PureComponent { {assetType === 'token' ? ( - + {NetworksChainId.mainnet === this.props.chainId && ( + + )} ({ + chainId: state.engine.backgroundState.NetworkController.provider.chainId +}); + +export default connect(mapStateToProps)(AddAsset); diff --git a/app/components/Views/AddAsset/index.test.js b/app/components/Views/AddAsset/index.test.js index 310a5389180..a3e33019bdd 100644 --- a/app/components/Views/AddAsset/index.test.js +++ b/app/components/Views/AddAsset/index.test.js @@ -1,10 +1,27 @@ import React from 'react'; +import configureMockStore from 'redux-mock-store'; import { shallow } from 'enzyme'; import AddAsset from './'; +const mockStore = configureMockStore(); + describe('AddAsset', () => { it('should render correctly', () => { - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); + const initialState = { + engine: { + backgroundState: { + NetworkController: { + provider: { + chainId: '1' + } + } + } + } + }; + + const wrapper = shallow(, { + context: { store: mockStore(initialState) } + }); + expect(wrapper.dive()).toMatchSnapshot(); }); }); diff --git a/app/components/Views/Asset/index.js b/app/components/Views/Asset/index.js index f88c527139c..7fed702bfcf 100644 --- a/app/components/Views/Asset/index.js +++ b/app/components/Views/Asset/index.js @@ -2,13 +2,14 @@ import React, { PureComponent } from 'react'; import { ActivityIndicator, InteractionManager, View, StyleSheet } from 'react-native'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; +import { swapsUtils } from '@metamask/swaps-controller/'; + import { colors } from '../../../styles/common'; import AssetOverview from '../../UI/AssetOverview'; import Transactions from '../../UI/Transactions'; import { getNetworkNavbarOptions } from '../../UI/Navbar'; import Engine from '../../../core/Engine'; import { safeToChecksumAddress } from '../../../util/address'; -import { SWAPS_CONTRACT_ADDRESS } from '@estebanmino/controllers/dist/swaps/SwapsUtil'; import { addAccountTimeFlagFilter } from '../../../util/transactions'; const styles = StyleSheet.create({ @@ -168,7 +169,8 @@ class Asset extends PureComponent { if (isTransfer) return this.navAddress === transferInformation.contractAddress.toLowerCase(); if ( swapsTransactions[tx.id] && - (to?.toLowerCase() === SWAPS_CONTRACT_ADDRESS || to?.toLowerCase() === this.navAddress) + (to?.toLowerCase() === swapsUtils.getSwapsContractAddress(chainId) || + to?.toLowerCase() === this.navAddress) ) { const { destinationToken, sourceToken } = swapsTransactions[tx.id]; return destinationToken.address === this.navAddress || sourceToken.address === this.navAddress; diff --git a/app/components/Views/BrowserTab/__snapshots__/index.test.js.snap b/app/components/Views/BrowserTab/__snapshots__/index.test.js.snap index 20e5dcf04a1..be35fc8eb2d 100644 --- a/app/components/Views/BrowserTab/__snapshots__/index.test.js.snap +++ b/app/components/Views/BrowserTab/__snapshots__/index.test.js.snap @@ -181,7 +181,7 @@ exports[`Browser should render correctly 1`] = ` "marginTop": 7, } } - testID="cancel-url-button" + testID="ios-cancel-url-button" > { ensIgnoreList.push(hostname); return { url: fullUrl, reload: true }; } - Logger.error(err, 'Failed to resolve ENS name'); - Alert.alert(strings('browser.error'), strings('browser.failed_to_resolve_ens_name')); + if (err?.message?.startsWith('EnsIpfsResolver - no known ens-ipfs registry for chainId')) { + trackErrorAsAnalytics('Browser: Failed to resolve ENS name for chainId', err?.message); + } else { + Logger.error(err, 'Failed to resolve ENS name'); + } + + Alert.alert(strings('browser.failed_to_resolve_ens_name'), err.message); goBack(); } }, @@ -988,7 +994,7 @@ export const BrowserTab = props => { * Handle keyboard hide */ const keyboardDidHide = useCallback(() => { - if (!isTabActive()) return false; + if (!isTabActive() || isEmulatorSync()) return false; if (!fromHomepage.current) { if (showUrlModal) { hideUrlModal(); @@ -1413,12 +1419,17 @@ export const BrowserTab = props => { onPress={() => (!autocompleteValue ? setShowUrlModal(false) : setAutocompleteValue(''))} style={styles.iconCloseButton} > - + ) : ( {strings('browser.cancel')} diff --git a/app/components/Views/ChoosePassword/__snapshots__/index.test.js.snap b/app/components/Views/ChoosePassword/__snapshots__/index.test.js.snap index 9e928630fda..1e5887b6990 100644 --- a/app/components/Views/ChoosePassword/__snapshots__/index.test.js.snap +++ b/app/components/Views/ChoosePassword/__snapshots__/index.test.js.snap @@ -24,7 +24,7 @@ exports[`ChoosePassword should render correctly 1`] = ` Array [ "Create password", "Secure wallet", - "Confirm seed phrase", + "Confirm Secret Recovery phrase", ] } /> diff --git a/app/components/Views/ChoosePassword/index.js b/app/components/Views/ChoosePassword/index.js index 5601e44c747..731396c1a28 100644 --- a/app/components/Views/ChoosePassword/index.js +++ b/app/components/Views/ChoosePassword/index.js @@ -418,7 +418,7 @@ class ChoosePassword extends PureComponent { if (hdKeyring.accounts.includes(selectedAddress)) { PreferencesController.setSelectedAddress(selectedAddress); } else { - PreferencesController.setSelectedAddress(hdKeyring[0]); + PreferencesController.setSelectedAddress(hdKeyring.accounts[0]); } }; diff --git a/app/components/Views/Entry/index.js b/app/components/Views/Entry/index.js index e458b8f5827..a5c03710ad4 100644 --- a/app/components/Views/Entry/index.js +++ b/app/components/Views/Entry/index.js @@ -76,7 +76,7 @@ const Entry = props => { const animation = useRef(null); const animationName = useRef(null); - const opacity = new Animated.Value(1); + const opacity = useRef(new Animated.Value(1)).current; const onAnimationFinished = useCallback(() => { Animated.timing(opacity, { diff --git a/app/components/Views/ImportFromSeed/__snapshots__/index.test.js.snap b/app/components/Views/ImportFromSeed/__snapshots__/index.test.js.snap index 25e9974b730..d2e80f81121 100644 --- a/app/components/Views/ImportFromSeed/__snapshots__/index.test.js.snap +++ b/app/components/Views/ImportFromSeed/__snapshots__/index.test.js.snap @@ -75,7 +75,7 @@ exports[`ImportFromSeed should render correctly 1`] = ` } } > - Seed phrase + Secret recovery phrase - Imported accounts are viewable in your wallet but are not recoverable with your MetaMask seed phrase. + Imported accounts are viewable in your wallet but are not recoverable with your MetaMask Secret Recovery phrase. String(text).toLowerCase() === 'delete'; const deviceHeight = Device.getDeviceHeight(); @@ -182,13 +183,11 @@ const styles = StyleSheet.create({ } }); -/* TODO: we should have translation strings for these */ -const PASSCODE_NOT_SET_ERROR = 'Error: Passcode not set.'; -const WRONG_PASSWORD_ERROR = 'Error: Decrypt failed'; -const WRONG_PASSWORD_ERROR_ANDROID = 'Error: error:1e000065:Cipher functions:OPENSSL_internal:BAD_DECRYPT'; -const VAULT_ERROR = 'Error: Cannot unlock without a previous vault.'; -const CLEAN_VAULT_ERROR = - 'MetaMask encountered an error due to reaching a storage limit. The local data has been corrupted. Please reinstall MetaMask and restore with your seed phrase.'; +const PASSCODE_NOT_SET_ERROR = strings('login.passcode_not_set_error'); +const WRONG_PASSWORD_ERROR = strings('login.wrong_password_error'); +const WRONG_PASSWORD_ERROR_ANDROID = strings('login.wrong_password_error_android'); +const VAULT_ERROR = strings('login.vault_error'); +const CLEAN_VAULT_ERROR = strings('login.clean_vault_error'); /** * View where returning users can authenticate @@ -319,6 +318,10 @@ class Login extends PureComponent { error.toLowerCase() === WRONG_PASSWORD_ERROR_ANDROID.toLowerCase() ) { this.setState({ loading: false, error: strings('login.invalid_password') }); + + trackErrorAsAnalytics('Login: Invalid Password', error); + + return; } else if (error === PASSCODE_NOT_SET_ERROR) { Alert.alert( 'Security Alert', @@ -520,6 +523,7 @@ class Login extends PureComponent { {strings('login.password')} @@ -61,7 +61,7 @@ exports[`ManualBackupStep1 should render correctly 1`] = ` } } > - Write down your seed phrase + Write down your Secret Recovery phrase - This is your seed phrase. Write it down on a paper and keep it in a safe place. You'll be asked to re-enter this phrase (in order) on the next step. + This is your Secret Recovery phrase. Write it down on a paper and keep it in a safe place. You'll be asked to re-enter this phrase (in order) on the next step. - Tap to reveal your seed phrase + Tap to reveal your Secret Recovery phrase - Confirm seed phrase + Confirm Secret Recovery phrase { + if (confusableCollection.length) { + const texts = toAddressName.split('').map((char, index) => { + // if text has a confusable highlight it red + if (confusableCollection.includes(char)) { + // if the confusable is zero width, replace it with `?` + const replacement = hasZeroWidthPoints(char) ? '?' : char; + return ( + + {replacement} + + ); + } + return ( + + {char} + + ); + }); + return ( + + {texts} + + ); + } + return ( + + {toAddressName} + + ); +}; + +AddressName.propTypes = { + toAddressName: PropTypes.string, + confusableCollection: PropTypes.array +}; + export const AddressTo = props => { const { addressToReady, @@ -132,7 +179,9 @@ export const AddressTo = props => { onInputFocus, onSubmit, onInputBlur, - inputWidth + inputWidth, + confusableCollection, + displayExclamation } = props; return ( @@ -173,12 +222,18 @@ export const AddressTo = props => { + {displayExclamation && ( + + + + )} {toAddressName && ( - - {toAddressName} - + )} { diff --git a/app/components/Views/SendFlow/Amount/index.js b/app/components/Views/SendFlow/Amount/index.js index f2f3e27e5db..6f64eb6bf7d 100644 --- a/app/components/Views/SendFlow/Amount/index.js +++ b/app/components/Views/SendFlow/Amount/index.js @@ -52,6 +52,7 @@ import Analytics from '../../../../core/Analytics'; import { ANALYTICS_EVENT_OPTS } from '../../../../util/analytics'; import dismissKeyboard from 'react-native/Libraries/Utilities/dismissKeyboard'; import NetworkMainAssetLogo from '../../../UI/NetworkMainAssetLogo'; +import { isMainNet } from '../../../../util/networks'; const { hexToBN, BNToHex } = util; @@ -330,6 +331,10 @@ class Amount extends PureComponent { * An array that represents the user tokens */ tokens: PropTypes.array, + /** + * Chain Id + */ + chainId: PropTypes.string, /** * Current provider ticker */ @@ -664,7 +669,7 @@ class Amount extends PureComponent { }; onInputChange = (inputValue, selectedAsset, useMax) => { - const { contractExchangeRates, conversionRate, currentCurrency, ticker } = this.props; + const { contractExchangeRates, conversionRate, currentCurrency, chainId, ticker } = this.props; const { internalPrimaryCurrencyIsCrypto } = this.state; let inputValueConversion, renderableInputValueConversion, hasExchangeRate, comma; // Remove spaces from input @@ -678,7 +683,7 @@ class Amount extends PureComponent { const processedInputValue = isDecimal(inputValue) ? handleWeiNumber(inputValue) : '0'; selectedAsset = selectedAsset || this.props.selectedAsset; if (selectedAsset.isETH) { - hasExchangeRate = !!conversionRate; + hasExchangeRate = isMainNet(chainId) ? !!conversionRate : false; if (internalPrimaryCurrencyIsCrypto) { inputValueConversion = `${weiToFiatNumber(toWei(processedInputValue), conversionRate)}`; renderableInputValueConversion = `${weiToFiat( @@ -692,7 +697,7 @@ class Amount extends PureComponent { } } else { const exchangeRate = contractExchangeRates[selectedAsset.address]; - hasExchangeRate = !!exchangeRate; + hasExchangeRate = isMainNet(chainId) ? !!exchangeRate : false; // If !hasExchangeRate we have to handle crypto amount if (internalPrimaryCurrencyIsCrypto || !hasExchangeRate) { inputValueConversion = `${balanceToFiatNumber(processedInputValue, conversionRate, exchangeRate)}`; @@ -766,6 +771,7 @@ class Amount extends PureComponent { renderToken = (token, index) => { const { accounts, + chainId, selectedAddress, conversionRate, currentCurrency, @@ -776,11 +782,15 @@ class Amount extends PureComponent { const { address, decimals, symbol } = token; if (token.isETH) { balance = renderFromWei(accounts[selectedAddress].balance); - balanceFiat = weiToFiat(hexToBN(accounts[selectedAddress].balance), conversionRate, currentCurrency); + balanceFiat = isMainNet(chainId) + ? weiToFiat(hexToBN(accounts[selectedAddress].balance), conversionRate, currentCurrency) + : null; } else { balance = renderFromTokenMinimalUnit(contractBalances[address], decimals); const exchangeRate = contractExchangeRates[address]; - balanceFiat = balanceToFiat(balance, conversionRate, exchangeRate, currentCurrency); + balanceFiat = isMainNet(chainId) + ? balanceToFiat(balance, conversionRate, exchangeRate, currentCurrency) + : null; } return ( ({ providerType: state.engine.backgroundState.NetworkController.provider.type, primaryCurrency: state.settings.primaryCurrency, selectedAddress: state.engine.backgroundState.PreferencesController.selectedAddress, + chainId: state.engine.backgroundState.NetworkController.provider.chainId, ticker: state.engine.backgroundState.NetworkController.provider.ticker, tokens: state.engine.backgroundState.AssetsController.tokens, transactionState: ownProps.transaction || state.transaction, diff --git a/app/components/Views/SendFlow/Confirm/__snapshots__/index.test.js.snap b/app/components/Views/SendFlow/Confirm/__snapshots__/index.test.js.snap index c91387b3fb9..9ef917d8156 100644 --- a/app/components/Views/SendFlow/Confirm/__snapshots__/index.test.js.snap +++ b/app/components/Views/SendFlow/Confirm/__snapshots__/index.test.js.snap @@ -24,11 +24,40 @@ exports[`Confirm should render correctly 1`] = ` fromAccountAddress="0x1" onPressIcon={null} /> - + + + We have detected a confusable character in the ENS name. Check the ENS name to avoid a potential scam. + + } + isVisible={false} + title="Check the recipient address" + toggleModal={[Function]} + /> - balance */ @@ -262,6 +272,10 @@ class Confirm extends PureComponent { * Set transaction object to be sent */ prepareTransaction: PropTypes.func, + /** + * Chain Id + */ + chainId: PropTypes.string, /** * Network id */ @@ -309,6 +323,7 @@ class Confirm extends PureComponent { }; state = { + confusableCollection: [], gasSpeedSelected: 'average', gasEstimationReady: false, customGas: undefined, @@ -327,6 +342,7 @@ class Confirm extends PureComponent { transactionTotalAmountFiat: undefined, errorMessage: undefined, fromAccountModalVisible: false, + warningModalVisible: false, mode: REVIEW, over: false }; @@ -364,6 +380,18 @@ class Confirm extends PureComponent { } }; + handleConfusables = () => { + const { identities = undefined, transactionState } = this.props; + const { transactionToName = undefined } = transactionState; + const accountNames = (identities && Object.keys(identities).map(hash => identities[hash].name)) || []; + const isOwnAccount = accountNames.includes(transactionToName); + if (transactionToName && !isOwnAccount) { + this.setState({ confusableCollection: collectConfusables(transactionToName) }); + } + }; + + toggleWarningModal = () => this.setState(state => ({ warningModalVisible: !state.warningModalVisible })); + componentDidMount = async () => { // For analytics AnalyticsV2.trackEvent(AnalyticsV2.ANALYTICS_EVENTS.SEND_TRANSACTION_STARTED, this.getAnalyticsParams()); @@ -372,6 +400,7 @@ class Confirm extends PureComponent { await this.handleFetchBasicEstimates(); showCustomNonce && (await this.setNetworkNonce()); navigation.setParams({ providerType }); + this.handleConfusables(); this.parseTransactionData(); this.prepareTransaction(); }; @@ -907,7 +936,7 @@ class Confirm extends PureComponent { render = () => { const { transactionToName, selectedAsset, paymentRequest } = this.props.transactionState; - const { showHexData, showCustomNonce, primaryCurrency, network } = this.props; + const { addressBook, showHexData, showCustomNonce, primaryCurrency, network, chainId } = this.props; const { nonce } = this.props.transaction; const { gasEstimationReady, @@ -924,10 +953,36 @@ class Confirm extends PureComponent { errorMessage, transactionConfirmed, warningGasPriceHigh, + confusableCollection, mode, - over + over, + warningModalVisible } = this.state; + const checksummedAddress = transactionTo && toChecksumAddress(transactionTo); + const existingContact = checksummedAddress && addressBook[network] && addressBook[network][checksummedAddress]; + const displayExclamation = !existingContact && !!confusableCollection.length; + + const AdressToComponent = () => ( + + ); + + const AdressToComponentWrap = () => + !existingContact && confusableCollection.length ? ( + + + + ) : ( + + ); + const is_main_net = isMainNet(network); const errorPress = is_main_net ? this.buyEth : this.gotoFaucet; const networkName = capitalize(getNetworkName(network)); @@ -943,14 +998,16 @@ class Confirm extends PureComponent { fromAccountName={fromAccountName} fromAccountBalance={fromAccountBalance} /> - + + {strings('transaction.confusable_msg')}} + /> + {!selectedAsset.tokenId ? ( @@ -958,7 +1015,7 @@ class Confirm extends PureComponent { {transactionValue} - {transactionValueFiat} + {isMainNet(chainId) && {transactionValueFiat}} ) : ( @@ -979,7 +1036,7 @@ class Confirm extends PureComponent { } fiat={transactionValueFiat} totalValue={transactionTotalAmount} transactionValue={transactionValue} @@ -1042,6 +1099,7 @@ class Confirm extends PureComponent { const mapStateToProps = state => ({ accounts: state.engine.backgroundState.AccountTrackerController.accounts, + addressBook: state.engine.backgroundState.AddressBookController?.addressBook, contractBalances: state.engine.backgroundState.TokenBalancesController.contractBalances, contractExchangeRates: state.engine.backgroundState.TokenRatesController.contractExchangeRates, currentCurrency: state.engine.backgroundState.CurrencyRateController.currentCurrency, @@ -1051,6 +1109,7 @@ const mapStateToProps = state => ({ providerType: state.engine.backgroundState.NetworkController.provider.type, showHexData: state.settings.showHexData, showCustomNonce: state.settings.showCustomNonce, + chainId: state.engine.backgroundState.NetworkController.provider.chainId, ticker: state.engine.backgroundState.NetworkController.provider.ticker, keyrings: state.engine.backgroundState.KeyringController.keyrings, transaction: getNormalizedTxState(state), diff --git a/app/components/Views/SendFlow/ErrorMessage/__snapshots__/index.test.js.snap b/app/components/Views/SendFlow/ErrorMessage/__snapshots__/index.test.js.snap index 57413a9c970..621c6cfde6f 100644 --- a/app/components/Views/SendFlow/ErrorMessage/__snapshots__/index.test.js.snap +++ b/app/components/Views/SendFlow/ErrorMessage/__snapshots__/index.test.js.snap @@ -32,6 +32,7 @@ exports[`ErrorMessage should render correctly 1`] = ` link={false} modal={false} primary={false} + red={false} reset={false} right={false} small={true} diff --git a/app/components/Views/SendFlow/SendTo/__snapshots__/index.test.js.snap b/app/components/Views/SendFlow/SendTo/__snapshots__/index.test.js.snap index d6855203729..c9965feb7e4 100644 --- a/app/components/Views/SendFlow/SendTo/__snapshots__/index.test.js.snap +++ b/app/components/Views/SendFlow/SendTo/__snapshots__/index.test.js.snap @@ -27,6 +27,7 @@ exports[`SendTo should render correctly 1`] = ` /> Add to address book Enter an alias diff --git a/app/components/Views/SendFlow/SendTo/index.js b/app/components/Views/SendFlow/SendTo/index.js index 54a4e6adda7..bd0f0b7bd42 100644 --- a/app/components/Views/SendFlow/SendTo/index.js +++ b/app/components/Views/SendFlow/SendTo/index.js @@ -7,7 +7,6 @@ import { StyleSheet, View, TouchableOpacity, - Text, TextInput, SafeAreaView, InteractionManager, @@ -34,6 +33,9 @@ import Analytics from '../../../../core/Analytics'; import { ANALYTICS_EVENT_OPTS } from '../../../../util/analytics'; import { allowedToBuy } from '../../../UI/FiatOrders'; import NetworkList from '../../../../util/networks'; +import Text from '../../../Base/Text'; +import Icon from 'react-native-vector-icons/FontAwesome'; +import { collectConfusables, hasZeroWidthPoints } from '../../../../util/validators'; const { hexToBN } = util; const styles = StyleSheet.create({ @@ -125,12 +127,41 @@ const styles = StyleSheet.create({ marginBottom: 32 }, buyEth: { - ...fontStyles.bold, color: colors.black, textDecorationLine: 'underline' }, - bold: { - ...fontStyles.bold + confusabeError: { + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + margin: 16, + padding: 16, + borderWidth: 1, + borderColor: colors.red, + backgroundColor: colors.red000, + borderRadius: 8 + }, + confusabeWarning: { + borderColor: colors.yellow, + backgroundColor: colors.yellow100 + }, + confusableTitle: { + marginTop: -3, + color: colors.red, + ...fontStyles.bold, + fontSize: 14 + }, + confusableMsg: { + color: colors.red, + fontSize: 12, + lineHeight: 16, + paddingRight: 10 + }, + black: { + color: colors.black + }, + warningIcon: { + marginRight: 8 } }); @@ -210,6 +241,7 @@ class SendFlow extends PureComponent { toEnsName: undefined, addToAddressToAddressBook: false, alias: undefined, + confusableCollection: [], inputWidth: { width: '99%' } }; @@ -274,7 +306,7 @@ class SendFlow extends PureComponent { const { AssetsContractController } = Engine.context; const { addressBook, network, identities, providerType } = this.props; const networkAddressBook = addressBook[network] || {}; - let addressError, toAddressName, toEnsName, errorContinue, isOnlyWarning; + let addressError, toAddressName, toEnsName, errorContinue, isOnlyWarning, confusableCollection; let [addToAddressToAddressBook, toSelectedAddressReady] = [false, false]; if (isValidAddress(toSelectedAddress)) { const checksummedToSelectedAddress = toChecksumAddress(toSelectedAddress); @@ -304,7 +336,7 @@ class SendFlow extends PureComponent { addressError = ( {strings('transaction.tokenContractAddressWarning_1')} - {strings('transaction.tokenContractAddressWarning_2')} + {strings('transaction.tokenContractAddressWarning_2')} {strings('transaction.tokenContractAddressWarning_3')} ); @@ -329,6 +361,7 @@ class SendFlow extends PureComponent { */ } else if (isENS(toSelectedAddress)) { toEnsName = toSelectedAddress; + confusableCollection = collectConfusables(toEnsName); const resolvedAddress = await doENSLookup(toSelectedAddress, network); if (resolvedAddress) { const checksummedResolvedAddress = toChecksumAddress(resolvedAddress); @@ -352,7 +385,8 @@ class SendFlow extends PureComponent { toSelectedAddressName: toAddressName, toEnsName, errorContinue, - isOnlyWarning + isOnlyWarning, + confusableCollection }); }; @@ -510,7 +544,7 @@ class SendFlow extends PureComponent { return ( <> {'\n'} - + {strings('fiat_on_ramp.buy_eth')} @@ -519,6 +553,7 @@ class SendFlow extends PureComponent { render = () => { const { ticker } = this.props; + const { addressBook, network } = this.props; const { fromSelectedAddress, fromAccountName, @@ -532,8 +567,16 @@ class SendFlow extends PureComponent { toInputHighlighted, inputWidth, errorContinue, - isOnlyWarning + isOnlyWarning, + confusableCollection } = this.state; + + const checksummedAddress = toSelectedAddress && toChecksumAddress(toSelectedAddress); + const existingContact = checksummedAddress && addressBook[network] && addressBook[network][checksummedAddress]; + const displayConfusableWarning = !existingContact && confusableCollection && !!confusableCollection.length; + const displayAsWarning = + confusableCollection && confusableCollection.length && !confusableCollection.some(hasZeroWidthPoints); + return ( @@ -556,6 +599,7 @@ class SendFlow extends PureComponent { onInputBlur={this.onToInputFocus} onSubmit={this.onTransactionDirectionSet} inputWidth={inputWidth} + confusableCollection={(!existingContact && confusableCollection) || []} /> @@ -578,6 +622,25 @@ class SendFlow extends PureComponent { /> )} + {displayConfusableWarning && ( + + + + + + + {strings('transaction.confusable_title')} + + + {strings('transaction.confusable_msg')} + + + + )} {addToAddressToAddressBook && ( + + + Hide Tokens Without Balance + + + Prevents tokens with no balance from displaying in your token listing. + + + + + @@ -174,6 +191,10 @@ class Settings extends PureComponent { this.props.setPrimaryCurrency(primaryCurrency); }; + toggleHideZeroBalanceTokens = toggleHideZeroBalanceTokens => { + this.props.setHideZeroBalanceTokens(toggleHideZeroBalanceTokens); + }; + componentDidMount = () => { const languages = getLanguages(); this.setState({ languages }); @@ -189,7 +210,14 @@ class Settings extends PureComponent { }; render() { - const { currentCurrency, primaryCurrency, useBlockieIcon, setUseBlockieIcon, selectedAddress } = this.props; + const { + currentCurrency, + primaryCurrency, + useBlockieIcon, + setUseBlockieIcon, + selectedAddress, + hideZeroBalanceTokens + } = this.props; return ( @@ -249,6 +277,18 @@ class Settings extends PureComponent { )} + + {strings('app_settings.hide_zero_balance_tokens_title')} + {strings('app_settings.hide_zero_balance_tokens_desc')} + + + + {strings('app_settings.accounts_identicon_title')} {strings('app_settings.accounts_identicon_desc')} @@ -282,13 +322,15 @@ const mapStateToProps = state => ({ searchEngine: state.settings.searchEngine, primaryCurrency: state.settings.primaryCurrency, useBlockieIcon: state.settings.useBlockieIcon, - selectedAddress: state.engine.backgroundState.PreferencesController.selectedAddress + selectedAddress: state.engine.backgroundState.PreferencesController.selectedAddress, + hideZeroBalanceTokens: state.settings.hideZeroBalanceTokens }); const mapDispatchToProps = dispatch => ({ setSearchEngine: searchEngine => dispatch(setSearchEngine(searchEngine)), setPrimaryCurrency: primaryCurrency => dispatch(setPrimaryCurrency(primaryCurrency)), - setUseBlockieIcon: useBlockieIcon => dispatch(setUseBlockieIcon(useBlockieIcon)) + setUseBlockieIcon: useBlockieIcon => dispatch(setUseBlockieIcon(useBlockieIcon)), + setHideZeroBalanceTokens: hideZeroBalanceTokens => dispatch(setHideZeroBalanceTokens(hideZeroBalanceTokens)) }); export default connect( diff --git a/app/components/Views/Settings/NetworksSettings/__snapshots__/index.test.js.snap b/app/components/Views/Settings/NetworksSettings/__snapshots__/index.test.js.snap index 7272f90e459..c377e72f308 100644 --- a/app/components/Views/Settings/NetworksSettings/__snapshots__/index.test.js.snap +++ b/app/components/Views/Settings/NetworksSettings/__snapshots__/index.test.js.snap @@ -268,7 +268,9 @@ exports[`NetworksSettings should render correctly 1`] = ` - + 0) { return ( - + {strings('app_settings.network_rpc_networks')} {this.renderRpcNetworks()} diff --git a/app/components/Views/Settings/SecuritySettings/__snapshots__/index.test.js.snap b/app/components/Views/Settings/SecuritySettings/__snapshots__/index.test.js.snap index 7f7062dde0d..701c965d354 100644 --- a/app/components/Views/Settings/SecuritySettings/__snapshots__/index.test.js.snap +++ b/app/components/Views/Settings/SecuritySettings/__snapshots__/index.test.js.snap @@ -63,6 +63,13 @@ exports[`SecuritySettings should render correctly 1`] = ` Protect your wallet + - Protect your wallet by saving your seed phrase in various places like on a piece of paper, password manager and/or the cloud. + Protect your wallet by saving your Secret Recovery phrase in various places like on a piece of paper, password manager and/or the cloud. - Important! Seed phrase not backed up + Important! Secret recovery phrase not backed up - Choose a strong password to unlock MetaMask app on your device. If you lose this password, you will need your seedphrase to re-import your wallet. + Choose a strong password to unlock MetaMask app on your device. If you lose this password, you will need your Secret recovery phrase to re-import your wallet. {strings('app_settings.protect_title')} + {strings('app_settings.protect_desc')} { - const { TransactionController } = this.datamodel.context; - try { - const hash = await (await TransactionController.addTransaction( - payload.params[0], - payload.origin, - WalletDevice.MM_MOBILE - )).result; - end(undefined, hash); - } catch (error) { - end(error); - } - } - }, - getAccounts: (end, payload) => { - const { approvedHosts, privacyMode } = store.getState(); - const isEnabled = !privacyMode || approvedHosts[payload.hostname]; - const { KeyringController } = this.datamodel.context; - const isUnlocked = KeyringController.isUnlocked(); - const selectedAddress = this.datamodel.context.PreferencesController.state - .selectedAddress; - end(null, isUnlocked && isEnabled && selectedAddress ? [selectedAddress] : []); + const preferencesController = new PreferencesController( + {}, + { + ipfsGateway: AppConstants.IPFS_DEFAULT_GATEWAY_URL + } + ); + const networkController = new NetworkController({ + infuraProjectId: process.env.MM_INFURA_PROJECT_ID || NON_EMPTY, + providerConfig: { + static: { + eth_sendTransaction: async (payload, next, end) => { + const { TransactionController } = this.context; + try { + const hash = await (await TransactionController.addTransaction( + payload.params[0], + payload.origin, + WalletDevice.MM_MOBILE + )).result; + end(undefined, hash); + } catch (error) { + end(error); } } - }), - new PhishingController(), - new PreferencesController( - {}, - { - ipfsGateway: AppConstants.IPFS_DEFAULT_GATEWAY_URL - } + }, + getAccounts: (end, payload) => { + const { approvedHosts, privacyMode } = store.getState(); + const isEnabled = !privacyMode || approvedHosts[payload.hostname]; + const { KeyringController } = this.context; + const isUnlocked = KeyringController.isUnlocked(); + const selectedAddress = this.context.PreferencesController.state.selectedAddress; + end(null, isUnlocked && isEnabled && selectedAddress ? [selectedAddress] : []); + } + } + }); + const assetsContractController = new AssetsContractController(); + const assetsController = new AssetsController({ + onPreferencesStateChange: listener => preferencesController.subscribe(listener), + onNetworkStateChange: listener => networkController.subscribe(listener), + getAssetName: assetsContractController.getAssetName.bind(assetsContractController), + getAssetSymbol: assetsContractController.getAssetSymbol.bind(assetsContractController), + getCollectibleTokenURI: assetsContractController.getCollectibleTokenURI.bind(assetsContractController) + }); + const currencyRateController = new CurrencyRateController({ + nativeCurrency, + currentCurrency + }); + + const controllers = [ + new KeyringController( + { + removeIdentity: preferencesController.removeIdentity.bind(preferencesController), + syncIdentities: preferencesController.syncIdentities.bind(preferencesController), + updateIdentities: preferencesController.updateIdentities.bind(preferencesController), + setSelectedAddress: preferencesController.setSelectedAddress.bind(preferencesController) + }, + { encryptor }, + initialState.KeyringController + ), + new AccountTrackerController({ + onPreferencesStateChange: listener => preferencesController.subscribe(listener), + getIdentities: () => preferencesController.state.identities + }), + new AddressBookController(), + assetsContractController, + assetsController, + new AssetsDetectionController({ + onAssetsStateChange: listener => assetsController.subscribe(listener), + onPreferencesStateChange: listener => preferencesController.subscribe(listener), + onNetworkStateChange: listener => networkController.subscribe(listener), + getOpenSeaApiKey: () => assetsController.openSeaApiKey, + getBalancesInSingleCall: assetsContractController.getBalancesInSingleCall.bind( + assetsContractController ), - new TokenBalancesController({ interval: 10000 }), - new TokenRatesController(), - new TransactionController(), - new TypedMessageManager(), - new SwapsController({ clientId: AppConstants.SWAPS.CLIENT_ID }) - ], - initialState - ); + addTokens: assetsController.addTokens.bind(assetsController), + addCollectible: assetsController.addCollectible.bind(assetsController), + removeCollectible: assetsController.removeCollectible.bind(assetsController), + getAssetsState: () => assetsController.state + }), + currencyRateController, + new PersonalMessageManager(), + new MessageManager(), + networkController, + new PhishingController(), + preferencesController, + new TokenBalancesController( + { + onAssetsStateChange: listener => assetsController.subscribe(listener), + getSelectedAddress: () => preferencesController.state.selectedAddress, + getBalanceOf: assetsContractController.getBalanceOf.bind(assetsContractController) + }, + { interval: 10000 } + ), + new TokenRatesController({ + onAssetsStateChange: listener => assetsController.subscribe(listener), + onCurrencyRateStateChange: listener => currencyRateController.subscribe(listener) + }), + new TransactionController({ + getNetworkState: () => networkController.state, + onNetworkStateChange: listener => networkController.subscribe(listener), + getProvider: () => networkController.provider + }), + new TypedMessageManager(), + new SwapsController({ + clientId: AppConstants.SWAPS.CLIENT_ID, + fetchAggregatorMetadataThreshold: AppConstants.SWAPS.CACHE_AGGREGATOR_METADATA_THRESHOLD, + fetchTokensThreshold: AppConstants.SWAPS.CACHE_TOKENS_THRESHOLD, + fetchTopAssetsThreshold: AppConstants.SWAPS.CACHE_TOP_ASSETS_THRESHOLD + }) + ]; + + // set initial state + // TODO: Pass initial state into each controller constructor instead + // This is being set post-construction for now to ensure it's functionally equivalent with + // how the `ComponsedController` used to set initial state. + for (const controller of controllers) { + if (initialState[controller.name]) { + controller.update(initialState[controller.name]); + } + } + + this.datamodel = new ComposableController(controllers, initialState); + this.context = controllers.reduce((context, controller) => { + context[controller.name] = controller; + return context; + }, {}); const { AssetsController: assets, KeyringController: keyring, NetworkController: network, TransactionController: transaction - } = this.datamodel.context; + } = this.context; assets.setApiKey(process.env.MM_OPENSEA_KEY); network.refreshNetwork(); @@ -154,16 +221,18 @@ class Engine { AccountTrackerController, AssetsContractController, AssetsDetectionController, - NetworkController: { provider }, + NetworkController: { provider, state: NetworkControllerState }, TransactionController, SwapsController - } = this.datamodel.context; + } = this.context; provider.sendAsync = provider.sendAsync.bind(provider); AccountTrackerController.configure({ provider }); AssetsContractController.configure({ provider }); + SwapsController.configure({ provider, + chainId: NetworkControllerState?.provider?.chainId, pollCountLimit: AppConstants.SWAPS.POLL_COUNT_LIMIT, quotePollingInterval: AppConstants.SWAPS.POLLING_INTERVAL }); @@ -174,7 +243,7 @@ class Engine { } refreshTransactionHistory = async forceCheck => { - const { TransactionController, PreferencesController, NetworkController } = this.datamodel.context; + const { TransactionController, PreferencesController, NetworkController } = this.context; const { selectedAddress } = PreferencesController.state; const { type: networkType } = NetworkController.state.provider; const { networkId } = Networks[networkType]; @@ -235,7 +304,7 @@ class Engine { AssetsController, TokenBalancesController, TokenRatesController - } = this.datamodel.context; + } = this.context; const { selectedAddress } = PreferencesController.state; const { conversionRate, currentCurrency } = CurrencyRateController.state; const { accounts } = AccountTrackerController.state; @@ -301,12 +370,7 @@ class Engine { // Whenever we are gonna start a new wallet // either imported or created, we need to // get rid of the old data from state - const { - TransactionController, - AssetsController, - TokenBalancesController, - TokenRatesController - } = this.datamodel.context; + const { TransactionController, AssetsController, TokenBalancesController, TokenRatesController } = this.context; //Clear assets info AssetsController.update({ @@ -339,7 +403,7 @@ class Engine { NetworkController, TransactionController, AssetsController - } = this.datamodel.context; + } = this.context; // Select same network ? await NetworkController.setProviderType(network.provider.type); @@ -427,7 +491,7 @@ let instance; export default { get context() { - return instance && instance.datamodel && instance.datamodel.context; + return instance && instance.context; }, get state() { const { diff --git a/app/core/Engine.test.js b/app/core/Engine.test.js index 8265d38a60f..a400440d10a 100644 --- a/app/core/Engine.test.js +++ b/app/core/Engine.test.js @@ -2,19 +2,19 @@ import Engine from './Engine'; describe('Engine', () => { it('should expose an API', () => { const engine = Engine.init({}); - expect(engine.datamodel.context).toHaveProperty('AccountTrackerController'); - expect(engine.datamodel.context).toHaveProperty('AddressBookController'); - expect(engine.datamodel.context).toHaveProperty('AssetsContractController'); - expect(engine.datamodel.context).toHaveProperty('AssetsController'); - expect(engine.datamodel.context).toHaveProperty('AssetsDetectionController'); - expect(engine.datamodel.context).toHaveProperty('CurrencyRateController'); - expect(engine.datamodel.context).toHaveProperty('KeyringController'); - expect(engine.datamodel.context).toHaveProperty('NetworkController'); - expect(engine.datamodel.context).toHaveProperty('PersonalMessageManager'); - expect(engine.datamodel.context).toHaveProperty('PhishingController'); - expect(engine.datamodel.context).toHaveProperty('PreferencesController'); - expect(engine.datamodel.context).toHaveProperty('TokenBalancesController'); - expect(engine.datamodel.context).toHaveProperty('TokenRatesController'); - expect(engine.datamodel.context).toHaveProperty('TypedMessageManager'); + expect(engine.context).toHaveProperty('AccountTrackerController'); + expect(engine.context).toHaveProperty('AddressBookController'); + expect(engine.context).toHaveProperty('AssetsContractController'); + expect(engine.context).toHaveProperty('AssetsController'); + expect(engine.context).toHaveProperty('AssetsDetectionController'); + expect(engine.context).toHaveProperty('CurrencyRateController'); + expect(engine.context).toHaveProperty('KeyringController'); + expect(engine.context).toHaveProperty('NetworkController'); + expect(engine.context).toHaveProperty('PersonalMessageManager'); + expect(engine.context).toHaveProperty('PhishingController'); + expect(engine.context).toHaveProperty('PreferencesController'); + expect(engine.context).toHaveProperty('TokenBalancesController'); + expect(engine.context).toHaveProperty('TokenRatesController'); + expect(engine.context).toHaveProperty('TypedMessageManager'); }); }); diff --git a/app/core/Vault.js b/app/core/Vault.js index 6c3ef8684d1..b001330f5ae 100644 --- a/app/core/Vault.js +++ b/app/core/Vault.js @@ -76,6 +76,6 @@ export const recreateVaultWithSamePassword = async (password = '', selectedAddre if (hdKeyring.accounts.includes(selectedAddress)) { PreferencesController.setSelectedAddress(selectedAddress); } else { - PreferencesController.setSelectedAddress(hdKeyring[0]); + PreferencesController.setSelectedAddress(hdKeyring.accounts[0]); } }; diff --git a/app/images/bnb-logo.png b/app/images/bnb-logo.png new file mode 100644 index 00000000000..3712a470d2f Binary files /dev/null and b/app/images/bnb-logo.png differ diff --git a/app/reducers/settings/index.js b/app/reducers/settings/index.js index f269bea8a51..12e43aba595 100644 --- a/app/reducers/settings/index.js +++ b/app/reducers/settings/index.js @@ -5,7 +5,8 @@ const initialState = { searchEngine: AppConstants.DEFAULT_SEARCH_ENGINE, primaryCurrency: 'ETH', lockTime: -1, // Disabled by default - useBlockieIcon: true + useBlockieIcon: true, + hideZeroBalanceTokens: false }; const settingsReducer = (state = initialState, action) => { @@ -35,6 +36,11 @@ const settingsReducer = (state = initialState, action) => { ...state, showCustomNonce: action.showCustomNonce }; + case 'SET_HIDE_ZERO_BALANCE_TOKENS': + return { + ...state, + hideZeroBalanceTokens: action.hideZeroBalanceTokens + }; case 'SET_USE_BLOCKIE_ICON': return { ...state, diff --git a/app/reducers/swaps/index.js b/app/reducers/swaps/index.js index 48569c4e948..5c3bbd139f7 100644 --- a/app/reducers/swaps/index.js +++ b/app/reducers/swaps/index.js @@ -6,7 +6,7 @@ export const SWAPS_SET_HAS_ONBOARDED = 'SWAPS_SET_HAS_ONBOARDED'; const MAX_TOKENS_WITH_BALANCE = 5; // * Action Creator -export const setSwapsLiveness = live => ({ type: SWAPS_SET_LIVENESS, payload: live }); +export const setSwapsLiveness = (live, chainId) => ({ type: SWAPS_SET_LIVENESS, payload: { live, chainId } }); export const setSwapsHasOnboarded = hasOnboarded => ({ type: SWAPS_SET_HAS_ONBOARDED, payload: hasOnboarded }); // * Selectors @@ -15,7 +15,10 @@ export const setSwapsHasOnboarded = hasOnboarded => ({ type: SWAPS_SET_HAS_ONBOA * Returns the swaps liveness state */ -export const swapsLivenessSelector = state => state.swaps.isLive; +export const swapsLivenessSelector = state => { + const chainId = state.engine.backgroundState.NetworkController.provider.chainId; + return state.swaps[chainId]?.isLive || false; +}; /** * Returns the swaps onboarded state @@ -106,16 +109,25 @@ export const swapsTopAssetsSelector = createSelector( // * Reducer export const initialState = { - isLive: true, - hasOnboarded: false + isLive: true, // TODO: should we remove it? + hasOnboarded: false, + + '1': { + isLive: true + } }; function swapsReducer(state = initialState, action) { switch (action.type) { case SWAPS_SET_LIVENESS: { + const { live, chainId } = action.payload; + const data = state[chainId]; return { ...state, - isLive: Boolean(action.payload) + [chainId]: { + ...data, + isLive: live + } }; } case SWAPS_SET_HAS_ONBOARDED: { diff --git a/app/reducers/swaps/swaps.test.js b/app/reducers/swaps/swaps.test.js index 8ad95806c35..94812cdeae6 100644 --- a/app/reducers/swaps/swaps.test.js +++ b/app/reducers/swaps/swaps.test.js @@ -10,10 +10,10 @@ describe('swaps reducer', () => { it('should set liveness', () => { const initalState = reducer(undefined, emptyAction); - const notLiveState = reducer(initalState, { type: SWAPS_SET_LIVENESS, payload: false }); - expect(notLiveState.isLive).toBe(false); - const liveState = reducer(initalState, { type: SWAPS_SET_LIVENESS, payload: true }); - expect(liveState.isLive).toBe(true); + const notLiveState = reducer(initalState, { type: SWAPS_SET_LIVENESS, payload: { live: false, chainId: 1 } }); + expect(notLiveState['1'].isLive).toBe(false); + const liveState = reducer(initalState, { type: SWAPS_SET_LIVENESS, payload: { live: true, chainId: 1 } }); + expect(liveState['1'].isLive).toBe(true); }); it('should set has onboarded', () => { diff --git a/app/util/Device.js b/app/util/Device.js index e172643eee2..7fc173d2373 100644 --- a/app/util/Device.js +++ b/app/util/Device.js @@ -1,6 +1,7 @@ 'use strict'; import { Dimensions, Platform } from 'react-native'; +import { getModel } from 'react-native-device-info'; export default class Device { static getDeviceWidth() { @@ -80,4 +81,10 @@ export default class Device { static isMediumDevice() { return this.getDeviceHeight() < 736; } + + static isIphone12() { + const model = getModel(); + const models = ['iPhone 12', 'iPhone 12 Pro', 'iPhone 12 Pro Max']; + return models.includes(model); + } } diff --git a/app/util/Logger.js b/app/util/Logger.js index 12262b83046..68da02eeca3 100644 --- a/app/util/Logger.js +++ b/app/util/Logger.js @@ -43,8 +43,27 @@ export default class Logger { if (__DEV__) { console.warn(DEBUG, error); // eslint-disable-line no-console } else if (metricsOptIn === AGREED) { - let exception = error.error || error.message || error.originalError || error; - if (!(error instanceof Error)) { + let exception = error; + + if (!error) { + if (!extra) return console.warn('No error nor extra info provided'); + + const typeExtra = typeof extra; + switch (typeExtra) { + case 'string': + exception = new Error(extra); + break; + case 'object': + if (extra.message) { + exception = new Error(extra.message); + } else { + exception = new Error(JSON.stringify(extra)); + } + break; + default: + exception = new Error('error to capture is not an error instance'); + } + } else if (!(error instanceof Error)) { const type = typeof error; switch (type) { case 'string': diff --git a/app/util/address.js b/app/util/address.js index 842a597adbd..d7ae893516c 100644 --- a/app/util/address.js +++ b/app/util/address.js @@ -2,6 +2,9 @@ import { toChecksumAddress } from 'ethereumjs-util'; import Engine from '../core/Engine'; import AppConstants from '../core/AppConstants'; import { strings } from '../../locales/i18n'; +import { tlc } from '../util/general'; + +const { supportedTLDs } = AppConstants; /** * Returns full checksummed address @@ -67,11 +70,11 @@ export async function importAccountFromPrivateKey(private_key) { * @returns {boolean} - Returns a boolean indicating if it is valid */ export function isENS(name) { - const rec = name && name.split('.'); - if (!rec || rec.length === 1 || !AppConstants.supportedTLDs.includes(rec[rec.length - 1])) { - return false; - } - return true; + const OFFSET = 1; + const index = name && name.lastIndexOf('.'); + const tld = index && index >= OFFSET && tlc(name.substr(index + OFFSET, name.length - OFFSET)); + if (index && tld && supportedTLDs.includes(tld)) return true; + return false; } /** diff --git a/app/util/address.test.js b/app/util/address.test.js new file mode 100644 index 00000000000..3ef53dd3455 --- /dev/null +++ b/app/util/address.test.js @@ -0,0 +1,19 @@ +import { isENS } from './address'; + +describe('isENS', () => { + it('should return false by default', () => { + expect(isENS()).toBe(false); + }); + it('should return false for normal domain', () => { + expect(isENS('ricky.codes')).toBe(false); + }); + it('should return true for ens', () => { + expect(isENS('rickycodes.eth')).toBe(true); + }); + it('should return true for eth ens', () => { + expect(isENS('ricky.eth.eth')).toBe(true); + }); + it('should return true for metamask ens', () => { + expect(isENS('ricky.metamask.eth')).toBe(true); + }); +}); diff --git a/app/util/analyticsV2.js b/app/util/analyticsV2.js index c1083b179fe..3bcffe71f46 100644 --- a/app/util/analyticsV2.js +++ b/app/util/analyticsV2.js @@ -88,7 +88,30 @@ export const trackEventV2 = (eventName, params) => { }); }; +/** + * This functions logs errors to analytics instead of sentry. + * The objective is to log errors (that are not errors from our side) like “Invalid Password”. + * An error like this generally means a user inserted the wrong password, so logging to sentry doesn't make sense. + * But we still want to log this to analytics so that we are aware of a rapid increase which may mean it's an error from our side, for example, an error with the encryption library. + * @param {String} type + * @param {String} errorMessage + * @param {String} otherInfo + */ +export const trackErrorAsAnalytics = (type, errorMessage, otherInfo) => { + try { + Analytics.trackEventWithParameters(generateOpt('Error occurred'), { + error: true, + type, + errorMessage, + otherInfo + }); + } catch (error) { + Logger.error(error, 'Error logging analytics - trackErrorAsAnalytics'); + } +}; + export default { ANALYTICS_EVENTS: ANALYTICS_EVENTS_V2, - trackEvent: trackEventV2 + trackEvent: trackEventV2, + trackErrorAsAnalytics }; diff --git a/app/util/infura-conversion.json b/app/util/infura-conversion.json index 28eb58d2055..67020bab413 100644 --- a/app/util/infura-conversion.json +++ b/app/util/infura-conversion.json @@ -28,6 +28,13 @@ "name": "Indonesian Rupiah" } }, + { + "symbol": "ethnzd", + "quote": { + "code": "nzd", + "name": "New Zealand Dollar" + } + }, { "symbol": "ethphp", "quote": { diff --git a/app/util/middlewares.js b/app/util/middlewares.js index bc47af3df66..7e43dfd3b15 100644 --- a/app/util/middlewares.js +++ b/app/util/middlewares.js @@ -1,6 +1,19 @@ import Logger from './Logger'; +import { trackErrorAsAnalytics } from './analyticsV2'; -const REJECTED_TRANSACTION_ERROR = 'User rejected the transaction'; +/** + * List of rpc errors caused by the user rejecting a certain action. + * Errors that include these phrases should not be logged to Sentry. + * Examples of these errors include: + * - User rejected the transaction + * - User cancelled the transaction + * - User rejected the request. + * - MetaMask Message Signature: User denied message signature. + * - MetaMask Personal Message Signature: User denied message signature. + */ +const USER_REJECTED_ERRORS = ['user rejected', 'user denied', 'user cancelled']; + +const USER_REJECTED_ERROR_CODE = 4001; /** * Returns a middleware that appends the DApp origin to request @@ -21,6 +34,29 @@ export function createOriginMiddleware(opts) { }; } +/** + * Checks if the error code or message contains a user rejected error + * @param {String} errorMessage + * @returns {boolean} + */ +function containsUserRejectedError(errorMessage, errorCode) { + try { + if (!errorMessage || !(typeof errorMessage === 'string')) return false; + + const userRejectedErrorMessage = USER_REJECTED_ERRORS.some(userRejectedError => + errorMessage.toLowerCase().includes(userRejectedError.toLowerCase()) + ); + + if (userRejectedErrorMessage) return true; + + if (errorCode === USER_REJECTED_ERROR_CODE) return true; + + return false; + } catch (e) { + return false; + } +} + /** * Returns a middleware that logs RPC activity * @param {{ origin: string }} opts - The middleware options @@ -31,8 +67,40 @@ export function createLoggerMiddleware(opts) { next((/** @type {Function} */ cb) => { if (res.error) { const { error, ...resWithoutError } = res; - if (error && error.message !== REJECTED_TRANSACTION_ERROR) { - Logger.error(error, { message: 'Error in RPC response', res: resWithoutError }); + if (error) { + if (containsUserRejectedError(error.message, error.code)) { + trackErrorAsAnalytics(`Error in RPC response: User rejected`, error.message); + } else { + /** + * Example of a rpc error: + * { "code":-32603, + * "message":"Internal JSON-RPC error.", + * "data":{"code":-32000,"message":"gas required exceeds allowance (59956966) or always failing transaction"} + * } + * This will make the error log to sentry with the title "gas required exceeds allowance (59956966) or always failing transaction" + * making it easier to differentiate each error. + */ + let errorToLog = error; + const errorParams = { + message: 'Error in RPC response', + orginalError: error, + res: resWithoutError + }; + + if (error.message) { + errorToLog = new Error(error.message); + } + + if (error.data) { + errorParams.data = error.data; + + if (error.data.message) { + errorToLog = new Error(error.data.message); + } + } + + Logger.error(errorToLog, errorParams); + } } } if (req.isMetamaskInternal) { diff --git a/app/util/testSetup.js b/app/util/testSetup.js index f49f82f2be4..7794d231a18 100644 --- a/app/util/testSetup.js +++ b/app/util/testSetup.js @@ -86,7 +86,6 @@ jest.mock('react-native-keychain', () => ({ getSupportedBiometryType: () => Prom jest.mock('react-native-share', () => 'RNShare'); jest.mock('react-native-branch', () => ({ subscribe: () => 'RNBranch' })); jest.mock('react-native-sensors', () => 'RNSensors'); -jest.mock('react-native-device-info', () => 'DeviceInfo'); jest.mock('react-native-search-api', () => 'SearchApi'); jest.mock('react-native-reanimated', () => require('react-native-reanimated/mock')); jest.mock('react-native-background-timer', () => 'RNBackgroundTimer'); diff --git a/app/util/transactions.js b/app/util/transactions.js index 8058191ef0f..42d5aa9815d 100644 --- a/app/util/transactions.js +++ b/app/util/transactions.js @@ -5,9 +5,10 @@ import { strings } from '../../locales/i18n'; import contractMap from '@metamask/contract-metadata'; import { safeToChecksumAddress } from './address'; import { util } from '@metamask/controllers'; -import { swapsUtils } from '@estebanmino/controllers'; +import { swapsUtils } from '@metamask/swaps-controller'; import { hexToBN } from './number'; import AppConstants from '../core/AppConstants'; +import { isMainnetByChainId } from './networks'; const { SAI_ADDRESS } = AppConstants; export const TOKEN_METHOD_TRANSFER = 'transfer'; @@ -41,7 +42,7 @@ export const TRANSACTION_TYPES = { APPROVE: 'transaction_approve' }; -const { SWAPS_CONTRACT_ADDRESS } = swapsUtils; +const { getSwapsContractAddress } = swapsUtils; /** * Utility class with the single responsibility * of caching CollectibleAddresses @@ -210,13 +211,14 @@ export async function getMethodData(data) { * Returns wether the given address is a contract * * @param {string} address - Ethereum address + * @param {string} chainId - Current chainId * @returns {boolean} - Whether the given address is a contract */ -export async function isSmartContractAddress(address) { +export async function isSmartContractAddress(address, chainId) { if (!address) return false; address = toChecksumAddress(address); // If in contract map we don't need to cache it - if (contractMap[address]) { + if (isMainnetByChainId(chainId) && contractMap[address]) { return Promise.resolve(true); } const { TransactionController } = Engine.context; @@ -250,12 +252,13 @@ export async function isCollectibleAddress(address, tokenId) { * Returns corresponding transaction action key * * @param {object} transaction - Transaction object + * @param {string} chainId - Current chainId * @returns {string} - Corresponding transaction action key */ -export async function getTransactionActionKey(transaction) { +export async function getTransactionActionKey(transaction, chainId) { const { transaction: { data, to } = {} } = transaction; if (!to) return CONTRACT_METHOD_DEPLOY; - if (to === SWAPS_CONTRACT_ADDRESS) return SWAPS_TRANSACTION_ACTION_KEY; + if (to === getSwapsContractAddress(chainId)) return SWAPS_TRANSACTION_ACTION_KEY; let ret; // if data in transaction try to get method data if (data && data !== '0x') { @@ -282,7 +285,7 @@ export async function getTransactionActionKey(transaction) { * @param {string} selectedAddress - Current account public address * @returns {string} - Transaction type message */ -export async function getActionKey(tx, selectedAddress, ticker) { +export async function getActionKey(tx, selectedAddress, ticker, chainId) { if (tx && tx.isTransfer) { const selfSent = safeToChecksumAddress(tx.transaction.from) === selectedAddress; const translationKey = selfSent ? 'transactions.self_sent_unit' : 'transactions.received_unit'; @@ -290,7 +293,7 @@ export async function getActionKey(tx, selectedAddress, ticker) { if (tx.transferInformation.contractAddress === SAI_ADDRESS.toLowerCase()) tx.transferInformation.symbol = 'SAI'; return strings(translationKey, { unit: tx.transferInformation.symbol }); } - const actionKey = await getTransactionActionKey(tx); + const actionKey = await getTransactionActionKey(tx, chainId); if (actionKey === SEND_ETHER_ACTION_KEY) { const incoming = safeToChecksumAddress(tx.transaction.to) === selectedAddress; const selfSent = incoming && safeToChecksumAddress(tx.transaction.from) === selectedAddress; @@ -319,10 +322,11 @@ export async function getActionKey(tx, selectedAddress, ticker) { * Returns corresponding transaction function type * * @param {object} tx - Transaction object + * @param {string} chainId - Current chainId * @returns {string} - Transaction function type */ -export async function getTransactionReviewActionKey(transaction) { - const actionKey = await getTransactionActionKey({ transaction }); +export async function getTransactionReviewActionKey(transaction, chainId) { + const actionKey = await getTransactionActionKey({ transaction }, chainId); const transactionReviewActionKey = reviewActionKeys[actionKey]; if (transactionReviewActionKey) { return transactionReviewActionKey; diff --git a/app/util/validators.js b/app/util/validators.js index 25195c8e272..1eb1f24207e 100644 --- a/app/util/validators.js +++ b/app/util/validators.js @@ -1,4 +1,5 @@ import { ethers } from 'ethers'; +import { confusables } from 'unicode-confusables'; export const failedSeedPhraseRequirements = seed => { const wordCount = seed.split(/\s/u).length; @@ -13,3 +14,23 @@ export const parseSeedPhrase = seedPhrase => ?.join(' ') || ''; export const { isValidMnemonic } = ethers.utils; + +export const collectConfusables = ensName => { + const key = 'similarTo'; + const collection = confusables(ensName).reduce( + (total, current) => (key in current ? [...total, current.point] : total), + [] + ); + return collection; +}; + +const zeroWidthPoints = new Set([ + '\u200b', // zero width space + '\u200c', // zero width non-joiner + '\u200d', // zero width joiner + '\ufeff', // zero width no-break space + '\u2028', // line separator + '\u2029' // paragraph separator, +]); + +export const hasZeroWidthPoints = char => zeroWidthPoints.has(char); diff --git a/app/util/validators.test.js b/app/util/validators.test.js index a9f8b7c90e6..0f676b55953 100644 --- a/app/util/validators.test.js +++ b/app/util/validators.test.js @@ -1,4 +1,4 @@ -import { failedSeedPhraseRequirements, parseSeedPhrase } from './validators'; +import { failedSeedPhraseRequirements, parseSeedPhrase, hasZeroWidthPoints, collectConfusables } from './validators'; const VALID_24 = 'verb middle giant soon wage common wide tool gentle garlic issue nut retreat until album recall expire bronze bundle live accident expect dry cook'; @@ -37,3 +37,23 @@ describe('parseSeedPhrase', () => { expect(parseSeedPhrase(` ${String(VALID_12).toUpperCase()}`)).toEqual(VALID_12); }); }); + +describe('hasZeroWidthPoints', () => { + it('should detect zero-width unicode', () => { + expect('vita‍lik.eth'.split('').some(hasZeroWidthPoints)).toEqual(true); + }); + it('should not detect zero-width unicode', () => { + expect('vitalik.eth'.split('').some(hasZeroWidthPoints)).toEqual(false); + }); +}); + +describe('collectConfusables', () => { + it('should detect homoglyphic unicode points', () => { + expect(collectConfusables('vita‍lik.eth')).toHaveLength(1); + expect(collectConfusables('faceboоk.eth')).toHaveLength(1); + }); + + it('should detect multiple homoglyphic unicode points', () => { + expect(collectConfusables('ѕсоре.eth')).toHaveLength(5); + }); +}); diff --git a/app/videos/recovery-phrase.mp4 b/app/videos/recovery-phrase.mp4 new file mode 100644 index 00000000000..442ebdae82c Binary files /dev/null and b/app/videos/recovery-phrase.mp4 differ diff --git a/e2e/add-custom-rpc.spec.js b/e2e/add-custom-rpc.spec.js index 60efc364d04..d40b4db819a 100644 --- a/e2e/add-custom-rpc.spec.js +++ b/e2e/add-custom-rpc.spec.js @@ -129,30 +129,21 @@ describe('Custom RPC Tests', () => { await TestHelpers.tap('open-networks-button'); // Check that networks list is visible await TestHelpers.checkIfVisible('networks-list'); - // Swipe down on networks list - await TestHelpers.swipe('networks-list', 'up'); // Check that our network is added await TestHelpers.checkIfElementHasString('other-network-name', 'xDai'); - - // iOS change networks tests - if (device.getPlatform() === 'ios') { - // Change to Rinkeby Network - await TestHelpers.tapByText(RINKEBY); - // Check that we are on correct network - await TestHelpers.checkIfElementHasString('network-name', RINKEBY); - // Tap to prompt network list - await TestHelpers.tap('open-networks-button'); - // Check that networks list is visible - await TestHelpers.checkIfVisible('networks-list'); - // Swipe down on networks list - await TestHelpers.swipe('networks-list', 'up'); - // Change to back to xDai Network - await TestHelpers.tapByText('xDai'); - } else { - // Close list - await TestHelpers.tapByText('Close'); - } - + // Change to Rinkeby Network + await TestHelpers.tapByText(RINKEBY); + // Check that we are on correct network + await TestHelpers.checkIfElementHasString('network-name', RINKEBY); + // Tap to prompt network list + await TestHelpers.tap('open-networks-button'); + // Check that networks list is visible + await TestHelpers.checkIfVisible('networks-list'); + // Swipe up on networks list + await TestHelpers.swipe('other-networks-scroll', 'up', 'fast'); + await TestHelpers.delay(1000); + // Change to back to xDai Network + await TestHelpers.tapByText('xDai'); // Check that we are on the wallet screen await TestHelpers.checkIfVisible('wallet-screen'); // Check that we are on correct network @@ -171,22 +162,21 @@ describe('Custom RPC Tests', () => { // Check that we are on the networks screen await TestHelpers.checkIfVisible('networks-screen'); // Tap on xDai to remove network - await element(by.text('xDai')).longPress(); - // Tap remove + await TestHelpers.tapAndLongPressAtIndex('rpc-networks', 0); + //Remove xDAI and verify removed on wallet view + //Tap remove await TestHelpers.tapByText('Remove'); - // Go back to wallet screen if (device.getPlatform() === 'ios') { // Tap on back arrow - await TestHelpers.tapAtPoint('networks-screen', { x: 25, y: -22 }); + await TestHelpers.tap('nav-ios-back'); // Tap close await TestHelpers.tapByText('Close'); } else { // Go Back for android - await device.pressBack(); - await device.pressBack(); + await TestHelpers.tap('nav-android-back'); + await TestHelpers.tap('nav-android-back'); } - // Check that we are on the wallet screen await TestHelpers.checkIfExists('wallet-screen'); // Check that we are on Mainnet diff --git a/e2e/addressbook-tests.spec.js b/e2e/addressbook-tests.spec.js index 3fb208c3acc..0f0af8384dc 100644 --- a/e2e/addressbook-tests.spec.js +++ b/e2e/addressbook-tests.spec.js @@ -2,6 +2,7 @@ import TestHelpers from './helpers'; const INVALID_ADDRESS = '0xB8B4EE5B1b693971eB60bDa15211570df2dB221L'; +const TETHER_ADDRESS = '0xdac17f958d2ee523a2206206994597c13d831ec7'; const MYTH_ADDRESS = '0x1FDb169Ef12954F20A15852980e1F0C122BfC1D6'; const MEMO = 'Test adding ENS'; const PASSWORD = '12345678'; @@ -84,12 +85,12 @@ describe('Addressbook Tests', () => { it('should go to send view', async () => { // Check that we are on the wallet screen await TestHelpers.checkIfVisible('wallet-screen'); - // Tap on ETH asset - await TestHelpers.waitAndTap('eth-logo'); - // Check that we are on the token overview screen - await TestHelpers.checkIfVisible('token-asset-overview'); + // Open Drawer + await TestHelpers.tap('hamburger-menu-button-wallet'); + // Check that the drawer is visbile + await TestHelpers.checkIfVisible('drawer-screen'); // Tap on Send button - await TestHelpers.tapByText('SEND'); + await TestHelpers.tap('drawer-send-button'); // Check that we are on the send screen await TestHelpers.checkIfVisible('send-screen'); // Make sure view with my accounts visible @@ -97,15 +98,26 @@ describe('Addressbook Tests', () => { }); it('should input a valid address to send to', async () => { - // Input incorrect address + // Input incorrect address Currently commented out until https://github.com/MetaMask/metamask-mobile/issues/2533 is fixed + // if (device.getPlatform() === 'android') { + // await TestHelpers.replaceTextInField('txn-to-address-input', INVALID_ADDRESS); + // await element(by.id('txn-to-address-input')).tapReturnKey(); + // } else { + // await TestHelpers.typeTextAndHideKeyboard('txn-to-address-input', INVALID_ADDRESS); + // } + // // Check that the error is displayed + // await TestHelpers.checkIfVisible('address-error'); + //Input token address to test for error if (device.getPlatform() === 'android') { - await TestHelpers.replaceTextInField('txn-to-address-input', INVALID_ADDRESS); - await element(by.id('txn-to-address-input')).tapReturnKey(); + await TestHelpers.replaceTextInField('txn-to-address-input', TETHER_ADDRESS); } else { - await TestHelpers.typeTextAndHideKeyboard('txn-to-address-input', INVALID_ADDRESS); + await TestHelpers.typeTextAndHideKeyboard('txn-to-address-input', TETHER_ADDRESS); } // Check that the error is displayed await TestHelpers.checkIfVisible('address-error'); + // Tap x to remove address + await TestHelpers.tap('clear-address-button'); + await TestHelpers.delay(1000); // Input valid myth address if (device.getPlatform() === 'android') { await TestHelpers.replaceTextInField('txn-to-address-input', MYTH_ADDRESS); @@ -142,8 +154,6 @@ describe('Addressbook Tests', () => { it('should go to settings then select contacts', async () => { // Tap on cancel button await TestHelpers.tap('send-cancel-button'); - // Tap on back button to proceed to wallet view - await TestHelpers.tap('asset-back-button'); // Check that we are on the wallet screen await TestHelpers.checkIfVisible('wallet-screen'); // Open Drawer @@ -186,11 +196,10 @@ describe('Addressbook Tests', () => { await TestHelpers.replaceTextInField('contact-memo-input', MEMO); // Tap add contact button if (device.getPlatform() === 'android') { - await TestHelpers.tapByText('Add contact'); - await TestHelpers.delay(1000); - await TestHelpers.tapByText('Add contact'); - } else { await TestHelpers.tap('contact-add-contact-button'); + await TestHelpers.delay(700); + await TestHelpers.tap('contact-add-contact-button'); + } else { await TestHelpers.tap('contact-add-contact-button'); } // Check that we are on the contacts screen @@ -208,6 +217,9 @@ describe('Addressbook Tests', () => { await TestHelpers.replaceTextInField('contact-name-input', 'Moon'); // Tap on Edit contact button await TestHelpers.tapByText('Edit contact'); + if (device.getPlatform() === 'ios') { + await TestHelpers.tapByText('Edit contact'); + } // Check that we are on the contacts screen await TestHelpers.checkIfVisible('contacts-screen'); // Check that Ibrahim address is saved in the address book @@ -223,8 +235,11 @@ describe('Addressbook Tests', () => { await TestHelpers.tapByText('Edit'); // Tap on Delete await TestHelpers.tapByText('Delete'); - // Tap on Delete - await TestHelpers.tapByText('Delete'); + if (device.getPlatform() === 'ios') { + await TestHelpers.tapByText('Delete', 1); + } else { + await TestHelpers.tapByText('Delete'); + } // Ensure Moon is not visible await TestHelpers.checkIfElementWithTextIsNotVisible('Moon'); }); @@ -234,7 +249,7 @@ describe('Addressbook Tests', () => { await TestHelpers.tap('title-back-arrow-button'); // tap to get out of settings view if (device.getPlatform() === 'android') { - await device.pressBack(); + await TestHelpers.tap('nav-android-back'); } else { await TestHelpers.tapByText('Close'); } diff --git a/e2e/browser-tests.spec.js b/e2e/browser-tests.spec.js index 2f85fecd908..642e14790e7 100644 --- a/e2e/browser-tests.spec.js +++ b/e2e/browser-tests.spec.js @@ -3,7 +3,7 @@ import TestHelpers from './helpers'; const ENS_Example = 'https://brunobarbieri.eth'; const ENS_TLD = 'https://inbox.mailchain.xyz'; -const UNISWAP = 'https://uniswap.eth'; +const UNISWAP = 'https://uniswap.exchange'; const PASSWORD = '12345678'; describe('Browser Tests', () => { @@ -95,42 +95,46 @@ describe('Browser Tests', () => { }); it('should go to first explore tab and navigate back to homepage', async () => { - // Tap on first category - if (device.getPlatform() === 'android') { + // This can only be done on Android since we removed option for iOS due to Appstore + if (!device.getPlatform() === 'android') { + // Tap on first category await TestHelpers.tapAtPoint('browser-screen', { x: 100, y: 425 }); - } else { - await TestHelpers.tapAtPoint('browser-screen', { x: 100, y: 450 }); + // Tap on first option + await TestHelpers.tapAtPoint('browser-screen', { x: 80, y: 100 }); + // Tap back button + await TestHelpers.waitAndTap('go-back-button'); + // Tap back button + await TestHelpers.waitAndTap('go-back-button'); + // Wait for page to load + await TestHelpers.delay(1000); + // Check that we are on the browser screen + await TestHelpers.checkIfVisible('browser-screen'); } - // Tap on first option - await TestHelpers.tapAtPoint('browser-screen', { x: 80, y: 100 }); - // Tap back button - await TestHelpers.waitAndTap('go-back-button'); - // Tap back button - await TestHelpers.waitAndTap('go-back-button'); - // Wait for page to load - await TestHelpers.delay(1000); - // Check that we are on the browser screen - await TestHelpers.checkIfVisible('browser-screen'); }); it('should go to uniswap', async () => { // Tap on home on bottom navbar - await TestHelpers.tap('home-button'); + // await TestHelpers.tap('home-button'); // Wait for page to load - await TestHelpers.delay(1000); + await TestHelpers.delay(3000); // Tap on search in bottom navbar await TestHelpers.tap('search-button'); // Navigate to URL if (device.getPlatform() === 'ios') { + await TestHelpers.clearField('url-input'); await TestHelpers.typeTextAndHideKeyboard('url-input', UNISWAP); + await TestHelpers.delay(2000); } else { + await TestHelpers.tap('android-cancel-url-button'); await TestHelpers.replaceTextInField('url-input', UNISWAP); await element(by.id('url-input')).tapReturnKey(); } // Wait for page to load await TestHelpers.delay(5000); - // Check that the dapp title is correct - await TestHelpers.checkIfElementWithTextIsVisible('uniswap.eth', 0); + if (device.getPlatform() === 'android') { + // Check that the dapp title is correct + await TestHelpers.checkIfElementWithTextIsVisible('app.uniswap.org', 0); + } // Tap on CANCEL button await TestHelpers.tap('connect-cancel-button'); @@ -139,7 +143,11 @@ describe('Browser Tests', () => { // Wait for page to load await TestHelpers.delay(3000); await TestHelpers.tap('connect-cancel-button'); - + // Android has weird behavior where the URL modal stays open, so this closes it + // Close URL modal + if (device.getPlatform() === 'android') { + await device.pressBack(); + } // Check that we are still on the browser screen await TestHelpers.checkIfVisible('browser-screen'); }); @@ -166,7 +174,10 @@ describe('Browser Tests', () => { await TestHelpers.checkIfVisible('browser-screen'); // Tap on Favorites tab if (device.getPlatform() === 'ios') { - await TestHelpers.tapAtPoint('browser-screen', { x: 274, y: 227 }); + // Tap on options + await TestHelpers.waitAndTap('options-button'); + // Open new tab + await TestHelpers.tapByText('New tab'); await TestHelpers.tapAtPoint('browser-screen', { x: 174, y: 281 }); await TestHelpers.delay(1500); } else { @@ -220,7 +231,7 @@ describe('Browser Tests', () => { await TestHelpers.checkIfVisible('browser-screen'); // Tap on empowr from search results if (device.getPlatform() === 'ios') { - await TestHelpers.tapAtPoint('browser-screen', { x: 20, y: 245 }); + await TestHelpers.tapAtPoint('browser-screen', { x: 60, y: 270 }); } else { await TestHelpers.tapAtPoint('browser-screen', { x: 56, y: 284 }); await TestHelpers.delay(700); diff --git a/e2e/helpers.js b/e2e/helpers.js index 309f9ebba6a..f0818ea95ee 100644 --- a/e2e/helpers.js +++ b/e2e/helpers.js @@ -52,7 +52,13 @@ export default class TestHelpers { static async tapAndLongPress(elementId) { await TestHelpers.tap(elementId); - return element(by.id(elementId)).longPress(); + return element(by.id(elementId)).longPress(2000); + } + + static async tapAndLongPressAtIndex(elementId, index) { + return element(by.id(elementId, index)) + .atIndex(index || 0) + .longPress(2000); } static async replaceTextInField(elementId, text) { @@ -71,8 +77,8 @@ export default class TestHelpers { .tap(); } - static async swipe(elementId, direction) { - await element(by.id(elementId)).swipe(direction); + static async swipe(elementId, direction, speed, percentage) { + await element(by.id(elementId)).swipe(direction, speed, percentage); } static async scrollTo(scrollviewId, edge) { diff --git a/e2e/onboarding-wizard-opt-out.spec.js b/e2e/onboarding-wizard-opt-out.spec.js index 9246407301f..a282eccc314 100644 --- a/e2e/onboarding-wizard-opt-out.spec.js +++ b/e2e/onboarding-wizard-opt-out.spec.js @@ -84,28 +84,11 @@ describe('Onboarding wizard opt-out', () => { // Scroll to the bottom if (device.getPlatform() === 'android') { - await TestHelpers.swipe('change-password-section', 'up'); - TestHelpers.delay(1500); - await TestHelpers.swipe('change-password-section', 'up'); - TestHelpers.delay(1500); - await TestHelpers.swipe('auto-lock-section', 'up'); - TestHelpers.delay(1500); - await TestHelpers.swipe('clear-privacy-section', 'up'); - TestHelpers.delay(1500); - await TestHelpers.swipe('clear-cookies-section', 'up'); - TestHelpers.delay(1500); - await TestHelpers.swipe('privacy-mode-section', 'up'); - TestHelpers.delay(1500); - await TestHelpers.swipe('privacy-mode-section', 'up'); - TestHelpers.delay(1500); - await TestHelpers.swipe('privacy-mode-section', 'up'); - TestHelpers.delay(1500); - await TestHelpers.swipe('metametrics-section', 'up'); - TestHelpers.delay(1500); + await TestHelpers.swipe('security-settings-scrollview', 'up', 'fast'); + await TestHelpers.delay(1000); } else { - await TestHelpers.swipe('auto-lock-section', 'up'); + await TestHelpers.swipe('auto-lock-section', 'up', 'fast'); } - // Toggle Metametrics on await TestHelpers.tap('metametrics-switch'); TestHelpers.delay(1000); @@ -133,43 +116,40 @@ describe('Onboarding wizard opt-out', () => { await TestHelpers.checkIfNotVisible('onboarding-wizard-step1-view'); }); - // commenting this out as tapping on this take a tour prompt currently doesn't work - // once fixed I can add it back in - - // it('should take tour and skip tutorial', async () => { - // // Open Drawer - // await TestHelpers.tap('hamburger-menu-button-wallet'); - // // Check that the drawer is visbile - // await TestHelpers.checkIfVisible('drawer-screen'); - // // Tap on Browser - // await TestHelpers.tapByText('Browser'); - // // Wait for page to load - // await TestHelpers.delay(1000); - // // Check that we are on the browser screen - // await TestHelpers.checkIfVisible('browser-screen'); - // // Scroll on browser to show tutorial box and tap to skip - // if (device.getPlatform() === 'ios') { - // await TestHelpers.swipe('browser-screen', 'up'); - // } else { - // await TestHelpers.checkIfExists('browser-webview'); - // await TestHelpers.swipe('browser-webview', 'up'); - // await TestHelpers.delay(1000); - // } - // // Tap on the Take a tour box - // if (device.getPlatform() === 'ios') { - // await TestHelpers.tapAtPoint('browser-screen', { x: 215, y: 555 }); - // } else { - // await TestHelpers.tapAtPoint('browser-screen', { x: 175, y: 480 }); - // } - // // Check that we are on the wallet screen - // await TestHelpers.checkIfNotVisible('browser-screen'); - // // Check that the onboarding wizard is present - // await TestHelpers.checkIfVisible('onboarding-wizard-step1-view'); - // // Check that Take the tour CTA is visible and tap it - // await TestHelpers.waitAndTap('onboarding-wizard-next-button'); - // // Tap on Skip Tutorial - // await TestHelpers.tapByText('Skip Tutorial'); - // // Check that the wizard is not visible anymore - // await TestHelpers.checkIfNotVisible('onboarding-wizard-step1-view'); - // }); + it('should take tour and skip tutorial', async () => { + // Open Drawer + await TestHelpers.tap('hamburger-menu-button-wallet'); + // Check that the drawer is visbile + await TestHelpers.checkIfVisible('drawer-screen'); + // Tap on Browser + await TestHelpers.tapByText('Browser'); + // Wait for page to load + await TestHelpers.delay(1000); + // Check that we are on the browser screen + await TestHelpers.checkIfVisible('browser-screen'); + // Scroll on browser to show tutorial box and tap to skip + if (device.getPlatform() === 'ios') { + await TestHelpers.swipe('browser-screen', 'up'); + } else { + await TestHelpers.checkIfExists('browser-webview'); + await TestHelpers.swipe('browser-webview', 'up'); + await TestHelpers.delay(1000); + } + // Tap on the Take a tour box + if (device.getPlatform() === 'ios') { + await TestHelpers.tapAtPoint('browser-screen', { x: 215, y: 555 }); + } else { + await TestHelpers.tapAtPoint('browser-screen', { x: 175, y: 480 }); + } + // Check that we are on the wallet screen + await TestHelpers.checkIfNotVisible('browser-screen'); + // Check that the onboarding wizard is present + await TestHelpers.checkIfVisible('onboarding-wizard-step1-view'); + // Check that Take the tour CTA is visible and tap it + await TestHelpers.waitAndTap('onboarding-wizard-next-button'); + // Tap on Skip Tutorial + await TestHelpers.tapByText('Skip Tutorial'); + // Check that the wizard is not visible anymore + await TestHelpers.checkIfNotVisible('onboarding-wizard-step1-view'); + }); }); diff --git a/e2e/request-token-flow.spec.js b/e2e/request-token-flow.spec.js index 65bc6835692..3c4583dc3c8 100644 --- a/e2e/request-token-flow.spec.js +++ b/e2e/request-token-flow.spec.js @@ -90,7 +90,7 @@ describe('Request Token Flow', () => { await TestHelpers.checkIfVisible('receive-request-screen'); }); - it('should request ETH', async () => { + it('should request DAI', async () => { // Tap on request payment button await TestHelpers.tap('request-payment-button'); // Tap on ETH @@ -110,9 +110,13 @@ describe('Request Token Flow', () => { await TestHelpers.typeTextAndHideKeyboard('request-search-asset-input', 'DAI'); } else { await TestHelpers.replaceTextInField('request-search-asset-input', 'DAI'); + await TestHelpers.delay(1000); } // Select DAI from search results await TestHelpers.tapByText('DAI', 1); + if (device.getPlatform() === 'ios') { + await TestHelpers.tapByText('DAI', 1); + } // Request 5.50 DAI await TestHelpers.typeTextAndHideKeyboard('request-amount-input', 5.5); // Make sure we're on the right screen diff --git a/e2e/start-exploring.spec.js b/e2e/start-exploring.spec.js index 28dbf2d7b18..f940774bfc2 100644 --- a/e2e/start-exploring.spec.js +++ b/e2e/start-exploring.spec.js @@ -94,15 +94,13 @@ describe('Start Exploring', () => { // Ensure step 3 is shown correctly await TestHelpers.checkIfVisible('step3-title'); // Focus into account 1 name - await TestHelpers.tapAndLongPress('account-label'); - // Clear text - await TestHelpers.clearField('account-label-text-input'); - // Change account name if (device.getPlatform() === 'android') { + await TestHelpers.tapAndLongPress('edit-account-label'); + // Clear text + await TestHelpers.clearField('account-label-text-input'); + // Change account name await TestHelpers.replaceTextInField('account-label-text-input', ACCOUNT); await element(by.id('account-label-text-input')).tapReturnKey(); - } else { - await TestHelpers.typeTextAndHideKeyboard('account-label-text-input', ACCOUNT); } // Check that Got it! CTA is visible and tap it if (!device.getPlatform() === 'android') { @@ -133,6 +131,14 @@ describe('Start Exploring', () => { await TestHelpers.tapByText('Back'); // Ensure step 5 is shown correctly await TestHelpers.checkIfVisible('step5-title'); + // Tap on Back + await TestHelpers.tapByText('Back'); + // Ensure step 4 is shown correctly + await TestHelpers.checkIfVisible('step4-title'); + // Check that Got it! CTA is visible and tap it + await TestHelpers.tapByText('Got it!'); + // Ensure step 5 is shown correctly + await TestHelpers.checkIfVisible('step5-title'); // Check that Got it! CTA is visible and tap it await TestHelpers.tapByText('Got it!'); // Ensure step 6 is shown correctly diff --git a/e2e/wallet-tests.spec.js b/e2e/wallet-tests.spec.js index f7b3070484d..a582db45ffc 100644 --- a/e2e/wallet-tests.spec.js +++ b/e2e/wallet-tests.spec.js @@ -5,9 +5,10 @@ const CORRECT_SEED_WORDS = 'fold media south add since false relax immense pause const CORRECT_PASSWORD = `12345678`; const TEST_PUBLIC_ADDRESS = '0xd3B9Cbea7856AECf4A6F7c3F4E8791F79cBeeD62'; const RINKEBY = 'Rinkeby Test Network'; +const ETHEREUM = 'Ethereum Main Network'; const COLLECTIBLE_CONTRACT_ADDRESS = '0x16baf0de678e52367adc69fd067e5edd1d33e3bf'; const COLLECTIBLE_IDENTIFIER = '404'; -const TOKEN_ADDRESS = '0x12525e53a7fB9e072e60062D087b19a05442BD8f'; +const TOKEN_ADDRESS = '0x107c4504cd79c5d2696ea0030a8dd4e92601b82e'; const TEST_PRIVATE_KEY = 'cbfd798afcfd1fd8ecc48cbecb6dc7e876543395640b758a90e11d986e758ad1'; const VALID_ADDRESS = '0xebe6CcB6B55e1d094d9c58980Bc10Fed69932cAb'; @@ -68,7 +69,9 @@ describe('Wallet Tests', () => { // Tap on Create New Account await TestHelpers.waitAndTap('create-account-button'); // Check if account was added - await TestHelpers.checkIfElementWithTextIsVisible('Account 2'); + if (device.getPlatform() === 'android') { + await TestHelpers.checkIfElementWithTextIsVisible('Account 2'); + } }); it('should be able to import account', async () => { @@ -178,15 +181,8 @@ describe('Wallet Tests', () => { await TestHelpers.tapByText('ADD'); // Check that identifier warning appears await TestHelpers.checkIfVisible('collectible-identifier-warning'); - // Go Back one view - if (device.getPlatform() === 'android') { - await device.pressBack(); - await TestHelpers.delay(1000); - } else { - await TestHelpers.tapAtPoint('add-custom-token-screen', { x: 25, y: -22 }); - } - + await TestHelpers.tap('asset-back-button'); // Tap on the add collectibles button await TestHelpers.waitAndTap('add-collectible-button'); // Check that we are on the add collectible asset screen @@ -213,12 +209,7 @@ describe('Wallet Tests', () => { // Check that the asset is correct await TestHelpers.checkIfElementHasString('collectible-name', '1 CryptoKitties'); // Tap on back arrow - if (device.getPlatform() === 'android') { - await device.pressBack(); - await TestHelpers.delay(1000); - } else { - await TestHelpers.tapAtPoint('collectible-overview-screen', { x: 25, y: -22 }); - } + await TestHelpers.tap('asset-back-button'); }); it('should add a token', async () => { @@ -226,6 +217,14 @@ describe('Wallet Tests', () => { await TestHelpers.checkIfVisible('wallet-screen'); // Tap on TOKENS tab await TestHelpers.tapByText('TOKENS'); + // Switch to mainnet + await TestHelpers.waitAndTap('open-networks-button'); + // Check that the Networks modal pops up + await TestHelpers.checkIfVisible('networks-list'); + // Tap on Eth Mainnet + await TestHelpers.tapByText(ETHEREUM); + // Check that we are on Eth Mainnet + await TestHelpers.checkIfElementWithTextIsVisible(ETHEREUM); // Tap on Add Tokens await TestHelpers.tap('add-token-button'); // Search for SAI @@ -233,12 +232,8 @@ describe('Wallet Tests', () => { // Wait for results to load await TestHelpers.delay(2000); // Select SAI - if (device.getPlatform() === 'android') { - await TestHelpers.tapItemAtIndex('searched-token-result'); - await TestHelpers.delay(500); - } else { - await TestHelpers.tapAtPoint('search-token-screen', { x: 115, y: 160 }); - } + await TestHelpers.tapItemAtIndex('searched-token-result'); + await TestHelpers.delay(500); // Tap on Add Token button await TestHelpers.tapByText('ADD TOKEN'); // Check that we are on the wallet screen @@ -272,11 +267,11 @@ describe('Wallet Tests', () => { await TestHelpers.delay(700); // Check that token decimals warning is displayed await TestHelpers.checkIfVisible('token-decimals-warning'); - // Tap on cancel button + // Go back if (device.getPlatform() === 'android') { await device.pressBack(); } else { - await TestHelpers.tapByText('CANCEL'); + await TestHelpers.tap('asset-back-button'); } // Tap on Add Tokens await TestHelpers.tap('add-token-button'); @@ -303,18 +298,29 @@ describe('Wallet Tests', () => { // Check that we are on the wallet screen await TestHelpers.checkIfVisible('wallet-screen'); // Check that TENX is added to wallet - await TestHelpers.checkIfElementWithTextIsVisible('0 TENX'); + await TestHelpers.checkIfElementWithTextIsVisible('0 BLT'); + }); + + it('should switch back to Rinkeby network', async () => { + // Tap on Ethereum Main Network to prompt modal + await TestHelpers.waitAndTap('open-networks-button'); + // Check that the Networks modal pops up + await TestHelpers.checkIfVisible('networks-list'); + // Tap on Rinkeby Test Nework + await TestHelpers.tapByText(RINKEBY); + // Check that we are on Rinkeby network + await TestHelpers.checkIfElementWithTextIsVisible(RINKEBY); }); it('should input a valid address', async () => { // Check that we are on the wallet screen await TestHelpers.checkIfVisible('wallet-screen'); - // Tap on ETH asset - await TestHelpers.waitAndTap('eth-logo'); - // Check that we are on the token overview screen - await TestHelpers.checkIfVisible('token-asset-overview'); + // Open Drawer + await TestHelpers.tap('hamburger-menu-button-wallet'); + // Check that the drawer is visbile + await TestHelpers.checkIfVisible('drawer-screen'); // Tap on Send button - await TestHelpers.tapByText('SEND'); + await TestHelpers.tap('drawer-send-button'); // Check that we are on the send screen await TestHelpers.checkIfVisible('send-screen'); // Input test address @@ -327,11 +333,7 @@ describe('Wallet Tests', () => { it('should input and validate amount', async () => { // Input amount - if (device.getPlatform() === 'android') { - await TestHelpers.replaceTextInField('txn-amount-input', '5'); - } else { - await TestHelpers.typeTextAndHideKeyboard('txn-amount-input', '5'); - } + await TestHelpers.replaceTextInField('txn-amount-input', '5'); // Tap Next CTA await TestHelpers.tap('txn-amount-next-button'); // Check that the insufficient funds warning pops up @@ -349,7 +351,7 @@ describe('Wallet Tests', () => { await TestHelpers.checkIfHasText('confirm-txn-amount', '0.00004 ETH'); // Tap on the Send CTA await TestHelpers.tap('txn-confirm-send-button'); - // Check that we are on the token overview screen - await TestHelpers.checkIfVisible('token-asset-overview'); + // Check that we are on the wallet screen + await TestHelpers.checkIfVisible('wallet-screen'); }); }); diff --git a/index.js b/index.js index e33a253cd25..45e25584a01 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ import './shim.js'; import 'react-native-gesture-handler'; +import 'react-native-url-polyfill/auto'; import crypto from 'crypto'; // eslint-disable-line import/no-nodejs-modules, no-unused-vars require('react-native-browser-polyfill'); // eslint-disable-line import/no-commonjs @@ -15,9 +16,10 @@ import { name } from './app.json'; // List of warnings that we're ignoring LogBox.ignoreLogs([ '{}', - // Uncomment the below line to run browser-tests.spec.js in debug mode + // Uncomment the below lines (21 and 22) to run browser-tests.spec.js in debug mode // in e2e tests until issue https://github.com/MetaMask/metamask-mobile/issues/1395 is resolved //"Error in RPC response", + // 'User rejected account access', "Can't perform a React state update", 'Error evaluating injectedJavaScript', 'createErrorFromErrorData', diff --git a/ios/MetaMask.xcodeproj/project.pbxproj b/ios/MetaMask.xcodeproj/project.pbxproj index 6e7beb4b1f4..76f02eaeba9 100644 --- a/ios/MetaMask.xcodeproj/project.pbxproj +++ b/ios/MetaMask.xcodeproj/project.pbxproj @@ -28,6 +28,7 @@ 2370F9A340CF4ADFBCFB0543 /* EuclidCircularB-RegularItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = 58572D81B5D54ED79A16A16D /* EuclidCircularB-RegularItalic.otf */; }; 298242C958524BB38FB44CAE /* Roboto-BoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = C9FD3FB1258A41A5A0546C83 /* Roboto-BoldItalic.ttf */; }; 2A27FC9EEF1F4FD18E658544 /* config.json in Resources */ = {isa = PBXBuildFile; fileRef = EF1C01B7F08047F9B8ADCFBA /* config.json */; }; + 2C2671FF7C9738D73A97E1E3 /* libPods-MetaMask.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AECD4D0D95B36200E86EEAAE /* libPods-MetaMask.a */; }; 2CDF19FE9DEE4BF8B07154B1 /* EuclidCircularB-LightItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = F79EAC4A7BF74E458277AFA4 /* EuclidCircularB-LightItalic.otf */; }; 2DB27BE39B164356A98A0FB1 /* Roboto-Italic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 5D7956F8525C4A45A2A555C3 /* Roboto-Italic.ttf */; }; 34CEE49BC79D411687B42FA9 /* Roboto-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 459C4774EB724F2D8E12F088 /* Roboto-Regular.ttf */; }; @@ -43,7 +44,6 @@ 813214A2220E40C7BBB5ED9E /* Roboto-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A783D1CD7D27456796FE2E1B /* Roboto-Bold.ttf */; }; 887E75FB64A54509A08D6C50 /* Roboto-LightItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = E020F42F788744B3BCE17F05 /* Roboto-LightItalic.ttf */; }; 8DEB44A7E7EF48E1B3298910 /* EuclidCircularB-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = CE0434C5FB7C4C6F9FEBDCE2 /* EuclidCircularB-Medium.otf */; }; - 8F24036CB356C6465E07CB50 /* libPods-MetaMask.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 789B8FA040E544968673792F /* libPods-MetaMask.a */; }; BF39E5BAE0F34F9091FF6AC0 /* EuclidCircularB-Semibold.otf in Resources */ = {isa = PBXBuildFile; fileRef = A8DE9C5BC0714D648276E123 /* EuclidCircularB-Semibold.otf */; }; CD13D926E1E84D9ABFE672C0 /* Roboto-BlackItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 3E2492C67CF345CABD7B8601 /* Roboto-BlackItalic.ttf */; }; D171C39A8BD44DBEB6B68480 /* EuclidCircularB-MediumItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = 42CBA652072F4BE2A8B815C1 /* EuclidCircularB-MediumItalic.otf */; }; @@ -189,9 +189,9 @@ 178440FE3F1C4F4180D14622 /* libTcpSockets.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libTcpSockets.a; sourceTree = ""; }; 1C516951C09F43CB97129B66 /* Octicons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Octicons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Octicons.ttf"; sourceTree = ""; }; 1F06D56A2D2F41FB9345D16F /* Lottie.framework */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = wrapper.framework; name = Lottie.framework; path = System/Library/Frameworks/Lottie.framework; sourceTree = SDKROOT; }; + 1F907A61887952BF381AC8E3 /* Pods-MetaMask.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MetaMask.debug.xcconfig"; path = "Target Support Files/Pods-MetaMask/Pods-MetaMask.debug.xcconfig"; sourceTree = ""; }; 278065D027394AD9B2906E38 /* libBVLinearGradient.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libBVLinearGradient.a; sourceTree = ""; }; 2D16E6891FA4F8E400B85C8A /* libReact.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libReact.a; sourceTree = BUILT_PRODUCTS_DIR; }; - 327DD2DF6994E9948B4D7E0F /* Pods-MetaMask.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MetaMask.release.xcconfig"; path = "Target Support Files/Pods-MetaMask/Pods-MetaMask.release.xcconfig"; sourceTree = ""; }; 3E2492C67CF345CABD7B8601 /* Roboto-BlackItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Roboto-BlackItalic.ttf"; path = "../app/fonts/Roboto-BlackItalic.ttf"; sourceTree = ""; }; 42C239E9FAA64BD9A34B8D8A /* MaterialCommunityIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = MaterialCommunityIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf"; sourceTree = ""; }; 42C6DDE3B80F47AFA9C9D4F5 /* Foundation.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Foundation.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Foundation.ttf"; sourceTree = ""; }; @@ -202,6 +202,7 @@ 4A2D27104599412CA00C35EF /* Ionicons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Ionicons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf"; sourceTree = ""; }; 57C103F40F394637B5A886FC /* FontAwesome5_Brands.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = FontAwesome5_Brands.ttf; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Brands.ttf"; sourceTree = ""; }; 58572D81B5D54ED79A16A16D /* EuclidCircularB-RegularItalic.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "EuclidCircularB-RegularItalic.otf"; path = "../app/fonts/EuclidCircularB-RegularItalic.otf"; sourceTree = ""; }; + 589C418BA726A01F27EA894F /* Pods-MetaMask.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MetaMask.release.xcconfig"; path = "Target Support Files/Pods-MetaMask/Pods-MetaMask.release.xcconfig"; sourceTree = ""; }; 5D7956F8525C4A45A2A555C3 /* Roboto-Italic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Roboto-Italic.ttf"; path = "../app/fonts/Roboto-Italic.ttf"; sourceTree = ""; }; 5E32A09A7BDC431FA403BA73 /* FontAwesome.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = FontAwesome.ttf; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf"; sourceTree = ""; }; 650F2B9724DC5FEB00C3B9C4 /* RCTAesForked.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAesForked.xcodeproj; path = "../node_modules/react-native-aes-crypto-forked/ios/RCTAesForked.xcodeproj"; sourceTree = ""; }; @@ -209,7 +210,6 @@ 654378AF243E2ADC00571B9C /* File.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = File.swift; sourceTree = ""; }; 67FBD519E04742E0AF191782 /* EuclidCircularB-Bold.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "EuclidCircularB-Bold.otf"; path = "../app/fonts/EuclidCircularB-Bold.otf"; sourceTree = ""; }; 684F2C84313849199863B5FE /* Roboto-Black.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Roboto-Black.ttf"; path = "../app/fonts/Roboto-Black.ttf"; sourceTree = ""; }; - 789B8FA040E544968673792F /* libPods-MetaMask.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MetaMask.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 7FF1597C0ACA4902B86140B2 /* Zocial.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Zocial.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Zocial.ttf"; sourceTree = ""; }; 8E369AC13A2049B6B21E5120 /* libRCTSearchApi.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRCTSearchApi.a; sourceTree = ""; }; 9499B01ECAC44DA29AC44E80 /* EuclidCircularB-SemiboldItalic.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "EuclidCircularB-SemiboldItalic.otf"; path = "../app/fonts/EuclidCircularB-SemiboldItalic.otf"; sourceTree = ""; }; @@ -219,9 +219,9 @@ A98029A3662F4C1391489A6B /* EuclidCircularB-Light.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "EuclidCircularB-Light.otf"; path = "../app/fonts/EuclidCircularB-Light.otf"; sourceTree = ""; }; A98DB430A7DA47EFB97EDF8B /* FontAwesome5_Solid.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = FontAwesome5_Solid.ttf; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Solid.ttf"; sourceTree = ""; }; AA9EDF17249955C7005D89EE /* MetaMaskDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = MetaMaskDebug.entitlements; path = MetaMask/MetaMaskDebug.entitlements; sourceTree = ""; }; + AECD4D0D95B36200E86EEAAE /* libPods-MetaMask.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MetaMask.a"; sourceTree = BUILT_PRODUCTS_DIR; }; BB8BA2D3C0354D6090B56A8A /* Roboto-Light.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Roboto-Light.ttf"; path = "../app/fonts/Roboto-Light.ttf"; sourceTree = ""; }; BF485CDA047B4D52852B87F5 /* EvilIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = EvilIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf"; sourceTree = ""; }; - BF8DE2E5A176599FCD8A7CFD /* Pods-MetaMask.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MetaMask.debug.xcconfig"; path = "Target Support Files/Pods-MetaMask/Pods-MetaMask.debug.xcconfig"; sourceTree = ""; }; C752564A28B44392AEE16BD5 /* Roboto-Medium.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Roboto-Medium.ttf"; path = "../app/fonts/Roboto-Medium.ttf"; sourceTree = ""; }; C9FD3FB1258A41A5A0546C83 /* Roboto-BoldItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Roboto-BoldItalic.ttf"; path = "../app/fonts/Roboto-BoldItalic.ttf"; sourceTree = ""; }; CE0434C5FB7C4C6F9FEBDCE2 /* EuclidCircularB-Medium.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "EuclidCircularB-Medium.otf"; path = "../app/fonts/EuclidCircularB-Medium.otf"; sourceTree = ""; }; @@ -250,7 +250,7 @@ 15ACC9FC22655C3A0063978B /* Lottie.framework in Frameworks */, 15F7795E22A1B7B500B1DF8C /* Mixpanel.framework in Frameworks */, 153F84CA2319B8FD00C19B63 /* Branch.framework in Frameworks */, - 8F24036CB356C6465E07CB50 /* libPods-MetaMask.a in Frameworks */, + 2C2671FF7C9738D73A97E1E3 /* libPods-MetaMask.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -358,7 +358,7 @@ children = ( 153C1A742217BCDC0088EFE0 /* JavaScriptCore.framework */, 2D16E6891FA4F8E400B85C8A /* libReact.a */, - 789B8FA040E544968673792F /* libPods-MetaMask.a */, + AECD4D0D95B36200E86EEAAE /* libPods-MetaMask.a */, ); name = Frameworks; sourceTree = ""; @@ -460,8 +460,8 @@ AA342D524556DBBE26F5997C /* Pods */ = { isa = PBXGroup; children = ( - BF8DE2E5A176599FCD8A7CFD /* Pods-MetaMask.debug.xcconfig */, - 327DD2DF6994E9948B4D7E0F /* Pods-MetaMask.release.xcconfig */, + 1F907A61887952BF381AC8E3 /* Pods-MetaMask.debug.xcconfig */, + 589C418BA726A01F27EA894F /* Pods-MetaMask.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -473,7 +473,7 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "MetaMask" */; buildPhases = ( - 72868E350C2B4E37DD67D4DA /* [CP] Check Pods Manifest.lock */, + AB8811CF1B31FCE5CC98A7C6 /* [CP] Check Pods Manifest.lock */, 65E00B0A247EA25400E5AC88 /* Start Packager */, 15FDD86321B76696006B7C35 /* Override xcconfig files */, 13B07F871A680F5B00A75B9A /* Sources */, @@ -481,7 +481,7 @@ 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, 15ACCA0022655C3A0063978B /* Embed Frameworks */, - 84D295E70E7158158F0F4201 /* [CP] Copy Pods Resources */, + B6E9B4C4CCD9D5D5F8286546 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -722,7 +722,7 @@ shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > \"${SRCROOT}/../node_modules/react-native/scripts/.packager.env\"\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open \"$SRCROOT/../node_modules/react-native/scripts/launchPackager.command\" || echo \"Can't start packager automatically\"\n fi\nfi\n"; showEnvVarsInLog = 0; }; - 72868E350C2B4E37DD67D4DA /* [CP] Check Pods Manifest.lock */ = { + AB8811CF1B31FCE5CC98A7C6 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -744,7 +744,7 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 84D295E70E7158158F0F4201 /* [CP] Copy Pods Resources */ = { + B6E9B4C4CCD9D5D5F8286546 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -841,7 +841,7 @@ /* Begin XCBuildConfiguration section */ 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = BF8DE2E5A176599FCD8A7CFD /* Pods-MetaMask.debug.xcconfig */; + baseConfigurationReference = 1F907A61887952BF381AC8E3 /* Pods-MetaMask.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_OPTIMIZATION = time; @@ -849,7 +849,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMaskDebug.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 613; + CURRENT_PROJECT_VERSION = 715; DEAD_CODE_STRIPPING = NO; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -882,7 +882,7 @@ "\"$(SRCROOT)/MetaMask/System/Library/Frameworks\"", ); LLVM_LTO = YES; - MARKETING_VERSION = 2.2.0; + MARKETING_VERSION = 2.3.0; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = ( "$(inherited)", @@ -905,7 +905,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 327DD2DF6994E9948B4D7E0F /* Pods-MetaMask.release.xcconfig */; + baseConfigurationReference = 589C418BA726A01F27EA894F /* Pods-MetaMask.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_OPTIMIZATION = time; @@ -913,7 +913,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 613; + CURRENT_PROJECT_VERSION = 715; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; FRAMEWORK_SEARCH_PATHS = ( @@ -945,7 +945,7 @@ "\"$(SRCROOT)/MetaMask/System/Library/Frameworks\"", ); LLVM_LTO = YES; - MARKETING_VERSION = 2.2.0; + MARKETING_VERSION = 2.3.0; ONLY_ACTIVE_ARCH = NO; OTHER_CFLAGS = ( "$(inherited)", diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 5b829048f67..582b362a534 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -271,14 +271,19 @@ PODS: - React - react-native-cookies (4.0.1): - React - - react-native-netinfo (4.1.5): - - React + - react-native-netinfo (6.0.0): + - React-Core - react-native-randombytes (3.5.3): - React - react-native-safe-area-context (3.1.9): - React-Core - react-native-splash-screen (3.2.0): - React + - react-native-video (5.1.1): + - React-Core + - react-native-video/Video (= 5.1.1) + - react-native-video/Video (5.1.1): + - React-Core - react-native-view-shot (3.1.2): - React - react-native-viewpager (3.3.0): @@ -363,8 +368,8 @@ PODS: - React - RNCMaskedView (0.1.10): - React - - RNDeviceInfo (3.1.4): - - React + - RNDeviceInfo (8.1.3): + - React-Core - RNFS (2.16.6): - React - RNGestureHandler (1.9.0): @@ -381,18 +386,18 @@ PODS: - React-Core - RNSensors (5.3.0): - React - - RNSentry (1.3.3): - - React - - Sentry (~> 4.4.0) - - RNShare (3.3.2): - - React + - RNSentry (2.4.2): + - React-Core + - Sentry (= 6.1.4) + - RNShare (5.2.2): + - React-Core - RNSVG (12.1.0): - React - RNVectorIcons (6.4.2): - React - - Sentry (4.4.3): - - Sentry/Core (= 4.4.3) - - Sentry/Core (4.4.3) + - Sentry (6.1.4): + - Sentry/Core (= 6.1.4) + - Sentry/Core (6.1.4) - TcpSockets (4.0.0): - CocoaAsyncSocket - React @@ -449,6 +454,7 @@ DEPENDENCIES: - react-native-randombytes (from `../node_modules/react-native-randombytes`) - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) - react-native-splash-screen (from `../node_modules/react-native-splash-screen`) + - react-native-video (from `../node_modules/react-native-video`) - react-native-view-shot (from `../node_modules/react-native-view-shot`) - "react-native-viewpager (from `../node_modules/@react-native-community/viewpager`)" - react-native-webview (from `../node_modules/react-native-webview`) @@ -558,6 +564,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-safe-area-context" react-native-splash-screen: :path: "../node_modules/react-native-splash-screen" + react-native-video: + :path: "../node_modules/react-native-video" react-native-view-shot: :path: "../node_modules/react-native-view-shot" react-native-viewpager: @@ -666,10 +674,11 @@ SPEC CHECKSUMS: react-native-branch: f630e0e399c64230463246469ca0817e4f821b78 react-native-camera: 8016138a8348b889e22c95b23f668a234f124335 react-native-cookies: ff2e6865dff2e5feeca8f1ed082ae7898e4fa912 - react-native-netinfo: 0e563248a4b9a99c33ec29bd03c2d50767db22a6 + react-native-netinfo: e849fc21ca2f4128a5726c801a82fc6f4a6db50d react-native-randombytes: 3638d24759d67c68f6ccba60c52a7a8a8faa6a23 react-native-safe-area-context: b6e0e284002381d2ff29fa4fff42b4d8282e3c94 react-native-splash-screen: 200d11d188e2e78cea3ad319964f6142b6384865 + react-native-video: 0bb76b6d6b77da3009611586c7dbf817b947f30e react-native-view-shot: 4475fde003fe8a210053d1f98fb9e06c1d834e1c react-native-viewpager: a7b438ca32c57b2614ece2a123e7fe116f743131 react-native-webview: dfd7202ff115c44d3ea401c2f36122fb3ac79f07 @@ -690,7 +699,7 @@ SPEC CHECKSUMS: RNCCheckbox: 357578d3b42652c78ee9a1bb9bcfc3195af6e161 RNCClipboard: 8148e21ac347c51fd6cd4b683389094c216bb543 RNCMaskedView: 5a8ec07677aa885546a0d98da336457e2bea557f - RNDeviceInfo: 88099e84466f053bae3f34c307b506738b2b6946 + RNDeviceInfo: 8d3a29207835f972bce883723882980143270d55 RNFS: 2bd9eb49dc82fa9676382f0585b992c424cd59df RNGestureHandler: 9b7e605a741412e20e13c512738a31bd1611759b RNI18n: e2f7e76389fcc6e84f2c8733ea89b92502351fd8 @@ -699,11 +708,11 @@ SPEC CHECKSUMS: RNReanimated: e03f7425cb7a38dcf1b644d680d1bfc91c3337ad RNScreens: 45c457af3d2ee9e08fc01e70da87e653d46b1198 RNSensors: c363d486c879e181905dea84a2535e49af1c2d25 - RNSentry: 86baf0c87120cd5eb491d073989f8cbc1a2174c6 - RNShare: bd6518701db274b8ba282b33620885f2225a5bcf + RNSentry: e86fb2e2fec0365644f4b582938bf66be515acce + RNShare: 5cfe16bfd42cd2c4869a7692462181ac8cc15a6d RNSVG: ce9d996113475209013317e48b05c21ee988d42e RNVectorIcons: 6607bd3a30291d0edb56f9bbe7ae411ee2b928b0 - Sentry: 14bdd673870e8cf64932b149fad5bbbf39a9b390 + Sentry: 9d055e2de30a77685e86b219acf02e59b82091fc TcpSockets: a8eb6b5867fe643e6cfed5db2e4de62f4d1e8fd0 Yoga: 4bd86afe9883422a7c4028c00e34790f560923d6 YogaKit: f782866e155069a2cca2517aafea43200b01fd5a diff --git a/locales/languages/en.json b/locales/languages/en.json index 45bb14c9d99..44f6add00ba 100644 --- a/locales/languages/en.json +++ b/locales/languages/en.json @@ -43,7 +43,7 @@ "warning_text_1": "Your current wallet and accounts will be", "warning_text_2": "removed", "warning_text_3": "if you proceed.", - "warning_text_4": "You can ONLY recover them with your wallet’s seed phrase. MetaMask cannot help you recover it.", + "warning_text_4": "You can ONLY recover them with your wallet’s Secret Recovery phrase. MetaMask cannot help you recover it.", "warning_proceed": "Remove wallet & proceed", "warning_cancel": "Cancel", "step1": "Wallet setup", @@ -123,7 +123,7 @@ "sync_help_step_four": "4. Scan the QR Code to start syncing", "sync_from_browser_extension_button": "Sync with MetaMask extension", "or": "OR", - "import_from_seed_button": "Import using seed phrase" + "import_from_seed_button": "Import using Secret Recovery phrase" }, "login": { "title": "Welcome Back!", @@ -142,7 +142,12 @@ "you_can_only": "You can ONLY recover this wallet with your ", "recovery_phrase": "Recovery Phrase ", "metamask_does_not": "MetaMask does not have your recovery phrase.", - "i_understand": "I understand, continue" + "i_understand": "I understand, continue", + "passcode_not_set_error": "Error: Passcode not set.", + "wrong_password_error": "Error: Decrypt failed", + "wrong_password_error_android": "Error: error:1e000065:Cipher functions:OPENSSL_internal:BAD_DECRYPT", + "vault_error": "Error: Cannot unlock without a previous vault.", + "clean_vault_error": "MetaMask encountered an error due to reaching a storage limit. The local data has been corrupted. Please reinstall MetaMask and restore with your Secret Recovery phrase." }, "enter_password": { "title": "Enter your password", @@ -157,7 +162,7 @@ "password": "New Password", "confirm_password": "Confirm password", "create_button": "Create password", - "import_with_seed_phrase": "Import with seed phrase", + "import_with_seed_phrase": "Import with Secret Recovery phrase", "password_length_error": "The password needs to be at least 8 characters long", "password_dont_match": "Passwords don't match", "password_strength": "Password strength:", @@ -166,7 +171,7 @@ "strength_strong": "Strong", "show": "Show", "hide": "Hide", - "seed_phrase": "Seed phrase", + "seed_phrase": "Secret recovery phrase", "must_be_at_least": "Must be at least {{number}} characters", "remember_me": "Remember me", "security_alert_title": "Security Alert", @@ -174,7 +179,7 @@ "i_understand": "I understand that MetaMask cannot recover this password for me.", "learn_more": "Learn more.", "secure": "Secure wallet", - "confirm": "Confirm seed phrase" + "confirm": "Confirm Secret Recovery phrase" }, "reset_password": { "title": "Change password", @@ -182,7 +187,7 @@ "password": "New Password", "confirm_password": "Confirm password", "reset_button": "Reset password", - "import_with_seed_phrase": "Import with seed phrase", + "import_with_seed_phrase": "Import with Secret Recovery phrase", "password_length_error": "The password needs to be at least 8 characters long", "password_dont_match": "Passwords don't match", "password_strength": "Password strength:", @@ -191,7 +196,7 @@ "strength_strong": "Strong", "show": "Show", "hide": "Hide", - "seed_phrase": "Seed phrase", + "seed_phrase": "Secret recovery phrase", "must_be_at_least": "Must be at least {{number}} characters", "remember_me": "Remember me", "security_alert_title": "Security Alert", @@ -199,24 +204,24 @@ "i_understand": "I understand that MetaMask cannot recover this password for me.", "learn_more": "Learn more.", "secure": "Secure wallet", - "confirm": "Confirm seed phrase", + "confirm": "Confirm Secret Recovery phrase", "password_updated": "Password updated", "successfully_changed": "Your password has been successfully changed" }, "import_from_seed": { "title": "Import from seed", - "seed_phrase_placeholder": "Enter your seed phrase here", + "seed_phrase_placeholder": "Enter your Secret Recovery phrase here", "new_password": "New Password", "confirm_password": "Confirm password", "import_button": "IMPORT", "cancel_button": "Cancel", "password_length_error": "The password needs to be at least 8 characters long", "password_dont_match": "Passwords don't match", - "seed_phrase_requirements": "Seed phrases contain 12, 15, 18, 21, or 24 words", - "invalid_seed_phrase": "Invalid seed phrase", + "seed_phrase_requirements": "Secret recovery phrases contain 12, 15, 18, 21, or 24 words", + "invalid_seed_phrase": "Invalid Secret Recovery phrase", "error": "Error", "invalid_qr_code_title": "Invalid QR Code", - "invalid_qr_code_message": "This QR code doesn't represent a valid seed phrase" + "invalid_qr_code_message": "This QR code doesn't represent a valid Secret Recovery phrase" }, "bottom_tab_bar": { "dapps": "ÐApps", @@ -289,7 +294,7 @@ "private_key_detected": "Private key detected", "do_you_want_to_import_this_account": "Do you want to import this account?", "error": "Error", - "logout_to_import_seed": "You need to log out first in order to import a seed phrase.", + "logout_to_import_seed": "You need to log out first in order to import a Secret Recovery phrase.", "ready_to_explore": "Ready to start exploring blockchain applications?", "unable_to_load": "Unable to load balance" }, @@ -420,9 +425,10 @@ "clear": "CLEAR", "protect_cta": "Protect", "protect_title": "Protect your wallet", - "protect_desc": "Protect your wallet by saving your seed phrase in various places like on a piece of paper, password manager and/or the cloud.", - "seedphrase_not_backed_up": "Important! Seed phrase not backed up", - "seedphrase_backed_up": "Seed phrase backed up", + "video_failed": "Video Failed to Load.", + "protect_desc": "Protect your wallet by saving your Secret Recovery phrase in various places like on a piece of paper, password manager and/or the cloud.", + "seedphrase_not_backed_up": "Important! Secret recovery phrase not backed up", + "seedphrase_backed_up": "Secret recovery phrase backed up", "back_up_now": "Back up now", "back_up_again": "Back up again", "view_hint": "View hint", @@ -444,7 +450,7 @@ "contacts_desc": "Add, edit, remove, and manage your accounts", "security_title": "Security & Privacy", "back": "Back", - "security_desc": "Privacy settings, MetaMetrics, private key and wallet seed phrase", + "security_desc": "Privacy settings, MetaMetrics, private key and wallet Secret Recovery phrase", "networks_title": "Networks", "networks_desc": "Add and edit custom RPC networks", "network_name_label": "Network Name", @@ -503,7 +509,9 @@ "invalid_hex_number_leading_zeros": "Invalid hexadecimal number. Remove any leading zeros.", "invalid_number": "Invalid number. Enter a decimal or '0x'-prefixed hexadecimal number.", "invalid_number_leading_zeros": "Invalid number. Remove any leading zeros.", - "invalid_number_range": "Invalid number. Enter a number between 1 and %{maxSafeChainId}" + "invalid_number_range": "Invalid number. Enter a number between 1 and %{maxSafeChainId}", + "hide_zero_balance_tokens_title": "Hide Tokens Without Balance", + "hide_zero_balance_tokens_desc": "Prevents tokens with no balance from displaying in your token listing." }, "app_information": { "title": "Information", @@ -516,22 +524,22 @@ "contact_us": "Contact Us" }, "reveal_credential": { - "seed_phrase_title": "Reveal seed phrase", + "seed_phrase_title": "Reveal Secret Recovery phrase", "private_key_title": "Show private key", "show_private_key": "Show private key", "private_key_title_for_account": "Show private key for \"{{accountName}}\"", "cancel": "Cancel", "confirm": "Next", - "seed_phrase_explanation": "If you ever change browsers or move computers, you will need this seed phrase to access your accounts. Save them somewhere safe and secret.", + "seed_phrase_explanation": "If you ever change browsers or move computers, you will need this Secret Recovery phrase to access your accounts. Save them somewhere safe and secret.", "private_key_explanation": "Save it somewhere safe and secret.", "private_key_warning": "This is the private key for the current selected account: {{accountName}}. Never disclose this key. Anyone with your private key can fully control your account, including transferring away any of your funds.", "seed_phrase_warning_explanation": "DO NOT share this phrase with anyone! These words can be used to steal all your accounts.", "private_key_warning_explanation": "Never disclose this key. Anyone with your private key can fully control your account, including transferring away any of your funds.", - "seed_phrase": "Your seed phrase", + "seed_phrase": "Your Secret Recovery phrase", "private_key": "Your private key", "copy_to_clipboard": "Copy to clipboard", "enter_password": "Enter password to continue", - "seed_phrase_copied": "Seed phrase copied to clipboard", + "seed_phrase_copied": "Secret recovery phrase copied to clipboard", "private_key_copied": "Private key copied to clipboard", "warning_incorrect_password": "Incorrect password", "unknown_error": "Couldn't unlock your account. Please try again.", @@ -541,7 +549,7 @@ }, "password_reset": { "password_title": "Password", - "password_desc": "Choose a strong password to unlock MetaMask app on your device. If you lose this password, you will need your seedphrase to re-import your wallet.", + "password_desc": "Choose a strong password to unlock MetaMask app on your device. If you lose this password, you will need your Secret recovery phrase to re-import your wallet.", "password_learn_more": "Learn more.", "change_password": "Change password" }, @@ -670,7 +678,9 @@ "tokenContractAddressWarning_2": "token contract address", "tokenContractAddressWarning_3": ". If you send tokens to this address, you will lose them.", "smartContractAddressWarning": "This address is a smart contract address. Please make sure you understand what this address is for, otherwise you risk losing your funds.", - "continueError": "I understand the risks, continue" + "continueError": "I understand the risks, continue", + "confusable_title": "Check the recipient address", + "confusable_msg": "We have detected a confusable character in the ENS name. Check the ENS name to avoid a potential scam." }, "custom_gas": { "total": "Total", @@ -875,13 +885,13 @@ "outdated_qr_code": "Expired QR Code", "outdated_qr_code_desc": "This seems to be an expired QR code! Please try scanning a new one", "something_wrong": "Ooops! Something went wrong...", - "something_wrong_desc": "We are having issues while trying to sync your wallet. Please try again later or import your wallet using your seed phrase", + "something_wrong_desc": "We are having issues while trying to sync your wallet. Please try again later or import your wallet using your Secret Recovery phrase", "allow_biometrics_title": "Do you want to enable {{biometrics}}?", "allow_biometrics_desc": "You can use {{biometrics}} to authenticate on MetaMask" }, "sync_with_extension_success": { "title": "Wallet imported", - "sync_complete_1": "Just remember, MetaMask cannot recover your seed phrase should you lose it. You can find the seed phrase in", + "sync_complete_1": "Just remember, MetaMask cannot recover your Secret Recovery phrase should you lose it. You can find the Secret Recovery phrase in", "sync_complete_2": "Settings > Security & Privacy", "button_continue": "Done", "password_tip": "The password to unlock your mobile wallet is the same one you set for the extension.", @@ -965,7 +975,7 @@ }, "import_private_key": { "title": "Import Account", - "description_one": "Imported accounts are viewable in your wallet but are not recoverable with your MetaMask seed phrase.", + "description_one": "Imported accounts are viewable in your wallet but are not recoverable with your MetaMask Secret Recovery phrase.", "learn_more_here": "Learn more about imported accounts here.", "subtitle": "Paste your private key string", "cta_text": "IMPORT", @@ -991,7 +1001,7 @@ "step_1": "Step 1:", "step_1_description": "Create password", "step_2": "Step 2:", - "step_2_description": "Save wallet seed phrase", + "step_2_description": "Save wallet Secret Recovery phrase", "info_text_1": "Take a few moments to finish setting up your MetaMask wallet.", "info_text_2": "This will ensure only you can access your funds and will let you recover your wallet if you lose your device", "cta_text": "Create Password", @@ -1002,7 +1012,7 @@ "remind_me_later_subtext": "(Not recommended)", "title": "Secure your wallet", "info_text_1_1": "Don’t risk losing your funds. Protect your wallet by saving your", - "info_text_1_2": "seed phrase", + "info_text_1_2": "Secret Recovery phrase", "info_text_1_3": "in a place you trust.", "info_text_1_4": "It’s the only way to recover your wallet if you get locked out of the app or get a new device.", "cta_text": "Start", @@ -1010,21 +1020,21 @@ "skip_button_cancel": "Secure now", "skip_button_confirm": "Skip", "skip_title": "Skip account security?", - "skip_check": "I understand that if I lose my seed phrase I will not be able to access my wallet.", - "what_is_seedphrase_title": "What is a ‘Seed phrase’", - "what_is_seedphrase_text_1": "A seed phrase is a set of twelve words that contains all the information about your wallet, including your funds. It’s like a secret code used to access your entire wallet.", - "what_is_seedphrase_text_2": "You must keep your seed phrase secret and safe. If someone gets your seed phrase, they’ll gain control over your accounts.", + "skip_check": "I understand that if I lose my Secret Recovery phrase I will not be able to access my wallet.", + "what_is_seedphrase_title": "What is a ‘Secret recovery phrase’", + "what_is_seedphrase_text_1": "A Secret Recovery phrase is a set of twelve words that contains all the information about your wallet, including your funds. It’s like a secret code used to access your entire wallet.", + "what_is_seedphrase_text_2": "You must keep your Secret Recovery phrase secret and safe. If someone gets your Secret Recovery phrase, they’ll gain control over your accounts.", "what_is_seedphrase_text_3": "Save it in a place where only you can access it. If you lose it, not even MetaMask can help you recover it." }, "account_backup_step_1B": { "title": "Secure your wallet", "subtitle_1": "Secure your wallet's", - "subtitle_2": "seed phrase.", + "subtitle_2": "Secret Recovery phrase.", "cta_text": "Start", "learn_more": "Learn More", "why_important": "Why is it important?", "manual_title": "Manual", - "manual_subtitle": "Write down your seed phrase on a piece of paper and store in a safe place.", + "manual_subtitle": "Write down your Secret Recovery phrase on a piece of paper and store in a safe place.", "manual_security": "Security level: Very strong", "risks_title": "Risks are:", "risks_1": "You lose it", @@ -1036,16 +1046,16 @@ "tips_2": "Store in a safe", "tips_3": "Store in multiple secret places", "why_secure_title": "Protect your wallet", - "why_secure_1": "Don’t risk losing your funds. Protect your wallet by saving your seed phrase in a place you trust.", + "why_secure_1": "Don’t risk losing your funds. Protect your wallet by saving your Secret Recovery phrase in a place you trust.", "why_secure_2": " It’s the only way to recover your wallet if you get locked out of the app or get a new device." }, "account_backup_step_2": { "cancel_backup_title": "Cancel Backup", - "cancel_backup_message": "We highly recommend you save your seed phrase in order to restore your wallet.", + "cancel_backup_message": "We highly recommend you save your Secret Recovery phrase in order to restore your wallet.", "cancel_backup_ok": "Yes, I'll take the risk", - "cancel_backup_no": "No, back up Seed Phrase", + "cancel_backup_no": "No, back up Secret Recovery Phrase", "title": "Grab a pen and paper", - "info": "Next step is to write your seed phrase down.", + "info": "Next step is to write your Secret Recovery phrase down.", "info_2_1": "You will be asked to ", "info_2_2": "re-enter", "info_2_3": " it for confirmation", @@ -1053,20 +1063,20 @@ }, "account_backup_step_3": { "cancel_backup_title": "Cancel Backup", - "cancel_backup_message": "We highly recommend you save your seed phrase in order to restore your wallet.", + "cancel_backup_message": "We highly recommend you save your Secret Recovery phrase in order to restore your wallet.", "cancel_backup_ok": "Yes, I'll take the risk", - "cancel_backup_no": "No, back up Seed Phrase", + "cancel_backup_no": "No, back up Secret Recovery Phrase", "title": "Is anyone watching?", - "info_text": "Make sure no other human or robot is watching your screen. If your seed phrase is copied, it can be used on other devices to steal your funds", + "info_text": "Make sure no other human or robot is watching your screen. If your Secret Recovery phrase is copied, it can be used on other devices to steal your funds", "cta_text": "NO ONE'S WATCHING ME" }, "account_backup_step_4": { "cancel_backup_title": "Cancel Backup", - "cancel_backup_message": "We highly recommend you save your seed phrase in order to restore your wallet.", + "cancel_backup_message": "We highly recommend you save your Secret Recovery phrase in order to restore your wallet.", "cancel_backup_ok": "Yes, I'll take the risk", - "cancel_backup_no": "No, back up Seed Phrase", + "cancel_backup_no": "No, back up Secret Recovery Phrase", "back": "Back", - "title": "Your seed phrase", + "title": "Your Secret Recovery phrase", "info_text_1": "Carefully write down these words on paper. Their order matters.", "info_text_2": "You'll be asked to re-enter it on the next screen", "cta_text": "I'VE COPIED THE PHRASE", @@ -1078,37 +1088,37 @@ "error_title": "Oops!", "error_message": "The order of the words is incorrect. Please make sure you wrote it down correctly and go to the previous screen if it is necessary", "back": "Back", - "title": "Confirm seed phrase", + "title": "Confirm Secret Recovery phrase", "info_text": "Insert each word in the order it was presented to you on the previous screen.", "cta_text": "CONFIRM PHRASE", - "modal_title": "Seed phrase confirmed!", + "modal_title": "Secret recovery phrase confirmed!", "modal_text": "This was to ensure you follow this security measure", "modal_button": "NEXT" }, "account_backup_step_6": { "title": "Security Tips", - "info_text": "MetaMask cannot recover your seed phrase if you lose it", - "tip_1": "Keep multiple backups of your seed phrase", + "info_text": "MetaMask cannot recover your Secret Recovery phrase if you lose it", + "tip_1": "Keep multiple backups of your Secret Recovery phrase", "tip_2": "Store the phrase in a trusted password manager and paper backups in a safe place", "tip_3": "Never share this phrase with anyone", - "disclaimer": "* You can find your seedphrase if you go to ", + "disclaimer": "* You can find your Secret recovery phrase if you go to ", "disclaimer_bold": "Settings > Security & Privacy", "cta_text": "GOT IT!", "modal_title": "Congratulations!", "modal_text": "You are all backed up and ready to go!", "modal_button": "DONE", - "copy_seed_phrase": "COPY SEED PHRASE TO CLIPBOARD" + "copy_seed_phrase": "COPY SECRET RECOVERY PHRASE TO CLIPBOARD" }, "manual_backup": { "progressOne": "Create Password", "progressTwo": "Secure wallet", - "progressThree": "Confirm seed phrase" + "progressThree": "Confirm Secret Recovery phrase" }, "manual_backup_step_1": { - "action": "Write down your seed phrase", - "info": "This is your seed phrase. Write it down on a paper and keep it in a safe place. You'll be asked to re-enter this phrase (in order) on the next step.", + "action": "Write down your Secret Recovery phrase", + "info": "This is your Secret Recovery phrase. Write it down on a paper and keep it in a safe place. You'll be asked to re-enter this phrase (in order) on the next step.", "continue": "Continue", - "reveal": "Tap to reveal your seed phrase", + "reveal": "Tap to reveal your Secret Recovery phrase", "watching": "Make sure no one is watching your screen.", "view": "View", "confirm_password": "Confirm your password", @@ -1116,21 +1126,21 @@ "confirm": "CONFIRM" }, "manual_backup_step_2": { - "action": "Confirm seed phrase", + "action": "Confirm Secret Recovery phrase", "info": "Select each word in the order it was presented to you.", "complete": "Complete Backup", "success": "Success" }, "manual_backup_step_3": { "congratulations": "Congratulations", - "success": "You’ve successfully protected your wallet. Remember to keep your seed phrase safe, it's your responsibility!", + "success": "You’ve successfully protected your wallet. Remember to keep your Secret Recovery phrase safe, it's your responsibility!", "hint": "Leave yourself a hint?", - "recover": "MetaMask cannot recover your wallet should you lose it. You can find your seedphrase in Settings > Security & Privacy.", + "recover": "MetaMask cannot recover your wallet should you lose it. You can find your Secret recovery phrase in Settings > Security & Privacy.", "learn": "Learn more", "done": "Done", "recovery_hint": "Recovery hint", "leave_hint": "Leave yourself a hint. Write the location / where you saved it to remind yourself how you can access it. This information does not leave your device.", - "no_seedphrase": "Do not use this to write your seed phrase.", + "no_seedphrase": "Do not use this to write your Secret Recovery phrase.", "example": "e.g. Mom's house", "save": "Save", "cancel": "Cancel" @@ -1186,8 +1196,8 @@ }, "protect_your_wallet_modal": { "title": "Protect your wallet", - "body_for_password": "Protect your wallet by setting a password and saving your seed phrase (required).", - "body_for_seedphrase": "Now that value was added to your wallet, protect your wallet by setting a password and saving your seed phrase (required).", + "body_for_password": "Protect your wallet by setting a password and saving your Secret Recovery phrase (required).", + "body_for_seedphrase": "Now that value was added to your wallet, protect your wallet by setting a password and saving your Secret Recovery phrase (required).", "button": "Protect wallet" }, "payment_request": { @@ -1369,6 +1379,8 @@ "verify_on": "Always verify the token address on", "verify_address_on": "Verify token address on", "only_verified_on": "{{symbol}} is only verified on {{occurances}} source.", + "block_explorer": "block explorer", + "a_block_explorer": "a block explorer", "token_verification": "Token verification", "token_multiple": "Multiple tokens can use the same name and symbol.", "token_check": "Check", @@ -1482,7 +1494,7 @@ "title": "Protect your wallet", "top_button": "Protect wallet", "bottom_button": "Remind me later", - "text": "Don’t risk losing your funds. Protect your wallet by saving your seed phrase in a place you trust.", + "text": "Don’t risk losing your funds. Protect your wallet by saving your Secret Recovery phrase in a place you trust.", "text_bold": "It’s the only way to recover your wallet if you get locked out of the app or get a new device.", "action": "Learn more" }, @@ -1502,15 +1514,15 @@ "submit_ticket_6": "here.", "submit_ticket_7": "Please include the error message and the screenshot.", "save_seedphrase_1": "If this error persists,", - "save_seedphrase_2": "save your seed phrase", - "save_seedphrase_3": "& re-install the app. Note: you can NOT restore your wallet without your seed phrase.", + "save_seedphrase_2": "save your Secret Recovery phrase", + "save_seedphrase_3": "& re-install the app. Note: you can NOT restore your wallet without your Secret Recovery phrase.", "copied_clipboard": "Copied to clipboard", "ok": "OK" }, "whats_new": { "title": "See what's new", "feature_security_settings_title": "Improved security settings", - "feature_security_settings_text": "You can now change your MetaMask app password & review how to backup your wallet seed phrase from Settings > Security & Privacy.", + "feature_security_settings_text": "You can now change your MetaMask app password & review how to backup your wallet Secret Recovery phrase from Settings > Security & Privacy.", "feature_security_settings_button": "View in Settings" }, "invalid_network": { diff --git a/package.json b/package.json index 61a48e635d0..c6d68c79465 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "metamask", - "version": "2.2.0", + "version": "2.3.0", "private": true, "scripts": { "watch": "./scripts/build.sh watcher watch", @@ -65,6 +65,8 @@ ] }, "resolutions": { + "**/lodash": "^4.17.21", + "**/ua-parser-js": "^0.7.24", "**/elliptic": "^6.5.4", "**/y18n": "^3.2.2", "pubnub/**/netmask": "^2.0.1", @@ -73,10 +75,11 @@ "react-native-level-fs/**/semver": "^4.3.2" }, "dependencies": { - "@estebanmino/controllers": "^3.3.17", "@exodus/react-native-payments": "https://github.com/wachunei/react-native-payments.git#package-json-hack", "@metamask/contract-metadata": "^1.23.0", - "@metamask/controllers": "^7.0.0", + "@metamask/controllers": "^8.0.0", + "@metamask/etherscan-link": "^2.0.0", + "@metamask/swaps-controller": "^4.0.0", "@react-native-community/async-storage": "1.12.1", "@react-native-community/blur": "^3.6.0", "@react-native-community/checkbox": "^0.4.2", @@ -86,11 +89,11 @@ "@react-native-community/netinfo": "6.0.0", "@react-native-community/viewpager": "^3.3.0", "@rnhooks/keyboard": "^0.0.3", - "@sentry/integrations": "5.13.0", - "@sentry/react-native": "1.3.3", + "@sentry/integrations": "6.3.1", + "@sentry/react-native": "2.4.2", "@tradle/react-native-http": "2.0.1", - "@walletconnect/client": "1.3.4", - "@walletconnect/utils": "1.3.4", + "@walletconnect/client": "^1.4.1", + "@walletconnect/utils": "^1.4.1", "asyncstorage-down": "4.2.0", "axios": "^0.21.1", "babel-plugin-transform-inline-environment-variables": "0.4.3", @@ -147,7 +150,7 @@ "react-native-confetti": "^0.1.0", "react-native-confetti-cannon": "^1.5.0", "react-native-crypto": "2.1.2", - "react-native-device-info": "3.1.4", + "react-native-device-info": "^8.1.3", "react-native-elevated-view": "0.0.6", "react-native-emoji": "1.3.1", "react-native-fade-in-image": "1.4.1", @@ -178,8 +181,11 @@ "react-native-svg": "12.1.0", "react-native-swipe-gestures": "1.0.3", "react-native-tcp": "aprock/react-native-tcp#11/head", + "react-native-url-polyfill": "^1.3.0", "react-native-v8": "^0.62.2-patch.1", "react-native-vector-icons": "6.4.2", + "react-native-video": "^5.1.1", + "react-native-video-controls": "^2.7.1", "react-native-view-shot": "^3.1.2", "react-native-webview": "^11.0.2", "react-navigation": "^4.4.3", @@ -196,8 +202,9 @@ "rn-fetch-blob": "^0.12.0", "stream-browserify": "1.0.0", "through2": "3.0.1", + "unicode-confusables": "^0.1.1", "url": "0.11.0", - "url-parse": "1.4.4", + "url-parse": "1.5.0", "valid-url": "1.0.9", "vm-browserify": "1.1.2", "web3-provider-engine": "^16.0.1", @@ -213,11 +220,11 @@ "babel-eslint": "10.1.0", "babel-jest": "^26.6.3", "concat-cli": "4.0.0", - "detox": "17.3.1", + "detox": "17.14.9", "enzyme": "3.9.0", "enzyme-adapter-react-16": "1.10.0", "enzyme-to-json": "3.3.5", - "eslint": "^6.5.1", + "eslint": "^7.14.0", "eslint-config-react-native": "4.0.0", "eslint-plugin-import": "2.18.2", "eslint-plugin-prettier": "^3.3.1", @@ -332,7 +339,7 @@ "fs": "react-native-level-fs" }, "engines": { - "node": "^10.17.0", + "node": "^14.0.0", "yarn": "^1.22.0" }, "rnpm": { diff --git a/patches/unicode-confusables+0.1.1.patch b/patches/unicode-confusables+0.1.1.patch new file mode 100644 index 00000000000..9b90e6b4c18 --- /dev/null +++ b/patches/unicode-confusables+0.1.1.patch @@ -0,0 +1,15 @@ +diff --git a/node_modules/unicode-confusables/data/confusables.json b/node_modules/unicode-confusables/data/confusables.json +index 855e49c..b0b8a0b 100644 +--- a/node_modules/unicode-confusables/data/confusables.json ++++ b/node_modules/unicode-confusables/data/confusables.json +@@ -157,8 +157,8 @@ + "໊": "๊", + "໋": "๋", + "꙯": "⃩", +- "
": " ", +- "
": " ", ++ "\u2028": " ", ++ "\u2029": " ", + " ": " ", + " ": " ", + " ": " ", diff --git a/yarn.lock b/yarn.lock index 4ff283b7705..e42458fa18a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.1", "@babel/code-frame@^7.10.4": +"@babel/code-frame@7.12.11", "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.1", "@babel/code-frame@^7.10.4": version "7.12.11" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== @@ -871,34 +871,20 @@ dependencies: "@types/hammerjs" "^2.0.36" -"@estebanmino/controllers@^3.3.17": - version "3.3.17" - resolved "https://registry.yarnpkg.com/@estebanmino/controllers/-/controllers-3.3.17.tgz#22f06daf2b5a004bcf40a12f905699263276159f" - integrity sha512-tmONppQxqLOW7uZSSk9gUAiNokU1tW2LezPggjZjzOp9CTBIc3cgr28o07hss7DF4+8IX6XOEtAjfeVgUUCQ2Q== +"@eslint/eslintrc@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.0.tgz#99cc0a0584d72f1df38b900fb062ba995f395547" + integrity sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog== dependencies: - "@metamask/contract-metadata" "^1.22.0" - abort-controller "^3.0.0" - async-mutex "^0.3.1" - bignumber.js "^9.0.1" - eth-ens-namehash "^2.0.8" - eth-json-rpc-infura "^5.1.0" - eth-keyring-controller "^6.1.0" - eth-method-registry "1.1.0" - eth-phishing-detect "^1.1.13" - eth-query "^2.1.2" - eth-rpc-errors "^4.0.0" - eth-sig-util "^3.0.0" - ethereumjs-util "^6.1.0" - ethereumjs-wallet "^1.0.1" - human-standard-collectible-abi "^1.0.2" - human-standard-token-abi "^2.0.0" - isomorphic-fetch "^3.0.0" - jsonschema "^1.2.4" - nanoid "^3.1.12" - single-call-balance-checker-abi "^1.0.0" - uuid "^8.3.2" - web3 "^0.20.7" - web3-provider-engine "^16.0.1" + ajv "^6.12.4" + debug "^4.1.1" + espree "^7.3.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.2.1" + js-yaml "^3.13.1" + minimatch "^3.0.4" + strip-json-comments "^3.1.1" "@ethersproject/abi@^5.0.5": version "5.0.5" @@ -1534,36 +1520,36 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" -"@json-rpc-tools/types@^1.5.7": - version "1.5.7" - resolved "https://registry.yarnpkg.com/@json-rpc-tools/types/-/types-1.5.7.tgz#6bfce4db9bfe5a40123866c520351c454f8da195" - integrity sha512-IlcqmYjrwAQ3sexea/0CB0JVFRrjblpk7SzlbiqHCNtYczgWIT8wrezpfFVC6S1XfIvKm9fIhqg2zU4FUsfh6w== +"@json-rpc-tools/types@^1.6.1": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@json-rpc-tools/types/-/types-1.7.1.tgz#e341947679c57192b13e7d2e4ec49009b1c448c2" + integrity sha512-SNhhx84LQwc9/QnOpdVd8LvXqurB0WcfIEe6961Qx+9ixeK1+U3Dt4tZTPkVohyKwBZ6YyRsqQl9ZYsy2SjdxQ== dependencies: - keyvaluestorage "^0.6.0" + keyvaluestorage-interface "^1.0.0" -"@json-rpc-tools/utils@1.5.7": - version "1.5.7" - resolved "https://registry.yarnpkg.com/@json-rpc-tools/utils/-/utils-1.5.7.tgz#932fa5b7885919e746caff0d32a61b613c631fd6" - integrity sha512-AE7p5p15Lgu/L6onIaQfvIEUHD3U95hmOBNp3rdIe/lTjZIZrwm+CUiA/UAa+4QqwlG74Ri4fKbm+uriW5HaZA== +"@json-rpc-tools/utils@1.6.1": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@json-rpc-tools/utils/-/utils-1.6.1.tgz#26e37d0fc4522721158d0f6057e136daa8813263" + integrity sha512-cNwP4QapAls+xATU8zLLqPYa9qCbgwEyWEK7vE1oH91b3LfbUYwHtiWZ1+rv0X/mh/9cWNTo2Oi2Sah/QX0WwA== dependencies: - "@json-rpc-tools/types" "^1.5.7" - -"@metamask/contract-metadata@^1.22.0": - version "1.22.0" - resolved "https://registry.yarnpkg.com/@metamask/contract-metadata/-/contract-metadata-1.22.0.tgz#55cc84756c703c433176b484b1d34f0e03d16d1e" - integrity sha512-t4ijbU+4OH9UAlrPkfLPFo6KmkRTRZJHB+Vly4ajF8oZMnota5YjVVl/SmltsoRC9xvJtRn9DUVf3YMHMIdofw== + "@json-rpc-tools/types" "^1.6.1" "@metamask/contract-metadata@^1.23.0": version "1.23.0" resolved "https://registry.yarnpkg.com/@metamask/contract-metadata/-/contract-metadata-1.23.0.tgz#c70be7f3eaeeb791651ce793b7cdc230e9780b18" integrity sha512-oTUqL9dtXtbng60DZMRsBmZ5HiOUUfEsZjuswOJ0yHO24YsW0ktCcgCJVYPv1HcOsF0SVrRtG4rtrvOl4nY+HA== -"@metamask/controllers@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@metamask/controllers/-/controllers-7.0.0.tgz#8daecd284faa897ca1f112a3f6f28d6936dac674" - integrity sha512-vb2/wgGfJFMUa4Ej67FMkV94s0vp765t2vwOt8EOxhWfmEP2v9myc2B95L5jJJZZgvCk2ojaV11CrFF4ookLng== +"@metamask/contract-metadata@^1.24.0": + version "1.25.0" + resolved "https://registry.yarnpkg.com/@metamask/contract-metadata/-/contract-metadata-1.25.0.tgz#442ace91fb40165310764b68d8096d0017bb0492" + integrity sha512-yhmYB9CQPv0dckNcPoWDcgtrdUp0OgK0uvkRE5QIBv4b3qENI1/03BztvK2ijbTuMlORUpjPq7/1MQDUPoRPVw== + +"@metamask/controllers@^8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@metamask/controllers/-/controllers-8.0.0.tgz#42ac5aaef67a03d3fe599a67a36597e01902ca8d" + integrity sha512-TrteMifsCxV1g3WHcSD1X98fF4hKep3sXZNGfrvkPqa8mrF03hJke21WBSTRtvJ3vkNLRWgi+5I6lVXFTzbYuQ== dependencies: - "@metamask/contract-metadata" "^1.23.0" + "@metamask/contract-metadata" "^1.24.0" "@types/uuid" "^8.3.0" async-mutex "^0.2.6" babel-runtime "^6.26.0" @@ -1590,6 +1576,11 @@ web3 "^0.20.7" web3-provider-engine "^16.0.1" +"@metamask/etherscan-link@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@metamask/etherscan-link/-/etherscan-link-2.0.0.tgz#89035736515a39532ba1142d87b9a8c2b4f920f1" + integrity sha512-/YS32hS2UTTxs0KyUmAgaDj1w4dzAvOrT+p4TJtpICeH3E/k51r2FO0Or7WJJI/mpzTqNKgcH5yyS2oCtupGiA== + "@metamask/mobile-provider@^2.0.1": version "2.0.1" resolved "https://registry.yarnpkg.com/@metamask/mobile-provider/-/mobile-provider-2.0.1.tgz#892f883deafe49200a3ae57d85237016ded63c12" @@ -1600,6 +1591,20 @@ resolved "https://registry.yarnpkg.com/@metamask/safe-event-emitter/-/safe-event-emitter-2.0.0.tgz#af577b477c683fad17c619a78208cede06f9605c" integrity sha512-/kSXhY692qiV1MXu6EeOZvg5nECLclxNXcKCxJ3cXQgYuRymRHpdx/t7JXfsK+JLjwA1e1c1/SBrlQYpusC29Q== +"@metamask/swaps-controller@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@metamask/swaps-controller/-/swaps-controller-4.0.0.tgz#60c1c1b8fd7ed90bb86a7402dccfee69322d29a6" + integrity sha512-D5D4Ld6rJRkJurAVUkWNTuijrQR4IqXaXh5mJAJn2IDgncXWK8XvIuIpFz/z6dURuoj/JIbfqMB2jX4GsWrBMA== + dependencies: + "@metamask/controllers" "^8.0.0" + abort-controller "^3.0.0" + async-mutex "^0.3.1" + bignumber.js "^9.0.1" + eth-query "^2.1.2" + ethereumjs-util "^7.0.10" + human-standard-token-abi "^2.0.0" + web3 "^0.20.7" + "@pedrouid/iso-crypto@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@pedrouid/iso-crypto/-/iso-crypto-1.0.0.tgz#cf06b40ef3da3d7ca7363bd7a521ed59fa2fd13d" @@ -1828,14 +1833,14 @@ resolved "https://registry.yarnpkg.com/@rnhooks/keyboard/-/keyboard-0.0.3.tgz#e17a62a9f1e4f25efdf0afa4359b82e3dbea6523" integrity sha512-tBaDWQkcLgeEQCol/6NkB8JyRkvS7L3//mkkOSNOoeLc74Fttz8kiLUsSj9cBwSyFCrWP2K04Tn8zNgWfdFQYg== -"@sentry/browser@^5.12.1": - version "5.19.1" - resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-5.19.1.tgz#b22f36fc71f36719ad352a54e6b31722622128c0" - integrity sha512-Aon5Nc2n8sIXKg6Xbr4RM3/Xs7vFpXksL56z3yIuGrmpCM8ToQ25/tQv8h+anYi72x5bn1npzaXB/NwU1Qwfhg== +"@sentry/browser@6.2.1": + version "6.2.1" + resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-6.2.1.tgz#f9f277e6f8cad0c7efd1a01726095d63a47a1c16" + integrity sha512-OAikFZ9EimD3noxMp8tA6Cf6qJcQ2U8k5QSgTPwdx+09nZOGJzbRFteK7WWmrS93ZJdzN61lpSQbg5v+bmmfbQ== dependencies: - "@sentry/core" "5.19.1" - "@sentry/types" "5.19.1" - "@sentry/utils" "5.19.1" + "@sentry/core" "6.2.1" + "@sentry/types" "6.2.1" + "@sentry/utils" "6.2.1" tslib "^1.9.3" "@sentry/cli@^1.52.4": @@ -1849,95 +1854,123 @@ progress "^2.0.3" proxy-from-env "^1.1.0" -"@sentry/core@5.19.1", "@sentry/core@^5.12.0": - version "5.19.1" - resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.19.1.tgz#f5ff7feb1118035f75f1d0bc2a76e2b040d2aa8e" - integrity sha512-BGGxjeT95Og/hloBhQXAVcndVXPmIU6drtF3oKRT12cBpiG965xEDEUwiJVvyb5MAvojdVEZBK2LURUFY/d7Zw== +"@sentry/core@6.2.1": + version "6.2.1" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-6.2.1.tgz#8b177e9bf591e2e7ddcb04f0b1403de3f5aa8755" + integrity sha512-jPqQEtafxxDtLONhCbTHh/Uq8mZRhsfbwJTSVYfPVEe/ELfFZLQK7tP6rOh7zEWKbTkE0mE6XcaoH3ZRAhgrqg== dependencies: - "@sentry/hub" "5.19.1" - "@sentry/minimal" "5.19.1" - "@sentry/types" "5.19.1" - "@sentry/utils" "5.19.1" + "@sentry/hub" "6.2.1" + "@sentry/minimal" "6.2.1" + "@sentry/types" "6.2.1" + "@sentry/utils" "6.2.1" tslib "^1.9.3" -"@sentry/hub@5.19.1": - version "5.19.1" - resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.19.1.tgz#f3bc8500680974ce43c1eedcd8e90696cc18b306" - integrity sha512-XjfbNGWVeDsP38alm5Cm08YPIw5Hu6HbPkw7a3y1piViTrg4HdtsE+ZJqq0YcURo2RTpg6Ks6coCS/zJxIPygQ== +"@sentry/hub@6.2.1": + version "6.2.1" + resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-6.2.1.tgz#35bc6bf841a93f4354b3a17592c938b3dba20b73" + integrity sha512-pG7wCQeRpzeP6t0bT4T0X029R19dbDS3/qswF8BL6bg0AI3afjfjBAZm/fqn1Uwe/uBoMHVVdbxgJDZeQ5d4rQ== dependencies: - "@sentry/types" "5.19.1" - "@sentry/utils" "5.19.1" + "@sentry/types" "6.2.1" + "@sentry/utils" "6.2.1" tslib "^1.9.3" -"@sentry/integrations@5.13.0": - version "5.13.0" - resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-5.13.0.tgz#358e3225ff302353ea38214daa4a5eed1eb5d4aa" - integrity sha512-xfi3LyspqFxb2xaSKu5MMrA4kuN8waagbOjInAut48jrdqATLioMjCcUruU/aHILAUCCznPW9zsR42J1OmVoMw== +"@sentry/integrations@6.2.1": + version "6.2.1" + resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-6.2.1.tgz#caa9b49de29523698668d45827633be86b2268ff" + integrity sha512-UBvuil/b9M5HGH6aBDzTiIVRsmpC/wqwDKy28IO05XLdalmKgJ9C1EQhoyN6xw+1lINpXXFtfq4NhfgZgWbc7Q== dependencies: - "@sentry/types" "5.12.4" - "@sentry/utils" "5.13.0" + "@sentry/types" "6.2.1" + "@sentry/utils" "6.2.1" + localforage "^1.8.1" tslib "^1.9.3" -"@sentry/integrations@^5.12.0": - version "5.19.1" - resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-5.19.1.tgz#9c6680eceff95b9fb66ad18c2cf9fa2e9f0181b5" - integrity sha512-JMiYtHh6m50eMC8BHkcZZjl6/Nx2JVivhkLx6EGsbgNTpGlsODhrRt+kgRKFhPWhEjKlcjx+l6tmq90NbfLusg== +"@sentry/integrations@6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-6.3.1.tgz#8bd4c05a83c5fe8ece6cb59a6e31e1e632a14af8" + integrity sha512-fB0+CmU2L2VJ8WyI33t060lxpBNAoh092jzMGEnnfPKTVMxnscjFrISzrWXQZs/OoR6q8Yo/+pZAT5gWA0dDOQ== dependencies: - "@sentry/types" "5.19.1" - "@sentry/utils" "5.19.1" + "@sentry/types" "6.3.1" + "@sentry/utils" "6.3.1" + localforage "^1.8.1" tslib "^1.9.3" -"@sentry/minimal@5.19.1": - version "5.19.1" - resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.19.1.tgz#04043d93a7dc90cbed1a31d80f6bf59688ea3100" - integrity sha512-pgNfsaCroEsC8gv+NqmPTIkj4wyK6ZgYLV12IT4k2oJLkGyg45TSAKabyB7oEP5jsj8sRzm8tDomu8M4HpaCHg== +"@sentry/minimal@6.2.1": + version "6.2.1" + resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-6.2.1.tgz#8f01480e1b56bc7dd54adf925e5317f233e19384" + integrity sha512-wuSXB4Ayxv9rBEQ4pm7fnG4UU2ZPtPnnChoEfd4/mw1UthXSvmPFEn6O4pdo2G8fTkl8eqm6wT/Q7uIXMEmw+A== dependencies: - "@sentry/hub" "5.19.1" - "@sentry/types" "5.19.1" + "@sentry/hub" "6.2.1" + "@sentry/types" "6.2.1" tslib "^1.9.3" -"@sentry/react-native@1.3.3": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@sentry/react-native/-/react-native-1.3.3.tgz#a2acb53171c743d89e92eb578dde9af9e6a91422" - integrity sha512-OGeq1igrYTtgPYnOZEEWFRPyDESiGCkvyShL6YJ7U7kaDq5Fw3hgOHJovo/QhsQC7qVaYjgaMPX3Ht08xbh4IQ== - dependencies: - "@sentry/browser" "^5.12.1" - "@sentry/core" "^5.12.0" - "@sentry/integrations" "^5.12.0" - "@sentry/types" "^5.12.0" - "@sentry/utils" "^5.12.0" - "@sentry/wizard" "^1.1.1" - -"@sentry/types@5.12.4": - version "5.12.4" - resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.12.4.tgz#6e52639bc3b4e136e9a0da5385890f8f78bb7697" - integrity sha512-JoN3YIp7Z+uxUZArj2B6NcEoXFQDhd0kqO0QpfiHZyg4Dhx2/E2aHuVx0H6Fndk+60iEZSECaCBXe2MOPo4fqA== - -"@sentry/types@5.19.1", "@sentry/types@^5.12.0": - version "5.19.1" - resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.19.1.tgz#8762f668d3fc2416fbde31d15d13009544caeb54" - integrity sha512-M5MhTLnjqYFwxMwcFPBpBgYQqI9hCvtVuj/A+NvcBHpe7VWOXdn/Sys+zD6C76DWGFYQdw3OWCsZimP24dL8mA== - -"@sentry/utils@5.13.0": - version "5.13.0" - resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.13.0.tgz#6463e53b6178dbbd3b90e671517cbca82744b055" - integrity sha512-BcmNQN+IfFbVWGnEwXHku69zqJc97sjBRYVxpStKMaO/4aLVIQcOJCMWxVJtVoSVAHQaigBZmFutWH7EJMRJxg== - dependencies: - "@sentry/types" "5.12.4" +"@sentry/react-native@2.4.2": + version "2.4.2" + resolved "https://registry.yarnpkg.com/@sentry/react-native/-/react-native-2.4.2.tgz#82095e33a2dcd7720c2f71f35f02329ab7351547" + integrity sha512-+GAH2cdbZBz+EJOpBGAvVRl2jExLYrZ/gfmHnew3NYGlE/77GX1KQGJ+sKLA6xnPtXjcC7tJ13uvbQD6cltZnQ== + dependencies: + "@sentry/browser" "6.2.1" + "@sentry/core" "6.2.1" + "@sentry/hub" "6.2.1" + "@sentry/integrations" "6.2.1" + "@sentry/react" "6.2.1" + "@sentry/tracing" "6.2.1" + "@sentry/types" "6.2.1" + "@sentry/utils" "6.2.1" + "@sentry/wizard" "^1.2.2" + +"@sentry/react@6.2.1": + version "6.2.1" + resolved "https://registry.yarnpkg.com/@sentry/react/-/react-6.2.1.tgz#26587f3f47e9699003b04ac558d8aa8a2b7416d7" + integrity sha512-emJnYVASM2hej2f8eSjqiDRMljwLsDJDSwa6kVc5HUOs9gnVrE4MR+vSywraACf5tKZbH1YI+NUXCmR++fIB0g== + dependencies: + "@sentry/browser" "6.2.1" + "@sentry/minimal" "6.2.1" + "@sentry/types" "6.2.1" + "@sentry/utils" "6.2.1" + hoist-non-react-statics "^3.3.2" tslib "^1.9.3" -"@sentry/utils@5.19.1", "@sentry/utils@^5.12.0": - version "5.19.1" - resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.19.1.tgz#e1134db40e4bb9732251e515721cec7ee94d4d9c" - integrity sha512-neUiNBnZSHjWTZWy2QV02EHTx1C2L3DBPzRXlh0ca5xrI7LMBLmhkHlhebn1E5ky3PW1teqZTgmh0jZoL99TEA== +"@sentry/tracing@6.2.1": + version "6.2.1" + resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-6.2.1.tgz#61c18c43c5390c348b35dafe73947ab379252d8f" + integrity sha512-bvStY1SnL08wkSeVK3j9K5rivQQJdKFCPR2VYRFOCaUoleZ6ChPUnBvxQ/E2LXc0hk/y/wo1q4r5B0dfCCY+bQ== dependencies: - "@sentry/types" "5.19.1" + "@sentry/hub" "6.2.1" + "@sentry/minimal" "6.2.1" + "@sentry/types" "6.2.1" + "@sentry/utils" "6.2.1" tslib "^1.9.3" -"@sentry/wizard@^1.1.1": - version "1.1.4" - resolved "https://registry.yarnpkg.com/@sentry/wizard/-/wizard-1.1.4.tgz#df51af4271d11f729b938dde4864514b69e4aac9" - integrity sha512-xVpL0lnQK2bbEwUKKjs3dKhy27va8HW75Q8r1vaR63iBCpB5LpP4Q4NN5G/VEWdYnH8rcazsOA207716E1cm4g== +"@sentry/types@6.2.1": + version "6.2.1" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.2.1.tgz#28c946230b2023f72307b65606d32052ad9e5353" + integrity sha512-h0OV1QT+fv5ojfK5/+iEXClu33HirmvbjcQC2jf05IHj9yXIOWy6EB10S8nBjuLiiFqQiAQYj3FN9Ip4eN8NJA== + +"@sentry/types@6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.3.1.tgz#af3b54728b29f633f38fbe51b8c10e3834fbc158" + integrity sha512-BEBn8JX1yaooCAuonbaMci9z0RjwwMbQ3Eny/eyDdd+rjXprZCZaStZnCvSThbNBqAJ8YaUqY2YBMnEwJxarAw== + +"@sentry/utils@6.2.1": + version "6.2.1" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.2.1.tgz#bfcb12c20d44bf2aeb0073b1264703c11c179ebd" + integrity sha512-6kQgM/yBPdXu+3qbJnI6HBcWztN9QfiMkH++ZiKk4ERhg9d2LYWlze478uTU5Fyo/JQYcp+McpjtjpR9QIrr0g== + dependencies: + "@sentry/types" "6.2.1" + tslib "^1.9.3" + +"@sentry/utils@6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.3.1.tgz#6d8e691139b5b49d8c655ad1dcaf2cb3ff0d0b03" + integrity sha512-cdtl/QWC9FtinAuW3w8QfvSfh/Q9ui5vwvjzVHiS1ga/U38edi2XX+cttY39ZYwz0SQG99cE10GOIhd1p7/mAA== + dependencies: + "@sentry/types" "6.3.1" + tslib "^1.9.3" + +"@sentry/wizard@^1.2.2": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@sentry/wizard/-/wizard-1.2.6.tgz#bb4e176e79dcab26c2ca18ddfbd0cc4a97645327" + integrity sha512-2flmpdOGfKXutHbiTRYWE4j5rUyTZGVHacai2A+z5giDN4WMSr1sqy04weyZzxF/fqMN6KMkDE5wtmK+btnEfw== dependencies: "@sentry/cli" "^1.52.4" chalk "^2.4.1" @@ -2005,6 +2038,13 @@ dependencies: "@types/node" "*" +"@types/bn.js@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" + integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA== + dependencies: + "@types/node" "*" + "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" @@ -2165,62 +2205,71 @@ semver "^7.3.2" tsutils "^3.17.1" -"@walletconnect/client@1.3.4": - version "1.3.4" - resolved "https://registry.yarnpkg.com/@walletconnect/client/-/client-1.3.4.tgz#6468f562a5df59dd33498da2649508e6b8261e63" - integrity sha512-CRV4npHVNb7D1BVRRCiVQ3YBGNp7lEq8Ikkrgg1eWDAXCUNTtzAhmGo6uCzxjh6cpvfiHQNjZbShrqP+d286QQ== +"@walletconnect/browser-utils@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@walletconnect/browser-utils/-/browser-utils-1.4.1.tgz#a8d5a038d28c19b739eb0ff4194ced140c922d36" + integrity sha512-ONrkPSI/27o1Wj8kUwE0uUZFk0GDCDQBJy614GsrhcwuQwJEW/B+nXPQ+Ca/4WvQySM5hWVHp1gO1kozSUkh3A== dependencies: - "@walletconnect/core" "^1.3.4" - "@walletconnect/iso-crypto" "^1.3.4" - "@walletconnect/types" "^1.3.4" - "@walletconnect/utils" "^1.3.4" + "@walletconnect/types" "^1.4.1" + detect-browser "5.2.0" + safe-json-utils "1.0.0" + window-getters "1.0.0" + window-metadata "1.0.0" -"@walletconnect/core@^1.3.4": - version "1.3.4" - resolved "https://registry.yarnpkg.com/@walletconnect/core/-/core-1.3.4.tgz#2059404ea778f582904bd71ab5ecf5a5bf6266b2" - integrity sha512-CwAiQYT7oo3MwrYIOoLSce8iaZT5mNwVavc4s3S2TvkxoVc+ORywaXvUDavgFfWqW0P4gg4tO+e8QPD2Mxfm1w== +"@walletconnect/client@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@walletconnect/client/-/client-1.4.1.tgz#c9c50df5afde23a35e23d96fe6d207c102e53850" + integrity sha512-JRW+9+j9LwszY76/WcIumEiLmhX7eidorH9SFFmI2pFfbrhB6KLe87FaA106kxwZUyWKOLZ6jVV4d1urYSdEwA== dependencies: - "@walletconnect/socket-transport" "^1.3.4" - "@walletconnect/types" "^1.3.4" - "@walletconnect/utils" "^1.3.4" + "@walletconnect/core" "^1.4.1" + "@walletconnect/iso-crypto" "^1.4.1" + "@walletconnect/types" "^1.4.1" + "@walletconnect/utils" "^1.4.1" -"@walletconnect/iso-crypto@^1.3.4": - version "1.3.4" - resolved "https://registry.yarnpkg.com/@walletconnect/iso-crypto/-/iso-crypto-1.3.4.tgz#3fb64d19509da8e3ef4b0ad0c2aaf4285c240968" - integrity sha512-LHH3x+ISyIzF+5RYmQxd9FesNSCfQvRlCmEFjBp0NjlqRr0q1hRD4kj71+B7B0W+5a9zwS5FkkKJ2gaxxNntdg== +"@walletconnect/core@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@walletconnect/core/-/core-1.4.1.tgz#68310ee7c9737a7a0a7f1308abbfc1c31212b9a6" + integrity sha512-NzWvhk4akI2uhORUxMDMS/8yAdfp+nzvb5QdTE0eTD0WOrK16qAfYLSU/IjFc2J2lqhuPVxfO2XV7QoxgCXfwA== + dependencies: + "@walletconnect/socket-transport" "^1.4.1" + "@walletconnect/types" "^1.4.1" + "@walletconnect/utils" "^1.4.1" + +"@walletconnect/iso-crypto@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@walletconnect/iso-crypto/-/iso-crypto-1.4.1.tgz#0d9793c679d6c5443c49cce83f5d8dd476a65df2" + integrity sha512-rzfqM/DFhzNxBriMCU4DOarPkH+Brgll+2a2YeO6zHgMlwZtBKi5mMgzBwbDC3XygOvKbcRTB9G9hr8uYn+i5g== dependencies: "@pedrouid/iso-crypto" "^1.0.0" - "@walletconnect/types" "^1.3.4" - "@walletconnect/utils" "^1.3.4" + "@walletconnect/types" "^1.4.1" + "@walletconnect/utils" "^1.4.1" -"@walletconnect/socket-transport@^1.3.4": - version "1.3.4" - resolved "https://registry.yarnpkg.com/@walletconnect/socket-transport/-/socket-transport-1.3.4.tgz#ef4ab90664562009f0e54d6e0c8cf417f4337810" - integrity sha512-zjh3gQ0B6i5QyKSsQFAXDvF+Wttl/OVf2BiHKcHi9XSH+lFKJJeXDACmEja3ujRMEBw4U6nr/iH2X2kHeHkP+w== +"@walletconnect/socket-transport@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@walletconnect/socket-transport/-/socket-transport-1.4.1.tgz#d9b7ebb9a2843cc44cf96c880c62be78d4a1625f" + integrity sha512-/5Mhu4bu3tS52LqTlmmjx5x/N89XqbuT0YMobvQ+k/m+VqSeBDntqIjwBt7XiFlCbrUTq3/yTajavGFxWFB6pA== dependencies: - "@walletconnect/types" "^1.3.4" + "@walletconnect/types" "^1.4.1" + "@walletconnect/utils" "^1.4.1" ws "7.3.0" -"@walletconnect/types@^1.3.4": - version "1.3.4" - resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-1.3.4.tgz#e44d4817bb2299ff8c2731cbad2f023ea506d912" - integrity sha512-XK9cogoYdMkk8UcJIBi23iMazYdFLll0MeR10sjVY2rc1twqQ/8oiJHw8u8o1m5Qdw3Zxm30UrgBebPvcv9m5g== +"@walletconnect/types@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-1.4.1.tgz#48297238b86f846b8c694504ca45f0059a2cca88" + integrity sha512-lzS9NbXjVb5N+W/UnCZAflxjLtYepUi4ev1IeFozSvr/cWxAhEe/sjixe7WEIpYklW27kfBhKccMH/KjUoRC7w== -"@walletconnect/utils@1.3.4", "@walletconnect/utils@^1.3.4": - version "1.3.4" - resolved "https://registry.yarnpkg.com/@walletconnect/utils/-/utils-1.3.4.tgz#2ee9625cc4c30dc6e4aacd27bea962ab8d2e6587" - integrity sha512-AI/U5tvG7UfyLwTpo5hGL5ZFV31ypk/cG2MUOGoUYKfjKhyQcxX+wc6kXv2S+nBdS5aFgCS1aT9qHcbAIfOFHw== +"@walletconnect/utils@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@walletconnect/utils/-/utils-1.4.1.tgz#86108470c211a02609274a6c7bbd516c5182a22e" + integrity sha512-JrVjcXmWVcU02fmVNZFBpJ48f84qyar24CF7szGv+k9ZxvU9J7XkM+Fic4790Dt3DaWhOzS9/eBUa+BEZcBbNw== dependencies: - "@json-rpc-tools/utils" "1.5.7" - "@walletconnect/types" "^1.3.4" + "@json-rpc-tools/utils" "1.6.1" + "@walletconnect/browser-utils" "^1.4.1" + "@walletconnect/types" "^1.4.1" bn.js "4.11.8" - detect-browser "5.1.0" enc-utils "3.0.0" js-sha3 "0.8.0" query-string "6.13.5" - safe-json-utils "1.0.0" - window-getters "1.0.0" - window-metadata "1.0.0" "@yarnpkg/lockfile@^1.0.0", "@yarnpkg/lockfile@^1.1.0": version "1.1.0" @@ -2298,7 +2347,7 @@ acorn-jsx@^5.0.0: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe" integrity sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ== -acorn-jsx@^5.2.0: +acorn-jsx@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== @@ -2318,7 +2367,7 @@ acorn@^7.1.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.2.0.tgz#17ea7e40d7c8640ff54a694c889c26f31704effe" integrity sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ== -acorn@^7.1.1: +acorn@^7.4.0: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== @@ -2395,6 +2444,26 @@ ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5, ajv@^6.9.1: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^8.0.1: + version "8.1.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.1.0.tgz#45d5d3d36c7cdd808930cc3e603cf6200dbeb736" + integrity sha512-B/Sk2Ix7A36fs/ZkuGLIR86EdjbgR6fsAcbx9lOP/QBSXujDNbVmIS/U4Itz5k8fPFDeVZl/zQ/gJW4Jrq6XjQ== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + anser@^1.4.9: version "1.4.9" resolved "https://registry.yarnpkg.com/anser/-/anser-1.4.9.tgz#1f85423a5dcf8da4631a341665ff675b96845760" @@ -2515,19 +2584,6 @@ anymatch@^3.0.3: normalize-path "^3.0.0" picomatch "^2.0.4" -aproba@^1.0.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== - -are-we-there-yet@~1.1.2: - version "1.1.5" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" - integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== - dependencies: - delegates "^1.0.0" - readable-stream "^2.0.6" - argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -3021,15 +3077,6 @@ bech32@1.1.4: resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== -better-sqlite3@^7.1.2: - version "7.1.2" - resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-7.1.2.tgz#95565757a834093f1ecae0d4457f60820ed5dd2a" - integrity sha512-8FWYnJ6Bx94MBX03J5Ka7sTRlvXXMEm4FW2Op7nM8ErQZeyALYLmSlbMBnfr4cMpS0tj0aYZv0a+26G2YJuIjg== - dependencies: - bindings "^1.5.0" - prebuild-install "^5.3.3" - tar "^6.0.5" - big-integer@^1.6.44: version "1.6.48" resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e" @@ -3087,15 +3134,6 @@ bl@^1.2.3, bl@~0.8.1: readable-stream "^2.3.5" safe-buffer "^5.1.1" -bl@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.3.tgz#12d6287adc29080e22a705e5764b2a9522cdc489" - integrity sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg== - dependencies: - buffer "^5.5.0" - inherits "^2.0.4" - readable-stream "^3.4.0" - blakejs@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.1.0.tgz#69df92ef953aa88ca51a32df6ab1c54a155fc7a5" @@ -3440,6 +3478,11 @@ camelcase@^5.0.0, camelcase@^5.3.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== +camelcase@^6.0.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" + integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== + capture-exit@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" @@ -3531,16 +3574,6 @@ child-process-promise@^2.2.0: node-version "^1.0.0" promise-polyfill "^6.0.1" -chownr@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== - -chownr@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" - integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== - ci-info@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" @@ -3617,11 +3650,6 @@ cli-width@^2.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== -cli-width@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" - integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== - cliui@^3.0.3: version "3.2.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" @@ -3658,6 +3686,15 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" @@ -3866,11 +3903,6 @@ connect@^3.6.5: parseurl "~1.3.3" utils-merge "1.0.1" -console-control-strings@^1.0.0, console-control-strings@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= - contains-path@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" @@ -4026,7 +4058,7 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0: +cross-spawn@^7.0.0, cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -4164,18 +4196,16 @@ decamelize@^1.1.1, decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= +decamelize@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== + decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= -decompress-response@^4.2.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986" - integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw== - dependencies: - mimic-response "^2.0.0" - dedent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.6.0.tgz#0e6da8f0ce52838ef5cec5c8f9396b0c1b64a3cb" @@ -4230,7 +4260,7 @@ deep-extend@^0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== -deep-is@~0.1.3: +deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= @@ -4309,11 +4339,6 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= - denodeify@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/denodeify/-/denodeify-1.2.1.tgz#3a36287f5034e699e7577901052c2e6c94251631" @@ -4337,25 +4362,20 @@ destroy@~1.0.4: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= -detect-browser@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/detect-browser/-/detect-browser-5.1.0.tgz#0c51c66b747ad8f98a6832bf3026a5a23a7850ff" - integrity sha512-WKa9p+/MNwmTiS+V2AS6eGxic+807qvnV3hC+4z2GTY+F42h1n8AynVTMMc4EJBC32qMs6yjOTpeDEQQt/AVqQ== - -detect-libc@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= +detect-browser@5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/detect-browser/-/detect-browser-5.2.0.tgz#c9cd5afa96a6a19fda0bbe9e9be48a6b6e1e9c97" + integrity sha512-tr7XntDAu50BVENgQfajMLzacmSe34D+qZc4zjnniz0ZVuw/TZcLcyxHQjYpJTM36sGEkZZlYLnIM1hH7alTMA== detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== -detox@17.3.1: - version "17.3.1" - resolved "https://registry.yarnpkg.com/detox/-/detox-17.3.1.tgz#508a1358c3c0c56670d520b2ab7480dddbfc486f" - integrity sha512-UFhHxsjfaOdO0tf6tIMXYCEIClfhgHHfjU/XZmrLavL2GKSfS3udGu+EcQ/Iqt57C1GYCHfnxMi2pgghjASmRQ== +detox@17.14.9: + version "17.14.9" + resolved "https://registry.yarnpkg.com/detox/-/detox-17.14.9.tgz#e269de0937e615eac92ac283b2303751bcf2c072" + integrity sha512-WLpl7BJ2xecZJ02hFFWgzo61RnlO/LlsH/U8GWztoXsT0z0t9iKsBci8AhSSErQfH0IHnNtDcZknIxt0gzNhoQ== dependencies: bunyan "^1.8.12" bunyan-debug-stream "^1.1.0" @@ -4369,16 +4389,17 @@ detox@17.3.1: lodash "^4.17.5" minimist "^1.2.0" proper-lockfile "^3.0.2" + resolve-from "^5.0.0" sanitize-filename "^1.6.1" - shell-utils "^1.0.9" + shell-quote "^1.7.2" signal-exit "^3.0.3" tail "^2.0.0" telnet-client "1.2.8" tempfile "^2.0.0" which "^1.3.1" ws "^3.3.1" - yargs "^13.0.0" - yargs-parser "^13.0.0" + yargs "^16.0.3" + yargs-unparser "^2.0.0" diff-sequences@^25.2.6: version "25.2.6" @@ -4547,6 +4568,11 @@ elliptic@6.5.3, elliptic@^6.0.0, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5 minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" +eme-encryption-scheme-polyfill@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/eme-encryption-scheme-polyfill/-/eme-encryption-scheme-polyfill-2.0.3.tgz#2ca6e06480e06cceb5e50efd27943ac46c959878" + integrity sha512-44CNFMsqzHdKHrzWxlS7xZ8KUHn5XutBqpmCuWzNIynmAyFInHrrD3ozv/RvK9ZhgV6QY6Easx8EWAmxteNodg== + emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" @@ -4577,14 +4603,14 @@ encoding@^0.1.11: dependencies: iconv-lite "~0.4.13" -end-of-stream@^1.1.0, end-of-stream@^1.4.0, end-of-stream@^1.4.1: +end-of-stream@^1.1.0, end-of-stream@^1.4.0: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== dependencies: once "^1.4.0" -enquirer@^2.3.6: +enquirer@^2.3.5, enquirer@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== @@ -4749,6 +4775,11 @@ es6-promisify@^5.0.0: dependencies: es6-promise "^4.0.3" +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -4990,14 +5021,22 @@ eslint-scope@^5.0.0: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-utils@^1.3.1, eslint-utils@^1.4.3: +eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-utils@^1.3.1: version "1.4.3" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== dependencies: eslint-visitor-keys "^1.1.0" -eslint-utils@^2.0.0: +eslint-utils@^2.0.0, eslint-utils@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== @@ -5009,6 +5048,16 @@ eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== +eslint-visitor-keys@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + +eslint-visitor-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" + integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== + eslint@^5.6.0: version "5.16.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea" @@ -5051,46 +5100,46 @@ eslint@^5.6.0: table "^5.2.3" text-table "^0.2.0" -eslint@^6.5.1: - version "6.8.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" - integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig== +eslint@^7.14.0: + version "7.24.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.24.0.tgz#2e44fa62d93892bfdb100521f17345ba54b8513a" + integrity sha512-k9gaHeHiFmGCDQ2rEfvULlSLruz6tgfA8DEn+rY9/oYPFFTlz55mM/Q/Rij1b2Y42jwZiK3lXvNTw6w6TXzcKQ== dependencies: - "@babel/code-frame" "^7.0.0" + "@babel/code-frame" "7.12.11" + "@eslint/eslintrc" "^0.4.0" ajv "^6.10.0" - chalk "^2.1.0" - cross-spawn "^6.0.5" + chalk "^4.0.0" + cross-spawn "^7.0.2" debug "^4.0.1" doctrine "^3.0.0" - eslint-scope "^5.0.0" - eslint-utils "^1.4.3" - eslint-visitor-keys "^1.1.0" - espree "^6.1.2" - esquery "^1.0.1" + enquirer "^2.3.5" + eslint-scope "^5.1.1" + eslint-utils "^2.1.0" + eslint-visitor-keys "^2.0.0" + espree "^7.3.1" + esquery "^1.4.0" esutils "^2.0.2" - file-entry-cache "^5.0.1" + file-entry-cache "^6.0.1" functional-red-black-tree "^1.0.1" glob-parent "^5.0.0" - globals "^12.1.0" + globals "^13.6.0" ignore "^4.0.6" import-fresh "^3.0.0" imurmurhash "^0.1.4" - inquirer "^7.0.0" is-glob "^4.0.0" js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.3.0" - lodash "^4.17.14" + levn "^0.4.1" + lodash "^4.17.21" minimatch "^3.0.4" - mkdirp "^0.5.1" natural-compare "^1.4.0" - optionator "^0.8.3" + optionator "^0.9.1" progress "^2.0.0" - regexpp "^2.0.1" - semver "^6.1.2" - strip-ansi "^5.2.0" - strip-json-comments "^3.0.1" - table "^5.2.3" + regexpp "^3.1.0" + semver "^7.2.1" + strip-ansi "^6.0.0" + strip-json-comments "^3.1.0" + table "^6.0.4" text-table "^0.2.0" v8-compile-cache "^2.0.3" @@ -5103,14 +5152,14 @@ espree@^5.0.1: acorn-jsx "^5.0.0" eslint-visitor-keys "^1.0.0" -espree@^6.1.2: - version "6.2.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" - integrity sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw== +espree@^7.3.0, espree@^7.3.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" + integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== dependencies: - acorn "^7.1.1" - acorn-jsx "^5.2.0" - eslint-visitor-keys "^1.1.0" + acorn "^7.4.0" + acorn-jsx "^5.3.1" + eslint-visitor-keys "^1.3.0" esprima@3.x.x: version "3.1.3" @@ -5129,6 +5178,13 @@ esquery@^1.0.1: dependencies: estraverse "^5.1.0" +esquery@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== + dependencies: + estraverse "^5.1.0" + esrecurse@^4.1.0: version "4.2.1" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" @@ -5136,6 +5192,13 @@ esrecurse@^4.1.0: dependencies: estraverse "^4.1.0" +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" @@ -5146,6 +5209,11 @@ estraverse@^5.1.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.1.0.tgz#374309d39fd935ae500e7b92e8a6b4c720e59642" integrity sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw== +estraverse@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -5542,6 +5610,18 @@ ethereumjs-util@^6.0.0, ethereumjs-util@^6.1.0: rlp "^2.2.3" secp256k1 "^3.0.1" +ethereumjs-util@^7.0.10: + version "7.0.10" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.0.10.tgz#5fb7b69fa1fda0acc59634cf39d6b0291180fc1f" + integrity sha512-c/xThw6A+EAnej5Xk5kOzFzyoSnw0WX0tSlZ6pAsfGVvQj3TItaDg9b1+Fz1RJXA+y2YksKwQnuzgt1eY6LKzw== + dependencies: + "@types/bn.js" "^5.1.0" + bn.js "^5.1.2" + create-hash "^1.1.2" + ethereum-cryptography "^0.1.3" + ethjs-util "0.1.6" + rlp "^2.2.4" + ethereumjs-util@^7.0.2: version "7.0.8" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.0.8.tgz#5258762b7b17e3d828e41834948363ff0a703ffd" @@ -5932,11 +6012,6 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -expand-template@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" - integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== - expect@^25.5.0: version "25.5.0" resolved "https://registry.yarnpkg.com/expect/-/expect-25.5.0.tgz#f07f848712a2813bb59167da3fb828ca21f58bba" @@ -6147,7 +6222,7 @@ figures@^2.0.0: dependencies: escape-string-regexp "^1.0.5" -figures@^3.0.0, figures@^3.2.0: +figures@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== @@ -6161,6 +6236,13 @@ file-entry-cache@^5.0.1: dependencies: flat-cache "^2.0.1" +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + file-uri-to-path@1, file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" @@ -6249,11 +6331,29 @@ flat-cache@^2.0.1: rimraf "2.6.3" write "1.0.3" +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + dependencies: + flatted "^3.1.0" + rimraf "^3.0.2" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + flatted@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== +flatted@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469" + integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA== + follow-redirects@^1.10.0: version "1.13.1" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7" @@ -6309,11 +6409,6 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= -fs-constants@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" - integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== - fs-extra@^0.22.1: version "0.22.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.22.1.tgz#5fd6f8049dc976ca19eb2355d658173cabcce056" @@ -6359,13 +6454,6 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-minipass@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" - integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== - dependencies: - minipass "^3.0.0" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -6433,20 +6521,6 @@ fwd-stream@^1.0.4: dependencies: readable-stream "~1.0.26-4" -gauge@~2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= - dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" - gensync@^1.0.0-beta.1: version "1.0.0-beta.1" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" @@ -6457,7 +6531,7 @@ get-caller-file@^1.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== -get-caller-file@^2.0.1: +get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -6522,11 +6596,6 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -github-from-package@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" - integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= - glob-parent@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" @@ -6589,6 +6658,13 @@ globals@^12.1.0: dependencies: type-fest "^0.8.1" +globals@^13.6.0: + version "13.8.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.8.0.tgz#3e20f504810ce87a8d72e55aecf8435b50f4c1b3" + integrity sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q== + dependencies: + type-fest "^0.20.2" + graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.4: version "4.2.4" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" @@ -6639,11 +6715,6 @@ has-symbols@^1.0.0, has-symbols@^1.0.1: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== -has-unicode@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= - has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" @@ -6750,9 +6821,9 @@ hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react- react-is "^16.7.0" hosted-git-info@^2.1.4: - version "2.8.8" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" - integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== html-element-map@^1.0.0: version "1.2.0" @@ -6928,6 +6999,11 @@ immediate@^3.2.3: resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.2.3.tgz#d140fa8f614659bd6541233097ddaac25cdd991c" integrity sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw= +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps= + immer@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/immer/-/immer-8.0.1.tgz#9c73db683e2b3975c424fb0572af5889877ae656" @@ -7003,7 +7079,7 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@^1.3.4, ini@~1.3.0: +ini@^1.3.4: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== @@ -7047,25 +7123,6 @@ inquirer@^6.2.0, inquirer@^6.2.2: strip-ansi "^5.1.0" through "^2.3.6" -inquirer@^7.0.0: - version "7.3.3" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" - integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== - dependencies: - ansi-escapes "^4.2.1" - chalk "^4.1.0" - cli-cursor "^3.1.0" - cli-width "^3.0.0" - external-editor "^3.0.3" - figures "^3.0.0" - lodash "^4.17.19" - mute-stream "0.0.8" - run-async "^2.4.0" - rxjs "^6.6.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - through "^2.3.6" - internal-slot@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.2.tgz#9c2e9fb3cd8e5e4256c6f45fe310067fcfa378a3" @@ -7308,6 +7365,11 @@ is-object@~0.1.2: resolved "https://registry.yarnpkg.com/is-object/-/is-object-0.1.2.tgz#00efbc08816c33cfc4ac8251d132e10dc65098d7" integrity sha1-AO+8CIFsM8/ErIJR0TLhDcZQmNc= +is-plain-obj@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -8146,6 +8208,11 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" @@ -8253,14 +8320,15 @@ keccakjs@^0.2.0: browserify-sha3 "^0.0.4" sha3 "^1.2.2" -keyvaluestorage@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/keyvaluestorage/-/keyvaluestorage-0.6.0.tgz#8ec60592741fca865ff6ff797c0d696c7315f8f5" - integrity sha512-27+JPFNTkGPTgckVEk7PAXVlFcODbyXVZQjR1Q7RBWC8rDXjBbBbP3HC9itpmHY9naxf8w7hanM3hUecQmAsQA== - dependencies: - better-sqlite3 "^7.1.2" - localStorage "^1.0.4" - safe-json-utils "^1.1.1" +keymirror@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/keymirror/-/keymirror-0.1.1.tgz#918889ea13f8d0a42e7c557250eee713adc95c35" + integrity sha1-kYiJ6hP40KQufFVyUO7nE63JXDU= + +keyvaluestorage-interface@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/keyvaluestorage-interface/-/keyvaluestorage-interface-1.0.0.tgz#13ebdf71f5284ad54be94bd1ad9ed79adad515ff" + integrity sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g== kind-of@^1.1.0: version "1.1.0" @@ -8460,6 +8528,21 @@ levn@^0.3.0, levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +lie@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e" + integrity sha1-mkNrLMd0bKWd56QfpGmz77dr2H4= + dependencies: + immediate "~3.0.5" + lil-uuid@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/lil-uuid/-/lil-uuid-0.1.1.tgz#f9edcf23f00e42bf43f0f843d98d8b53f3341f16" @@ -8516,10 +8599,12 @@ load-json-file@^2.0.0: pify "^2.0.0" strip-bom "^3.0.0" -localStorage@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/localStorage/-/localStorage-1.0.4.tgz#57dfa28084385f81431accb8ae24b196398223f7" - integrity sha512-r35zrihcDiX+dqWlJSeIwS9nrF95OQTgqMFm3FB2D/+XgdmZtcutZOb7t0xXkhOEM8a9kpuu7cc28g1g36I5DQ== +localforage@^1.8.1: + version "1.9.0" + resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.9.0.tgz#f3e4d32a8300b362b4634cc4e066d9d00d2f09d1" + integrity sha512-rR1oyNrKulpe+VM9cYmcFn6tsHuokyVHFaCM3+osEmxaHTbEk8oQu6eGDfS6DQLWi/N67XRmB8ECG37OES368g== + dependencies: + lie "3.1.1" locate-path@^2.0.0: version "2.0.0" @@ -8544,11 +8629,21 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= + lodash.escape@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98" integrity sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg= +lodash.flatten@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" + integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= + lodash.flattendeep@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" @@ -8579,15 +8674,15 @@ lodash.toarray@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" integrity sha1-JMS/zWsvuji/0FlNsRedjptlZWE= -lodash@4.x.x, lodash@^4.0.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0: - version "4.17.19" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" - integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= -lodash@^4.17.19: - version "4.17.20" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" - integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== +lodash@^4.0.0, lodash@^4.15.0, lodash@^4.16.4, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== log-symbols@^2.2.0: version "2.2.0" @@ -9312,11 +9407,6 @@ mimic-fn@^2.0.0, mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -mimic-response@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43" - integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA== - min-document@^2.19.0: version "2.19.0" resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" @@ -9341,26 +9431,11 @@ minimalistic-crypto-utils@^1.0.1: dependencies: brace-expansion "^1.1.7" -minimist@^1.1.1, minimist@^1.1.2, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5: +minimist@^1.1.1, minimist@^1.1.2, minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== -minipass@^3.0.0: - version "3.1.3" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" - integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== - dependencies: - yallist "^4.0.0" - -minizlib@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" - integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== - dependencies: - minipass "^3.0.0" - yallist "^4.0.0" - mixin-deep@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" @@ -9369,11 +9444,6 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" - integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== - mkdirp@0.x.x, mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@~0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" @@ -9381,11 +9451,6 @@ mkdirp@0.x.x, mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@~0.5.1: dependencies: minimist "^1.2.5" -mkdirp@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - moment@^2.10.6: version "2.26.0" resolved "https://registry.yarnpkg.com/moment/-/moment-2.26.0.tgz#5e1f82c6bafca6e83e808b30c8705eed0dcbd39a" @@ -9464,7 +9529,7 @@ mute-stream@0.0.7: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= -mute-stream@0.0.8, mute-stream@~0.0.4: +mute-stream@~0.0.4: version "0.0.8" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== @@ -9510,11 +9575,6 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" -napi-build-utils@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" - integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== - natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -9561,13 +9621,6 @@ nocache@^2.1.0: resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.1.0.tgz#120c9ffec43b5729b1d5de88cd71aa75a0ba491f" integrity sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q== -node-abi@^2.7.0: - version "2.19.3" - resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.19.3.tgz#252f5dcab12dad1b5503b2d27eddd4733930282d" - integrity sha512-9xZrlyfvKhWme2EXFKQhZRp1yNWT/uI1luYPr3sFl+H4keYY4xR+1jO7mvTTijIsHf1M+QDe9uWuKeEpLInIlg== - dependencies: - semver "^5.4.1" - node-addon-api@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" @@ -9639,11 +9692,6 @@ node-version@^1.0.0: resolved "https://registry.yarnpkg.com/node-version/-/node-version-1.2.0.tgz#34fde3ffa8e1149bd323983479dda620e1b5060d" integrity sha512-ma6oU4Sk0qOoKEAymVoTvk8EdXEobdS7m/mAGhDJ8Rouugho48crHBORAmy5BoOcv8wraPM6xumapQp5hl4iIQ== -noop-logger@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" - integrity sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI= - normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -9680,16 +9728,6 @@ npm-run-path@^4.0.0: dependencies: path-key "^3.0.0" -npmlog@^4.0.1: - version "4.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" - integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== - dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" - nth-check@^1.0.2, nth-check@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" @@ -9915,7 +9953,7 @@ opn@^5.4.0: dependencies: is-wsl "^1.1.0" -optionator@^0.8.1, optionator@^0.8.2, optionator@^0.8.3: +optionator@^0.8.1, optionator@^0.8.2: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== @@ -9927,6 +9965,18 @@ optionator@^0.8.1, optionator@^0.8.2, optionator@^0.8.3: type-check "~0.3.2" word-wrap "~1.2.3" +optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.3" + options@>=0.0.5: version "0.0.6" resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" @@ -10374,32 +10424,16 @@ posix-character-classes@^0.1.0: resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= -prebuild-install@^5.3.3: - version "5.3.6" - resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.3.6.tgz#7c225568d864c71d89d07f8796042733a3f54291" - integrity sha512-s8Aai8++QQGi4sSbs/M1Qku62PFK49Jm1CbgXklGz4nmHveDq0wzJkg7Na5QbnO1uNH8K7iqx2EQ/mV0MZEmOg== - dependencies: - detect-libc "^1.0.3" - expand-template "^2.0.3" - github-from-package "0.0.0" - minimist "^1.2.3" - mkdirp-classic "^0.5.3" - napi-build-utils "^1.0.1" - node-abi "^2.7.0" - noop-logger "^0.1.1" - npmlog "^4.0.1" - pump "^3.0.0" - rc "^1.2.7" - simple-get "^3.0.3" - tar-fs "^2.0.0" - tunnel-agent "^0.6.0" - which-pm-runs "^1.0.0" - precond@0.2: version "0.2.3" resolved "https://registry.yarnpkg.com/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac" integrity sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw= +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -10685,10 +10719,10 @@ querystring@0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= -querystringify@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" - integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== r2@^2.0.1: version "2.0.1" @@ -10748,16 +10782,6 @@ raw-body@^2.2.0: iconv-lite "0.4.24" unpipe "1.0.0" -rc@^1.2.7: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - react-clone-referenced-element@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/react-clone-referenced-element/-/react-clone-referenced-element-1.1.0.tgz#9cdda7f2aeb54fea791f3ab8c6ab96c7a77d0158" @@ -10888,10 +10912,10 @@ react-native-crypto@2.1.2: pbkdf2 "3.0.8" public-encrypt "^4.0.0" -react-native-device-info@3.1.4: - version "3.1.4" - resolved "https://registry.yarnpkg.com/react-native-device-info/-/react-native-device-info-3.1.4.tgz#58a1d465ce9e2c28809132b1c68dd7ec7ce5f00f" - integrity sha512-r+7APHkPO9EA/9w3e9K7yrFigaU+0/g8i3psrNUwCGFxoDewkCHOPj/1YZnAPUwPxgyG3svjZXeCtymI3xcSlA== +react-native-device-info@^8.1.3: + version "8.1.3" + resolved "https://registry.yarnpkg.com/react-native-device-info/-/react-native-device-info-8.1.3.tgz#022fc01372632bf80c0a6d201aab53344efc715f" + integrity sha512-73e3wiGFL8DvIXEd8x4Aq+mO8mZvHARwfYorIEu+X0trLHY92bP5Ict8DJHDjCQ0+HtAvpA4Wda2VoUVN1zh6Q== react-native-elevated-view@0.0.6: version "0.0.6" @@ -11131,6 +11155,13 @@ react-native-tcp@aprock/react-native-tcp#11/head: process "^0.11.9" util "^0.12.1" +react-native-url-polyfill@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/react-native-url-polyfill/-/react-native-url-polyfill-1.3.0.tgz#c1763de0f2a8c22cc3e959b654c8790622b6ef6a" + integrity sha512-w9JfSkvpqqlix9UjDvJjm1EjSt652zVQ6iwCIj1cVVkwXf4jQhQgTNXY6EVTwuAmUjg6BC6k9RHCBynoLFo3IQ== + dependencies: + whatwg-url-without-unicode "8.0.0-3" + react-native-v8@^0.62.2-patch.1: version "0.62.2-patch.1" resolved "https://registry.yarnpkg.com/react-native-v8/-/react-native-v8-0.62.2-patch.1.tgz#016a932ed5e60f6bca6803fbdf6c746fe1b55bf5" @@ -11147,6 +11178,22 @@ react-native-vector-icons@6.4.2: prop-types "^15.6.2" yargs "^13.2.2" +react-native-video-controls@^2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/react-native-video-controls/-/react-native-video-controls-2.7.1.tgz#52bd22ea83a964c9bf1e741e7ed6380ad0e28f1e" + integrity sha512-yoquLUx2x7EJOnDNGw0H4bb0gO4Io+ziFSDoDGGJqaT15IaXIiD+GCvCDbW9gLcUxsyLDwCr65sx8Yt2ZMvbCg== + dependencies: + lodash "^4.16.4" + +react-native-video@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/react-native-video/-/react-native-video-5.1.1.tgz#89a7989efeb8d404611c06154d1da227a745d7d8" + integrity sha512-zee8gRUrjPWRoZSEBiMebClqu1iAuCQNLjzqpmXFrRWEoJj7azM3BPqLQWJgsnfLiYUYGySeApC/G60THM5+tw== + dependencies: + keymirror "^0.1.1" + prop-types "^15.7.2" + shaka-player "^2.5.9" + react-native-view-shot@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/react-native-view-shot/-/react-native-view-shot-3.1.2.tgz#8c8e84c67a4bc8b603e697dbbd59dbc9b4f84825" @@ -11346,7 +11393,7 @@ readable-stream@1.1.x, readable-stream@^1.0.26-4, readable-stream@^1.0.27-1, rea isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@2, readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@^2.2.9, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@~2.3.6: +readable-stream@2, readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.2.2, readable-stream@^2.2.9, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -11359,7 +11406,7 @@ readable-stream@2, readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stre string_decoder "~1.1.1" util-deprecate "~1.0.1" -"readable-stream@2 || 3", readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: +"readable-stream@2 || 3", readable-stream@^3.1.1, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -11471,7 +11518,7 @@ regexpp@^2.0.1: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== -regexpp@^3.0.0: +regexpp@^3.0.0, regexpp@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== @@ -11579,6 +11626,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + require-main-filename@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" @@ -11683,7 +11735,7 @@ rimraf@2.x.x, rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.3: dependencies: glob "^7.1.3" -rimraf@^3.0.0: +rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== @@ -11760,7 +11812,7 @@ rsvp@^4.8.4: resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== -run-async@^2.2.0, run-async@^2.4.0: +run-async@^2.2.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== @@ -11806,13 +11858,6 @@ rxjs@^5.4.3: dependencies: symbol-observable "1.0.1" -rxjs@^6.6.0: - version "6.6.3" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552" - integrity sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ== - dependencies: - tslib "^1.9.0" - rxjs@^6.6.6: version "6.6.6" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.6.tgz#14d8417aa5a07c5e633995b525e1e3c0dec03b70" @@ -11847,11 +11892,6 @@ safe-json-utils@1.0.0: resolved "https://registry.yarnpkg.com/safe-json-utils/-/safe-json-utils-1.0.0.tgz#8b1d68b13cff2ac6a5b68e6c9651cf7f8bb56d9b" integrity sha512-n0hJm6BgX8wk3G+AS8MOQnfcA8dfE6ZMUfwkHUNx69YxPlU3HDaZTHXWto35Z+C4mOjK1odlT95WutkGC+0Idw== -safe-json-utils@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/safe-json-utils/-/safe-json-utils-1.1.1.tgz#0e883874467d95ab914c3f511096b89bfb3e63b1" - integrity sha512-SAJWGKDs50tAbiDXLf89PDwt9XYkWyANFWVzn4dTXl5QyI8t2o/bW5/OJl3lvc2WVU4MEpTo9Yz5NVFNsp+OJQ== - safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" @@ -11985,11 +12025,18 @@ semver@^4.3.2, semver@~2.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" integrity sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto= -semver@^6.0.0, semver@^6.1.2, semver@^6.3.0: +semver@^6.0.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^7.2.1: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" + semver@^7.3.2: version "7.3.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" @@ -12036,7 +12083,7 @@ serve-static@^1.13.1: parseurl "~1.3.3" send "0.17.1" -set-blocking@^2.0.0, set-blocking@~2.0.0: +set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= @@ -12081,6 +12128,13 @@ sha3@^1.2.2: dependencies: nan "2.13.2" +shaka-player@^2.5.9: + version "2.5.22" + resolved "https://registry.yarnpkg.com/shaka-player/-/shaka-player-2.5.22.tgz#64b4547b742b2a3a022be9fbe0686d944f9d0300" + integrity sha512-PAoeNLUQ/hT/9dY7QvNFgIiDtXSqbYVFuXXtLHh7ytVVqTvI/p4HLwfYShiR+sE/sbsDOr9D5l9D/ztLPhxgtw== + dependencies: + eme-encryption-scheme-polyfill "^2.0.1" + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -12115,18 +12169,11 @@ shell-quote@1.6.1: array-reduce "~0.0.0" jsonify "~0.0.0" -shell-quote@^1.6.1: +shell-quote@^1.6.1, shell-quote@^1.7.2: version "1.7.2" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== -shell-utils@^1.0.9: - version "1.0.10" - resolved "https://registry.yarnpkg.com/shell-utils/-/shell-utils-1.0.10.tgz#7fe7b8084f5d6d21323d941267013bc38aed063e" - integrity sha512-p1xuqhj3jgcXiV8wGoF1eL/NOvapN9tyGDoObqKwvZTUZn7fIzK75swLTEHfGa7sObeN9vxFplHw/zgYUYRTsg== - dependencies: - lodash "4.x.x" - shellwords@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" @@ -12145,20 +12192,6 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== -simple-concat@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" - integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== - -simple-get@^3.0.3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.1.0.tgz#b45be062435e50d159540b576202ceec40b9c6b3" - integrity sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA== - dependencies: - decompress-response "^4.2.0" - once "^1.3.1" - simple-concat "^1.0.0" - simple-plist@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/simple-plist/-/simple-plist-1.1.0.tgz#8354ab63eb3922a054c78ce96c209c532e907a23" @@ -12483,7 +12516,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: +string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -12647,12 +12680,12 @@ strip-hex-prefix@1.0.0: dependencies: is-hex-prefixed "1.0.0" -strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: +strip-json-comments@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -strip-json-comments@^3.0.1: +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -12745,44 +12778,23 @@ table@^5.2.3: slice-ansi "^2.1.0" string-width "^3.0.0" +table@^6.0.4: + version "6.4.0" + resolved "https://registry.yarnpkg.com/table/-/table-6.4.0.tgz#9501324358c313162cf52b2843a8b221e75fbefc" + integrity sha512-/Vfr23BDjJT2kfsCmYtnJqEPdD/8Dh/MDIQxfcbe+09lZUel6gluquwdMTrLERBw623Nv34DLGZ11krWn5AAqw== + dependencies: + ajv "^8.0.1" + lodash.clonedeep "^4.5.0" + lodash.flatten "^4.4.0" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.0" + tail@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/tail/-/tail-2.0.3.tgz#37567adc4624a70b35f1d146c3376fa3d6ef7c04" integrity sha512-s9NOGkLqqiDEtBttQZI7acLS8ycYK5sTlDwNjGnpXG9c8AWj0cfAtwEIzo/hVRMMiC5EYz+bXaJWC1u1u0GPpQ== -tar-fs@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" - integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== - dependencies: - chownr "^1.1.1" - mkdirp-classic "^0.5.2" - pump "^3.0.0" - tar-stream "^2.1.4" - -tar-stream@^2.1.4: - version "2.2.0" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" - integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== - dependencies: - bl "^4.0.3" - end-of-stream "^1.4.1" - fs-constants "^1.0.0" - inherits "^2.0.3" - readable-stream "^3.1.1" - -tar@^6.0.5: - version "6.1.0" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.0.tgz#d1724e9bcc04b977b18d5c573b333a2207229a83" - integrity sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA== - dependencies: - chownr "^2.0.0" - fs-minipass "^2.0.0" - minipass "^3.0.0" - minizlib "^2.1.1" - mkdirp "^1.0.3" - yallist "^4.0.0" - telnet-client@1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/telnet-client/-/telnet-client-1.2.8.tgz#946c0dadc8daa3f19bb40a3e898cb870403a4ca4" @@ -12973,11 +12985,16 @@ tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== -tslib@^2.0.0, tslib@^2.1.0: +tslib@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== +tslib@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c" + integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w== + tsutils@^3.17.1: version "3.17.1" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" @@ -13007,6 +13024,13 @@ tweetnacl@^1.0.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" @@ -13024,6 +13048,11 @@ type-fest@^0.11.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + type-fest@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" @@ -13051,10 +13080,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -ua-parser-js@^0.7.18: - version "0.7.21" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.21.tgz#853cf9ce93f642f67174273cc34565ae6f308777" - integrity sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ== +ua-parser-js@^0.7.18, ua-parser-js@^0.7.24: + version "0.7.28" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.28.tgz#8ba04e653f35ce210239c64661685bf9121dec31" + integrity sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g== uglify-es@^3.1.9: version "3.3.9" @@ -13079,6 +13108,11 @@ unicode-canonical-property-names-ecmascript@^1.0.4: resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== +unicode-confusables@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/unicode-confusables/-/unicode-confusables-0.1.1.tgz#17f14e8dc53ff81c12e92fd86e836ebdf14ea0c2" + integrity sha512-XTPBWmT88BDpXz9NycWk4KxDn+/AJmJYYaYBwuIH9119sopwk2E9GxU9azc+JNbhEsfiPul78DGocEihCp6MFQ== + unicode-match-property-ecmascript@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" @@ -13142,12 +13176,12 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= -url-parse@1.4.4: - version "1.4.4" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.4.tgz#cac1556e95faa0303691fec5cf9d5a1bc34648f8" - integrity sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg== +url-parse@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.0.tgz#90aba6c902aeb2d80eac17b91131c27665d5d828" + integrity sha512-9iT6N4s93SMfzunOyDPe4vo4nLcSu1yq0IQK1gURmjm8tQNlM6loiuCRrKG1hHGXfB2EWd6H4cGi7tGdaygMFw== dependencies: - querystringify "^2.0.0" + querystringify "^2.1.1" requires-port "^1.0.0" url@0.11.0: @@ -13396,6 +13430,11 @@ webidl-conversions@^4.0.2: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== +webidl-conversions@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" + integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== + whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" @@ -13423,6 +13462,15 @@ whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== +whatwg-url-without-unicode@8.0.0-3: + version "8.0.0-3" + resolved "https://registry.yarnpkg.com/whatwg-url-without-unicode/-/whatwg-url-without-unicode-8.0.0-3.tgz#ab6df4bf6caaa6c85a59f6e82c026151d4bb376b" + integrity sha512-HoKuzZrUlgpz35YO27XgD28uh/WJH4B0+3ttFqRo//lmq+9T/mIOJ6kqmINI9HpUpz1imRC/nR/lxKpJiv0uig== + dependencies: + buffer "^5.4.3" + punycode "^2.1.1" + webidl-conversions "^5.0.0" + whatwg-url@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" @@ -13458,11 +13506,6 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which-pm-runs@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" - integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= - which-typed-array@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.2.tgz#e5f98e56bda93e3dac196b01d47c1156679c00b2" @@ -13489,13 +13532,6 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" -wide-align@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== - dependencies: - string-width "^1.0.2 || 2" - window-getters@1.0.0, window-getters@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/window-getters/-/window-getters-1.0.0.tgz#b5b264538c4c79cead027f9997850222bf6d0852" @@ -13526,7 +13562,7 @@ winston@0.8.x: pkginfo "0.3.x" stack-trace "0.0.x" -word-wrap@~1.2.3: +word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== @@ -13753,7 +13789,7 @@ xtend@~3.0.0: resolved "https://registry.yarnpkg.com/xtend/-/xtend-3.0.0.tgz#5cce7407baf642cba7becda568111c493f59665a" integrity sha1-XM50B7r2Qsunvs2laBEcST9ZZlo= -y18n@^3.2.0, "y18n@^3.2.1 || ^4.0.0", y18n@^3.2.2, y18n@^4.0.0: +y18n@^3.2.0, "y18n@^3.2.1 || ^4.0.0", y18n@^3.2.2, y18n@^4.0.0, y18n@^5.0.5: version "3.2.2" resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.2.tgz#85c901bd6470ce71fc4bb723ad209b70f7f28696" integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ== @@ -13786,7 +13822,7 @@ yargs-parser@^11.1.1: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^13.0.0, yargs-parser@^13.1.2: +yargs-parser@^13.1.2: version "13.1.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== @@ -13810,6 +13846,21 @@ yargs-parser@^18.1.1, yargs-parser@^18.1.2: camelcase "^5.0.0" decamelize "^1.2.0" +yargs-parser@^20.2.2: + version "20.2.7" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a" + integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw== + +yargs-unparser@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== + dependencies: + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" + yargs@^12.0.2: version "12.0.5" resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13" @@ -13828,7 +13879,7 @@ yargs@^12.0.2: y18n "^3.2.1 || ^4.0.0" yargs-parser "^11.1.1" -yargs@^13.0.0, yargs@^13.2.2, yargs@^13.2.4: +yargs@^13.2.2, yargs@^13.2.4: version "13.3.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== @@ -13895,6 +13946,19 @@ yargs@^15.3.1: y18n "^4.0.0" yargs-parser "^18.1.1" +yargs@^16.0.3: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + yargs@^3.30.0: version "3.32.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.32.0.tgz#03088e9ebf9e756b69751611d2a5ef591482c995" @@ -13911,4 +13975,4 @@ yargs@^3.30.0: zxcvbn@4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/zxcvbn/-/zxcvbn-4.4.2.tgz#28ec17cf09743edcab056ddd8b1b06262cc73c30" - integrity sha1-KOwXzwl0PtyrBW3dixsGJizHPDA= \ No newline at end of file + integrity sha1-KOwXzwl0PtyrBW3dixsGJizHPDA=