From 3ec3baa3a1bb05ac63c859c5efaf5a94938093d8 Mon Sep 17 00:00:00 2001
From: Etienne Dusseault <etienne.dusseault@gmail.com>
Date: Thu, 23 Sep 2021 22:39:22 -0300
Subject: [PATCH 01/56] send-content (#12167)

---
 .storybook/test-data.js | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/.storybook/test-data.js b/.storybook/test-data.js
index 766196d2f..0eb4766c4 100644
--- a/.storybook/test-data.js
+++ b/.storybook/test-data.js
@@ -1165,6 +1165,10 @@ const state = {
       balance: '0x0',
       details: null,
     },
+    amount: '3782dace9d900000',
+    gas: {
+      price: null,
+    },
   },
   confirmTransaction: {
     txData: {

From bf4e5654974cefb2e1093491b8dee3a6cfb87f50 Mon Sep 17 00:00:00 2001
From: Etienne Dusseault <etienne.dusseault@gmail.com>
Date: Thu, 23 Sep 2021 22:40:03 -0300
Subject: [PATCH 02/56] edit-gas-display (#12165)

---
 .storybook/test-data.js                       | 38 +++++-----
 .../edit-gas-popover.stories.js               | 70 +++++++++++++++++--
 2 files changed, 82 insertions(+), 26 deletions(-)

diff --git a/.storybook/test-data.js b/.storybook/test-data.js
index 0eb4766c4..376182e34 100644
--- a/.storybook/test-data.js
+++ b/.storybook/test-data.js
@@ -1,5 +1,3 @@
-import { TRANSACTION_STATUSES } from '../shared/constants/transaction';
-
 const state = {
   invalidCustomNetwork: {
     state: 'CLOSED',
@@ -21,6 +19,24 @@ const state = {
         1559: true,
       },
     },
+    gasFeeEstimates: '0x5208',
+    swapsState: {
+      quotes: {},
+      fetchParams: null,
+      tokens: null,
+      tradeTxId: null,
+      approveTxId: null,
+      quotesLastFetched: null,
+      customMaxGas: '',
+      customGasPrice: null,
+      selectedAggId: null,
+      customApproveTxData: '',
+      errorKey: '',
+      topAggId: null,
+      routeState: '',
+      swapsFeatureIsLive: false,
+      swapsQuoteRefreshTime: 60000,
+    },
     isInitialized: true,
     isUnlocked: true,
     isAccountMenuOpen: false,
@@ -103,24 +119,6 @@ const state = {
         },
       },
     },
-    recipient: {
-      address: '0x39a4e4Af7cCB654dB9500F258c64781c8FbD39F0',
-      nickname: 'John Doe',
-      error: '',
-      warning: '',
-    },
-    addresses: [
-      {
-        address: '0x39a4e4Af7cCB654dB9500F258c64781c8FbD39F0',
-        name: 'DAI',
-        isEns: false,
-      },
-      {
-        address: '1x39a4e4Af7cCB654dB9500F258c64781c8FbD39F0',
-        name: 'ETH',
-        isEns: true,
-      },
-    ],
     contractExchangeRates: {
       '0xaD6D458402F60fD3Bd25163575031ACDce07538D': 0,
     },
diff --git a/ui/components/app/edit-gas-popover/edit-gas-popover.stories.js b/ui/components/app/edit-gas-popover/edit-gas-popover.stories.js
index 93d7cc830..b18d834be 100644
--- a/ui/components/app/edit-gas-popover/edit-gas-popover.stories.js
+++ b/ui/components/app/edit-gas-popover/edit-gas-popover.stories.js
@@ -1,31 +1,89 @@
 import React from 'react';
+import { Provider } from 'react-redux';
+import { action } from '@storybook/addon-actions';
+import { boolean } from '@storybook/addon-knobs';
+import { decGWEIToHexWEI } from '../../../helpers/utils/conversions.util';
+import configureStore from '../../../store/store';
+import testData from '../../../../.storybook/test-data';
+import { EDIT_GAS_MODES } from '../../../../shared/constants/gas';
 import EditGasPopover from '.';
 
+const store = configureStore(testData);
+
 export default {
   title: 'Edit Gas Display Popover',
+  decorators: [(story) => <Provider store={store}>{story()}</Provider>],
   id: __filename,
 };
 
-export const basic = () => {
+export const Basic = () => {
   return (
     <div style={{ width: '600px' }}>
-      <EditGasPopover />
+      <EditGasPopover
+        transaction={{
+          userFeeLevel: 'medium',
+          txParams: {
+            maxFeePerGas: decGWEIToHexWEI('10000'),
+            maxPriorityFeePerGas: '0x5600',
+            gas: `0x5600`,
+            gasPrice: '0x5600',
+          },
+        }}
+        defaultEstimateToUse="high"
+        mode={EDIT_GAS_MODES.SWAPS}
+        confirmButtonText="Submit"
+        onClose={() => action(`Close Edit Gas Popover`)()}
+        minimumGasLimit="5700"
+      />
     </div>
   );
 };
 
-export const basicWithDifferentButtonText = () => {
+export const BasicWithDifferentButtonText = () => {
   return (
     <div style={{ width: '600px' }}>
-      <EditGasPopover confirmButtonText="Custom Value" />
+      <EditGasPopover
+        confirmButtonText="Custom Value"
+        transaction={{
+          userFeeLevel: 'medium',
+          txParams: {
+            maxFeePerGas: decGWEIToHexWEI('10000'),
+            maxPriorityFeePerGas: '0x5600',
+            gas: `0x5600`,
+            gasPrice: '0x5600',
+          },
+        }}
+        defaultEstimateToUse="high"
+        mode={EDIT_GAS_MODES.SWAPS}
+        onClose={() => action(`Close Edit Gas Popover`)()}
+        minimumGasLimit="5700"
+      />
     </div>
   );
 };
 
-export const educationalContentFlow = () => {
+export const EducationalContentFlow = () => {
   return (
     <div style={{ width: '600px' }}>
-      <EditGasPopover editGasDisplayProps={{ showEducationButton: true }} />
+      <EditGasPopover
+        editGasDisplayProps={{
+          showEducationButton: boolean('Show Education Button', true),
+        }}
+        transaction={{
+          userFeeLevel: 'medium',
+          txParams: {
+            maxFeePerGas: decGWEIToHexWEI('10000'),
+            maxPriorityFeePerGas: '0x5600',
+            gas: `0x5600`,
+            gasPrice: '0x5600',
+          },
+        }}
+        defaultEstimateToUse="high"
+        mode={EDIT_GAS_MODES.SWAPS}
+        confirmButtonText="Submit"
+        onClose={() => action(`Close Edit Gas Popover`)()}
+        minimumGasLimit="5700"
+      />
     </div>
   );
 };

From 2a1d0cfd2c2504ab05a0ce991055d724a35371b8 Mon Sep 17 00:00:00 2001
From: Mark Stacey <markjstacey@gmail.com>
Date: Fri, 24 Sep 2021 11:42:28 -0230
Subject: [PATCH 03/56] Update `caniuse-lite` (#12203)

The dependency `caniuse-lite` has been updated using a Yarn resolution,
because it was pinned to a specific version by some dependencies. All
versions requested in our dependency tree are 1.x so this did not
introduce any breaking changes.

This resolves a frequent console warning that shows up during builds,
and when running tests and the linter.
---
 package.json |  1 +
 yarn.lock    | 15 +++++++++++----
 2 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/package.json b/package.json
index c5cf5a091..ce8c9bc02 100644
--- a/package.json
+++ b/package.json
@@ -69,6 +69,7 @@
   },
   "resolutions": {
     "**/regenerator-runtime": "^0.13.7",
+    "**/caniuse-lite": "1.0.30001260",
     "**/configstore/dot-prop": "^5.1.1",
     "**/ethers/elliptic": "^6.5.4",
     "**/knex/minimist": "^1.2.5",
diff --git a/yarn.lock b/yarn.lock
index c763e1955..dcdf5a466 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7762,10 +7762,12 @@ camelcase@^6.2.0:
   resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809"
   integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==
 
-caniuse-lite@^1.0.30000810, caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30001035, caniuse-lite@^1.0.30001097, caniuse-lite@^1.0.30001157:
-  version "1.0.30001162"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001162.tgz#9f83aad1f42539ce9aab58bb177598f2f8e22ec6"
-  integrity sha512-E9FktFxaNnp4ky3ucIGzEXLM+Knzlpuq1oN1sFAU0KeayygabGTmOsndpo8QrL4D9pcThlf4D2pUKaDxPCUmVw==
+caniuse-lite@1.0.30001260, caniuse-lite@^1.0.30000810, caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30001035, caniuse-lite@^1.0.30001097, caniuse-lite@^1.0.30001157:
+  version "1.0.30001260"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001260.tgz#e3be3f34ddad735ca4a2736fa9e768ef34316270"
+  integrity sha512-Fhjc/k8725ItmrvW5QomzxLeojewxvqiYCKeFcfFEhut28IVLdpHU19dneOmltZQIE5HNbawj1HYD+1f2bM1Dg==
+  dependencies:
+    nanocolors "^0.1.0"
 
 capture-exit@^2.0.0:
   version "2.0.0"
@@ -19642,6 +19644,11 @@ nanoassert@^1.0.0:
   resolved "https://registry.yarnpkg.com/nanoassert/-/nanoassert-1.1.0.tgz#4f3152e09540fde28c76f44b19bbcd1d5a42478d"
   integrity sha1-TzFS4JVA/eKMdvRLGbvNHVpCR40=
 
+nanocolors@^0.1.0:
+  version "0.1.12"
+  resolved "https://registry.yarnpkg.com/nanocolors/-/nanocolors-0.1.12.tgz#8577482c58cbd7b5bb1681db4cf48f11a87fd5f6"
+  integrity sha512-2nMHqg1x5PU+unxX7PGY7AuYxl2qDx7PSrTRjizr8sxdd3l/3hBuWWaki62qmtYm2U5i4Z5E7GbjlyDFhs9/EQ==
+
 nanoid@^2.0.0, nanoid@^2.1.6:
   version "2.1.11"
   resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280"

From a86129d23fd96e2cad5fdfb9c62676081fea80f8 Mon Sep 17 00:00:00 2001
From: Etienne Dusseault <etienne.dusseault@gmail.com>
Date: Fri, 24 Sep 2021 11:47:40 -0300
Subject: [PATCH 04/56] fee-card (#12166)

---
 ui/pages/swaps/fee-card/fee-card.stories.js | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/ui/pages/swaps/fee-card/fee-card.stories.js b/ui/pages/swaps/fee-card/fee-card.stories.js
index 4597edad7..b25c4b575 100644
--- a/ui/pages/swaps/fee-card/fee-card.stories.js
+++ b/ui/pages/swaps/fee-card/fee-card.stories.js
@@ -1,6 +1,7 @@
 import React from 'react';
 import { action } from '@storybook/addon-actions';
 import { text, boolean, number, object } from '@storybook/addon-knobs';
+import { MAINNET_CHAIN_ID } from '../../../../shared/constants/network';
 import FeeCard from './fee-card';
 
 const tokenApprovalTextComponent = (
@@ -31,6 +32,8 @@ export const WithAllProps = () => {
           fee: text('secondaryFee', '100 USD'),
           maxFee: text('secondaryMaxFee', '200 USD'),
         }}
+        chainId={MAINNET_CHAIN_ID}
+        networkAndAccountSupports1559={false}
         onFeeCardMaxRowClick={action('Clicked max fee row link')}
         tokenApprovalTextComponent={tokenApprovalTextComponent}
         tokenApprovalSourceTokenSymbol="ABC"
@@ -68,6 +71,8 @@ export const WithoutThirdRow = () => {
         isBestQuote={boolean('isBestQuote', true)}
         savings={object('savings 1', { total: '8.55' })}
         metaMaskFee="0.875"
+        chainId={MAINNET_CHAIN_ID}
+        networkAndAccountSupports1559={false}
       />
     </div>
   );
@@ -86,6 +91,8 @@ export const WithOnlyRequiredProps = () => {
         metaMaskFee="0.875"
         onQuotesClick={action('Clicked quotes link')}
         numberOfQuotes={2}
+        chainId={MAINNET_CHAIN_ID}
+        networkAndAccountSupports1559={false}
       />
     </div>
   );

From 730ff250284f0489241ce881461ac71d458f183f Mon Sep 17 00:00:00 2001
From: ryanml <ryanlanese@gmail.com>
Date: Fri, 24 Sep 2021 09:27:22 -0700
Subject: [PATCH 05/56] Fixing accounts scrollbar for chrome (#12205)

---
 ui/components/app/account-menu/index.scss | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/ui/components/app/account-menu/index.scss b/ui/components/app/account-menu/index.scss
index 5b7bda389..8f1f5c8c8 100644
--- a/ui/components/app/account-menu/index.scss
+++ b/ui/components/app/account-menu/index.scss
@@ -129,10 +129,6 @@
     max-height: 256px;
     scrollbar-width: auto;
 
-    &::-webkit-scrollbar {
-      display: none;
-    }
-
     @media screen and (max-width: $break-small) {
       max-height: 228px;
     }

From 30105a19d1f7a135b9965c4d5c3aaadcf87a0ab5 Mon Sep 17 00:00:00 2001
From: ryanml <ryanlanese@gmail.com>
Date: Fri, 24 Sep 2021 09:28:54 -0700
Subject: [PATCH 06/56] Fixing alignment of settings close button (#12202)

---
 ui/pages/settings/index.scss | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/ui/pages/settings/index.scss b/ui/pages/settings/index.scss
index f0df002be..a77436eb9 100644
--- a/ui/pages/settings/index.scss
+++ b/ui/pages/settings/index.scss
@@ -22,7 +22,7 @@
 
       flex: 1 0 auto;
 
-      @media screen and (max-width: 575px) {
+      @media screen and (max-width: $break-small) {
         text-overflow: ellipsis;
         overflow: hidden;
         white-space: nowrap;
@@ -108,6 +108,10 @@
     }
   }
 
+  &__close-button {
+    margin-left: auto;
+  }
+
   &__close-button::after {
     content: '\00D7';
     font-size: 40px;

From 08dff86be070ac0b43c53561f919cb0e3167f399 Mon Sep 17 00:00:00 2001
From: "Akintayo A. Olusegun" <akintayo.segun@gmail.com>
Date: Fri, 24 Sep 2021 21:14:07 +0400
Subject: [PATCH 07/56] Add target object word to "View on Etherscan" links
 (#12100)

* Add target object word to "View on Etherscan" links
Fix for: #9476

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

* Fix Swap tests checking for View $1 at or View $1 on

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

* Linter Fixes

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

* Fix  ui/hooks/useTransactionDisplayData.test.js expected result should
be May 13, 2020

Update Jest snapshot for view on etherscan test

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

* Add description of the variables in messages.

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

* 1. localize all of the blockExplorerViewAction values
2. Apply this nit. https://github.com/MetaMask/metamask-extension/pull/12100#discussion_r708343532

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

* Lint fixes.

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

* Fix locale value not used on GUI error lint error.

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

* Reverted this commit, on circle ci, the date is May 12.

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

* Add description to link block explore actions to where it's used.

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

* Fix missing messages.

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>
---
 app/_locales/am/messages.json                 |  9 -------
 app/_locales/ar/messages.json                 |  9 -------
 app/_locales/bg/messages.json                 |  9 -------
 app/_locales/bn/messages.json                 |  9 -------
 app/_locales/ca/messages.json                 |  9 -------
 app/_locales/da/messages.json                 |  9 -------
 app/_locales/de/messages.json                 |  9 -------
 app/_locales/el/messages.json                 |  9 -------
 app/_locales/en/messages.json                 | 25 ++++++++++++++++---
 app/_locales/es/messages.json                 |  9 -------
 app/_locales/es_419/messages.json             |  9 -------
 app/_locales/et/messages.json                 |  9 -------
 app/_locales/fa/messages.json                 |  9 -------
 app/_locales/fi/messages.json                 |  9 -------
 app/_locales/fil/messages.json                |  9 -------
 app/_locales/fr/messages.json                 |  9 -------
 app/_locales/he/messages.json                 |  9 -------
 app/_locales/hi/messages.json                 |  9 -------
 app/_locales/hr/messages.json                 |  9 -------
 app/_locales/ht/messages.json                 |  3 ---
 app/_locales/hu/messages.json                 |  9 -------
 app/_locales/id/messages.json                 |  9 -------
 app/_locales/it/messages.json                 |  9 -------
 app/_locales/ja/messages.json                 |  9 -------
 app/_locales/kn/messages.json                 |  9 -------
 app/_locales/ko/messages.json                 |  9 -------
 app/_locales/lt/messages.json                 |  9 -------
 app/_locales/lv/messages.json                 |  9 -------
 app/_locales/ms/messages.json                 |  9 -------
 app/_locales/no/messages.json                 |  9 -------
 app/_locales/ph/messages.json                 |  9 -------
 app/_locales/pl/messages.json                 |  9 -------
 app/_locales/pt_BR/messages.json              |  9 -------
 app/_locales/ro/messages.json                 |  9 -------
 app/_locales/ru/messages.json                 |  9 -------
 app/_locales/sk/messages.json                 |  9 -------
 app/_locales/sl/messages.json                 |  9 -------
 app/_locales/sr/messages.json                 |  9 -------
 app/_locales/sv/messages.json                 |  9 -------
 app/_locales/sw/messages.json                 |  9 -------
 app/_locales/tl/messages.json                 |  9 -------
 app/_locales/uk/messages.json                 |  9 -------
 app/_locales/vi/messages.json                 |  9 -------
 app/_locales/zh_CN/messages.json              |  9 -------
 app/_locales/zh_TW/messages.json              |  9 -------
 .../app/menu-bar/account-options-menu.js      |  4 ++-
 .../account-details-modal.component.js        |  4 ++-
 ...transaction-list-item-details.component.js |  9 +++++--
 ui/pages/asset/components/asset-options.js    |  4 ++-
 .../swaps/awaiting-swap/awaiting-swap.test.js |  2 +-
 .../view-on-ether-scan-link.test.js.snap      |  4 +--
 .../view-on-ether-scan-link.js                |  7 ++++--
 .../view-on-ether-scan-link.test.js           |  6 +++--
 53 files changed, 50 insertions(+), 405 deletions(-)

diff --git a/app/_locales/am/messages.json b/app/_locales/am/messages.json
index 50fa63a2e..3217ceb56 100644
--- a/app/_locales/am/messages.json
+++ b/app/_locales/am/messages.json
@@ -1124,15 +1124,6 @@
   "viewContact": {
     "message": "ዕውቂያን ይመልከቱ"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "በ $1ይመልከቱ"
-  },
-  "viewOnEtherscan": {
-    "message": "በ Etherscan ላይ ይመልከቱ"
-  },
-  "viewinExplorer": {
-    "message": "በኤክስፕሎረር ተመልከት"
-  },
   "visitWebSite": {
     "message": "ድረ ገጻችንን ይጎብኙ"
   },
diff --git a/app/_locales/ar/messages.json b/app/_locales/ar/messages.json
index 5f37cb4e1..3c63ec354 100644
--- a/app/_locales/ar/messages.json
+++ b/app/_locales/ar/messages.json
@@ -1120,15 +1120,6 @@
   "viewContact": {
     "message": "عرض جهة الاتصال"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "عرض في $1"
-  },
-  "viewOnEtherscan": {
-    "message": "عرضه على Etherscan"
-  },
-  "viewinExplorer": {
-    "message": "عرض في متصفح Explorer"
-  },
   "visitWebSite": {
     "message": "قم بزيارة موقعنا على الإنترنت"
   },
diff --git a/app/_locales/bg/messages.json b/app/_locales/bg/messages.json
index 57196e3b3..ceaa2f9f6 100644
--- a/app/_locales/bg/messages.json
+++ b/app/_locales/bg/messages.json
@@ -1123,15 +1123,6 @@
   "viewContact": {
     "message": "Преглед на контакта"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Преглед на $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Преглед на Etherscan"
-  },
-  "viewinExplorer": {
-    "message": "Преглед в Explorer"
-  },
   "visitWebSite": {
     "message": "Посетете нашият уеб сайт"
   },
diff --git a/app/_locales/bn/messages.json b/app/_locales/bn/messages.json
index 19c45effd..84369b869 100644
--- a/app/_locales/bn/messages.json
+++ b/app/_locales/bn/messages.json
@@ -1127,15 +1127,6 @@
   "viewContact": {
     "message": "পরিচিতি দেখুন"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "$1 এ দেখুন"
-  },
-  "viewOnEtherscan": {
-    "message": "Etherscan দেখুন"
-  },
-  "viewinExplorer": {
-    "message": "এক্সপ্লোরারে দেখুন"
-  },
   "visitWebSite": {
     "message": "আমাদের ওয়েবসাইট দেখুন"
   },
diff --git a/app/_locales/ca/messages.json b/app/_locales/ca/messages.json
index fe3311ead..8f5c9aa15 100644
--- a/app/_locales/ca/messages.json
+++ b/app/_locales/ca/messages.json
@@ -1096,15 +1096,6 @@
   "viewContact": {
     "message": "Veure Contacte"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Mostra a $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Veure a Etherscan"
-  },
-  "viewinExplorer": {
-    "message": "Mostra a Explorer"
-  },
   "visitWebSite": {
     "message": "Visita el nostre lloc web"
   },
diff --git a/app/_locales/da/messages.json b/app/_locales/da/messages.json
index c4c147bda..64c677d92 100644
--- a/app/_locales/da/messages.json
+++ b/app/_locales/da/messages.json
@@ -1096,15 +1096,6 @@
   "viewContact": {
     "message": "Vis kontakt"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Se på $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Se på Etherscan"
-  },
-  "viewinExplorer": {
-    "message": "Vis i stifinder"
-  },
   "visitWebSite": {
     "message": "Besøg vores webside"
   },
diff --git a/app/_locales/de/messages.json b/app/_locales/de/messages.json
index d6e52d78b..2165ab0da 100644
--- a/app/_locales/de/messages.json
+++ b/app/_locales/de/messages.json
@@ -1087,15 +1087,6 @@
   "viewContact": {
     "message": "Kontakt anzeigen"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Für $1 ansehen"
-  },
-  "viewOnEtherscan": {
-    "message": "Auf Etherscan ansehen"
-  },
-  "viewinExplorer": {
-    "message": "Im Explorer anzeigen"
-  },
   "visitWebSite": {
     "message": "Gehe zu unserer Webseite"
   },
diff --git a/app/_locales/el/messages.json b/app/_locales/el/messages.json
index e7239b9ae..91d8915e4 100644
--- a/app/_locales/el/messages.json
+++ b/app/_locales/el/messages.json
@@ -1121,15 +1121,6 @@
   "viewContact": {
     "message": "Εμφάνιση Επαφής"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Προβολή σε $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Προβολή στο Etherscan"
-  },
-  "viewinExplorer": {
-    "message": "Προβολή στον Εξερευνητή"
-  },
   "visitWebSite": {
     "message": "Επισκεφθείτε τον ιστότοπό μας"
   },
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 69f5cd019..f919903dc 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -259,6 +259,22 @@
   "betaWelcome": {
     "message": "Welcome to MetaMask Beta"
   },
+  "blockExplorerAccountAction": {
+    "message": "Account",
+    "description": "This is used with viewOnEtherscan and viewInExplorer e.g View Account in Explorer"
+  },
+  "blockExplorerAssetAction": {
+    "message": "Asset",
+    "description": "This is used with viewOnEtherscan and viewInExplorer e.g View Asset in Explorer"
+  },
+  "blockExplorerSwapAction": {
+    "message": "Swap",
+    "description": "This is used with viewOnEtherscan e.g View Swap on Etherscan"
+  },
+  "blockExplorerTransactionAction": {
+    "message": "Transaction",
+    "description": "This is used with viewOnCustomBlockExplorer and viewOnEtherscan e.g View Transaction on Etherscan"
+  },
   "blockExplorerUrl": {
     "message": "Block Explorer URL"
   },
@@ -2781,13 +2797,16 @@
     "message": "View More"
   },
   "viewOnCustomBlockExplorer": {
-    "message": "View at $1"
+    "message": "View $1 at $2",
+    "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Exporer URL"
   },
   "viewOnEtherscan": {
-    "message": "View on Etherscan"
+    "message": "View $1 on Etherscan",
+    "description": "$1 is the action type. e.g (Account, Transaction, Swap)"
   },
   "viewinExplorer": {
-    "message": "View in Explorer"
+    "message": "View $1 in Explorer",
+    "description": "$1 is the action type. e.g (Account, Transaction, Swap)"
   },
   "visitWebSite": {
     "message": "Visit our web site"
diff --git a/app/_locales/es/messages.json b/app/_locales/es/messages.json
index b799d2d59..61253b8af 100644
--- a/app/_locales/es/messages.json
+++ b/app/_locales/es/messages.json
@@ -2386,15 +2386,6 @@
   "viewMore": {
     "message": "Ver más"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Ver en $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Ver en Etherscan"
-  },
-  "viewinExplorer": {
-    "message": "Ver en el explorador"
-  },
   "visitWebSite": {
     "message": "Visite nuestro sitio web"
   },
diff --git a/app/_locales/es_419/messages.json b/app/_locales/es_419/messages.json
index b799d2d59..61253b8af 100644
--- a/app/_locales/es_419/messages.json
+++ b/app/_locales/es_419/messages.json
@@ -2386,15 +2386,6 @@
   "viewMore": {
     "message": "Ver más"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Ver en $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Ver en Etherscan"
-  },
-  "viewinExplorer": {
-    "message": "Ver en el explorador"
-  },
   "visitWebSite": {
     "message": "Visite nuestro sitio web"
   },
diff --git a/app/_locales/et/messages.json b/app/_locales/et/messages.json
index f44cca3d0..56ace12ae 100644
--- a/app/_locales/et/messages.json
+++ b/app/_locales/et/messages.json
@@ -1117,15 +1117,6 @@
   "viewContact": {
     "message": "Kuva kontakt"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Vaata $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Kuva Etherscanil"
-  },
-  "viewinExplorer": {
-    "message": "Kuva Exploreris"
-  },
   "visitWebSite": {
     "message": "Külastage meie veebilehte"
   },
diff --git a/app/_locales/fa/messages.json b/app/_locales/fa/messages.json
index 96b9ece48..b4eb1580c 100644
--- a/app/_locales/fa/messages.json
+++ b/app/_locales/fa/messages.json
@@ -1127,15 +1127,6 @@
   "viewContact": {
     "message": "مشاهده تماس"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "مشاهده در 1$1"
-  },
-  "viewOnEtherscan": {
-    "message": "مشاهده در ایترسکن"
-  },
-  "viewinExplorer": {
-    "message": "مشاهده در براوزر"
-  },
   "visitWebSite": {
     "message": "از وب سایت ما دیدن نمایید"
   },
diff --git a/app/_locales/fi/messages.json b/app/_locales/fi/messages.json
index 2baae1b63..a7eb15365 100644
--- a/app/_locales/fi/messages.json
+++ b/app/_locales/fi/messages.json
@@ -1124,15 +1124,6 @@
   "viewContact": {
     "message": "Näytä yhteyshenkilö"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Tarkastele kohdassa $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Näytä Etherscanissa"
-  },
-  "viewinExplorer": {
-    "message": "Tarkastele Explorerissa"
-  },
   "visitWebSite": {
     "message": "Vieraile verkkosivustollamme"
   },
diff --git a/app/_locales/fil/messages.json b/app/_locales/fil/messages.json
index a472c8bd0..235694a2f 100644
--- a/app/_locales/fil/messages.json
+++ b/app/_locales/fil/messages.json
@@ -1021,15 +1021,6 @@
   "viewContact": {
     "message": "Tingnan ang Contact"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Tingnan sa $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Tingnan sa Etherscan"
-  },
-  "viewinExplorer": {
-    "message": "Tingnan sa Explorer"
-  },
   "visitWebSite": {
     "message": "Bisitahin ang aming web site"
   },
diff --git a/app/_locales/fr/messages.json b/app/_locales/fr/messages.json
index f43a8f73f..6421c9c9e 100644
--- a/app/_locales/fr/messages.json
+++ b/app/_locales/fr/messages.json
@@ -1103,15 +1103,6 @@
   "viewContact": {
     "message": "Voir contact"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Afficher sur $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Voir sur Etherscan"
-  },
-  "viewinExplorer": {
-    "message": "Afficher dans Explorer"
-  },
   "visitWebSite": {
     "message": "Visitez notre site web"
   },
diff --git a/app/_locales/he/messages.json b/app/_locales/he/messages.json
index 3ddc4e4fb..0fca79788 100644
--- a/app/_locales/he/messages.json
+++ b/app/_locales/he/messages.json
@@ -1121,15 +1121,6 @@
   "viewContact": {
     "message": "הצג איש קשר"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "הצג ב- $1"
-  },
-  "viewOnEtherscan": {
-    "message": "הצג ב-Etherscan"
-  },
-  "viewinExplorer": {
-    "message": "הצג באקספלורר"
-  },
   "visitWebSite": {
     "message": "בקר/י באתר שלנו"
   },
diff --git a/app/_locales/hi/messages.json b/app/_locales/hi/messages.json
index de8052516..938b2d6fa 100644
--- a/app/_locales/hi/messages.json
+++ b/app/_locales/hi/messages.json
@@ -2386,15 +2386,6 @@
   "viewMore": {
     "message": "और देखें"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "$1 पर देखें"
-  },
-  "viewOnEtherscan": {
-    "message": "Etherscan पर देखें"
-  },
-  "viewinExplorer": {
-    "message": "एक्सप्लोरर में देखें"
-  },
   "visitWebSite": {
     "message": "हमारी वेबसाइट पर जाएँ"
   },
diff --git a/app/_locales/hr/messages.json b/app/_locales/hr/messages.json
index 656fd84f9..bf117d2ba 100644
--- a/app/_locales/hr/messages.json
+++ b/app/_locales/hr/messages.json
@@ -1117,15 +1117,6 @@
   "viewContact": {
     "message": "Prikaži kontakt"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Prikaži u $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Prikaži na Etherscanu"
-  },
-  "viewinExplorer": {
-    "message": "Prikaži u Exploreru"
-  },
   "visitWebSite": {
     "message": "Posjetite naše mrežno mjesto"
   },
diff --git a/app/_locales/ht/messages.json b/app/_locales/ht/messages.json
index 6cab69c61..0b11ad742 100644
--- a/app/_locales/ht/messages.json
+++ b/app/_locales/ht/messages.json
@@ -688,9 +688,6 @@
   "viewAccount": {
     "message": "Wè Kont"
   },
-  "viewOnEtherscan": {
-    "message": "Wè sou Etherscan"
-  },
   "visitWebSite": {
     "message": "Vizite sit entènèt nou an"
   },
diff --git a/app/_locales/hu/messages.json b/app/_locales/hu/messages.json
index 7e8cec14b..77ead06f9 100644
--- a/app/_locales/hu/messages.json
+++ b/app/_locales/hu/messages.json
@@ -1117,15 +1117,6 @@
   "viewContact": {
     "message": "Névjegy megtekintése"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Megtekintés $1-kor"
-  },
-  "viewOnEtherscan": {
-    "message": "Nézze meg Etherscanen"
-  },
-  "viewinExplorer": {
-    "message": "Megtekintés Explorerben"
-  },
   "visitWebSite": {
     "message": "Látogass el weboldalunkra"
   },
diff --git a/app/_locales/id/messages.json b/app/_locales/id/messages.json
index 80f539ad7..7df93469d 100644
--- a/app/_locales/id/messages.json
+++ b/app/_locales/id/messages.json
@@ -2386,15 +2386,6 @@
   "viewMore": {
     "message": "Lihat Selengkapnya"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Lihat di $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Lihat di Etherscan"
-  },
-  "viewinExplorer": {
-    "message": "Lihat di Explorer"
-  },
   "visitWebSite": {
     "message": "Kunjungi situs web kami"
   },
diff --git a/app/_locales/it/messages.json b/app/_locales/it/messages.json
index 57122608d..7752a1a4e 100644
--- a/app/_locales/it/messages.json
+++ b/app/_locales/it/messages.json
@@ -1947,15 +1947,6 @@
   "viewContact": {
     "message": "Visualizza contatto"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Vedi su $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Vedi su Etherscan"
-  },
-  "viewinExplorer": {
-    "message": "Vista in Explorer"
-  },
   "visitWebSite": {
     "message": "Visita il nostro sito web"
   },
diff --git a/app/_locales/ja/messages.json b/app/_locales/ja/messages.json
index e6f295cc6..c7f368b3e 100644
--- a/app/_locales/ja/messages.json
+++ b/app/_locales/ja/messages.json
@@ -2386,15 +2386,6 @@
   "viewMore": {
     "message": "詳細を表示"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "$1 に表示"
-  },
-  "viewOnEtherscan": {
-    "message": "Etherscan で表示"
-  },
-  "viewinExplorer": {
-    "message": "Explorer で表示"
-  },
   "visitWebSite": {
     "message": "当社の Web サイトにアクセス"
   },
diff --git a/app/_locales/kn/messages.json b/app/_locales/kn/messages.json
index 7aad7aed4..33566d0e1 100644
--- a/app/_locales/kn/messages.json
+++ b/app/_locales/kn/messages.json
@@ -1127,15 +1127,6 @@
   "viewContact": {
     "message": "ಸಂಪರ್ಕವನ್ನು ವೀಕ್ಷಿಸಿ"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "$1 ನಲ್ಲಿ ವೀಕ್ಷಿಸಿ"
-  },
-  "viewOnEtherscan": {
-    "message": "ಎಥೆರ್‌ಸ್ಕ್ಯಾನ್‌ನಲ್ಲಿ ವೀಕ್ಷಿಸಿ"
-  },
-  "viewinExplorer": {
-    "message": "ಎಕ್ಸ್‌ಪ್ಲೋರರ್‌ನಲ್ಲಿ ವೀಕ್ಷಿಸಿ"
-  },
   "visitWebSite": {
     "message": "ನಮ್ಮ ವೆಬ್ ಸೈಟ್ ಅನ್ನು ಭೇಟಿ ಮಾಡಿ"
   },
diff --git a/app/_locales/ko/messages.json b/app/_locales/ko/messages.json
index d62801f6a..d3346651a 100644
--- a/app/_locales/ko/messages.json
+++ b/app/_locales/ko/messages.json
@@ -2386,15 +2386,6 @@
   "viewMore": {
     "message": "더 보기"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "$1에서 보기"
-  },
-  "viewOnEtherscan": {
-    "message": "Etherscan에서 보기"
-  },
-  "viewinExplorer": {
-    "message": "탐색기에서 보기"
-  },
   "visitWebSite": {
     "message": "당사 웹사이트 방문하기"
   },
diff --git a/app/_locales/lt/messages.json b/app/_locales/lt/messages.json
index f315161d8..d0e00c629 100644
--- a/app/_locales/lt/messages.json
+++ b/app/_locales/lt/messages.json
@@ -1127,15 +1127,6 @@
   "viewContact": {
     "message": "Peržiūrėti kontaktą"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Peržiūrėti $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Peržiūrėti „Etherscan“"
-  },
-  "viewinExplorer": {
-    "message": "Peržiūrėti naršyklėje"
-  },
   "visitWebSite": {
     "message": "Apsilankykite mūsų svetainėje"
   },
diff --git a/app/_locales/lv/messages.json b/app/_locales/lv/messages.json
index 3fdcc90a0..95ca3aac9 100644
--- a/app/_locales/lv/messages.json
+++ b/app/_locales/lv/messages.json
@@ -1123,15 +1123,6 @@
   "viewContact": {
     "message": "Skatīt līgumu"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Skatīt $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Skatīt Etherscan"
-  },
-  "viewinExplorer": {
-    "message": "Skatīt Explorer"
-  },
   "visitWebSite": {
     "message": "Apmeklējiet mūsu tīmekļa vietni"
   },
diff --git a/app/_locales/ms/messages.json b/app/_locales/ms/messages.json
index 38f440b04..aca80b0f5 100644
--- a/app/_locales/ms/messages.json
+++ b/app/_locales/ms/messages.json
@@ -1101,15 +1101,6 @@
   "viewContact": {
     "message": "Lihat Kenalan"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Lihat pada $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Lihat di Etherscan"
-  },
-  "viewinExplorer": {
-    "message": "Lihat di Explorer"
-  },
   "visitWebSite": {
     "message": "Kunjungi laman web kami"
   },
diff --git a/app/_locales/no/messages.json b/app/_locales/no/messages.json
index c63a23962..d23287630 100644
--- a/app/_locales/no/messages.json
+++ b/app/_locales/no/messages.json
@@ -1102,15 +1102,6 @@
   "viewContact": {
     "message": "Se kontrakt"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Vis ved $1 "
-  },
-  "viewOnEtherscan": {
-    "message": "Vis på Etherscan "
-  },
-  "viewinExplorer": {
-    "message": "Se i Explorer"
-  },
   "visitWebSite": {
     "message": "Besøk nettsiden vår "
   },
diff --git a/app/_locales/ph/messages.json b/app/_locales/ph/messages.json
index b4c123481..9e9a3842c 100644
--- a/app/_locales/ph/messages.json
+++ b/app/_locales/ph/messages.json
@@ -2386,15 +2386,6 @@
   "viewMore": {
     "message": "Tumingin Pa"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Tingnan sa $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Tingnan sa Etherscan"
-  },
-  "viewinExplorer": {
-    "message": "Tingnan sa Explorer"
-  },
   "visitWebSite": {
     "message": "Bisitahin ang aming website"
   },
diff --git a/app/_locales/pl/messages.json b/app/_locales/pl/messages.json
index 4c7b7383d..d014eaf77 100644
--- a/app/_locales/pl/messages.json
+++ b/app/_locales/pl/messages.json
@@ -1115,15 +1115,6 @@
   "viewContact": {
     "message": "Wyświetl kontakt"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Wyświetl w $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Zobacz na Etherscan"
-  },
-  "viewinExplorer": {
-    "message": "Wyświetl w przeglądarce"
-  },
   "visitWebSite": {
     "message": "Odwiedź naszą stronę"
   },
diff --git a/app/_locales/pt_BR/messages.json b/app/_locales/pt_BR/messages.json
index 79879c23f..23fa47567 100644
--- a/app/_locales/pt_BR/messages.json
+++ b/app/_locales/pt_BR/messages.json
@@ -2386,15 +2386,6 @@
   "viewMore": {
     "message": "Exibir Mais"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Exibir em $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Exibir no Etherscan"
-  },
-  "viewinExplorer": {
-    "message": "Exibir no Explorer"
-  },
   "visitWebSite": {
     "message": "Visite nosso website"
   },
diff --git a/app/_locales/ro/messages.json b/app/_locales/ro/messages.json
index 9e934ab69..390abc7f0 100644
--- a/app/_locales/ro/messages.json
+++ b/app/_locales/ro/messages.json
@@ -1108,15 +1108,6 @@
   "viewContact": {
     "message": "Vizualizare contact"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Vizualizați la $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Vizualizați pe Etherscan"
-  },
-  "viewinExplorer": {
-    "message": "Vizualizare în Explorator"
-  },
   "visitWebSite": {
     "message": "Accesați site-ul nostru"
   },
diff --git a/app/_locales/ru/messages.json b/app/_locales/ru/messages.json
index 4d7457124..af81d4be3 100644
--- a/app/_locales/ru/messages.json
+++ b/app/_locales/ru/messages.json
@@ -2386,15 +2386,6 @@
   "viewMore": {
     "message": "Посмотреть больше"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Посмотреть на $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Посмотреть на Etherscan"
-  },
-  "viewinExplorer": {
-    "message": "Проводник в проводнике"
-  },
   "visitWebSite": {
     "message": "Посетите наш веб-сайт"
   },
diff --git a/app/_locales/sk/messages.json b/app/_locales/sk/messages.json
index 0a5af86c0..3fca411cd 100644
--- a/app/_locales/sk/messages.json
+++ b/app/_locales/sk/messages.json
@@ -1084,15 +1084,6 @@
   "viewContact": {
     "message": "Zobraziť kontakt"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Zobraziť na $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Zobraziť na Etherscan"
-  },
-  "viewinExplorer": {
-    "message": "Zobraziť v Exploreri"
-  },
   "visitWebSite": {
     "message": "Navštivte naši stránku"
   },
diff --git a/app/_locales/sl/messages.json b/app/_locales/sl/messages.json
index 2aa7c40d3..be803fe10 100644
--- a/app/_locales/sl/messages.json
+++ b/app/_locales/sl/messages.json
@@ -1112,15 +1112,6 @@
   "viewContact": {
     "message": "Ogled stika"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Ogled na $1 "
-  },
-  "viewOnEtherscan": {
-    "message": "Poglej na Etherscan"
-  },
-  "viewinExplorer": {
-    "message": "Ogled v Explorerju"
-  },
   "visitWebSite": {
     "message": "Obiščite našo spletno stran"
   },
diff --git a/app/_locales/sr/messages.json b/app/_locales/sr/messages.json
index 5a21f3df1..4190db660 100644
--- a/app/_locales/sr/messages.json
+++ b/app/_locales/sr/messages.json
@@ -1112,15 +1112,6 @@
   "viewContact": {
     "message": "Pogledaj kontakt"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Pogledaj na $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Pogledaj na Etherscan-u"
-  },
-  "viewinExplorer": {
-    "message": "Pogledati u Explorer-u"
-  },
   "visitWebSite": {
     "message": "Posetite našu veb lokaciju"
   },
diff --git a/app/_locales/sv/messages.json b/app/_locales/sv/messages.json
index 790027eda..0a42affa6 100644
--- a/app/_locales/sv/messages.json
+++ b/app/_locales/sv/messages.json
@@ -1102,15 +1102,6 @@
   "viewContact": {
     "message": "Visa kontakt"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Visa vid $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Visa på Etherscan"
-  },
-  "viewinExplorer": {
-    "message": "Visa i Utforskaren"
-  },
   "visitWebSite": {
     "message": "Besök vår hemsida"
   },
diff --git a/app/_locales/sw/messages.json b/app/_locales/sw/messages.json
index 79c583c7b..5c3aed3c9 100644
--- a/app/_locales/sw/messages.json
+++ b/app/_locales/sw/messages.json
@@ -1105,15 +1105,6 @@
   "viewContact": {
     "message": "Tazama Mawasiliano"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Tazama kwenye $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Tazama kwenye Etherscan"
-  },
-  "viewinExplorer": {
-    "message": "Tazama kwenye Explorer"
-  },
   "visitWebSite": {
     "message": "Tembelea Tovuti yetu"
   },
diff --git a/app/_locales/tl/messages.json b/app/_locales/tl/messages.json
index c65591572..fa48e14f6 100644
--- a/app/_locales/tl/messages.json
+++ b/app/_locales/tl/messages.json
@@ -1900,15 +1900,6 @@
   "viewContact": {
     "message": "Tingnan ang Contact"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Tingnan sa $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Tingnan sa Etherscan"
-  },
-  "viewinExplorer": {
-    "message": "Tingnan sa Explorer"
-  },
   "visitWebSite": {
     "message": "Bisitahin ang aming website"
   },
diff --git a/app/_locales/uk/messages.json b/app/_locales/uk/messages.json
index a9d372722..49509ac46 100644
--- a/app/_locales/uk/messages.json
+++ b/app/_locales/uk/messages.json
@@ -1127,15 +1127,6 @@
   "viewContact": {
     "message": "Переглянути контакт"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Дивитись на $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Дивитись на Etherscan"
-  },
-  "viewinExplorer": {
-    "message": "Дивитись в Explorer"
-  },
   "visitWebSite": {
     "message": "Відвідайте наш веб-сайт"
   },
diff --git a/app/_locales/vi/messages.json b/app/_locales/vi/messages.json
index 1553f11ee..5426ec6e5 100644
--- a/app/_locales/vi/messages.json
+++ b/app/_locales/vi/messages.json
@@ -2386,15 +2386,6 @@
   "viewMore": {
     "message": "Xem thêm"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "Xem tại $1"
-  },
-  "viewOnEtherscan": {
-    "message": "Xem trên Etherscan"
-  },
-  "viewinExplorer": {
-    "message": "Xem trên trình khám phá"
-  },
   "visitWebSite": {
     "message": "Truy cập trang web của chúng tôi"
   },
diff --git a/app/_locales/zh_CN/messages.json b/app/_locales/zh_CN/messages.json
index 1a83650d4..cbc14db78 100644
--- a/app/_locales/zh_CN/messages.json
+++ b/app/_locales/zh_CN/messages.json
@@ -1924,15 +1924,6 @@
   "viewContact": {
     "message": "查看联系人"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "在 $1 查看"
-  },
-  "viewOnEtherscan": {
-    "message": "在 Etherscan(以太坊浏览器)上查看"
-  },
-  "viewinExplorer": {
-    "message": "在浏览器中查看"
-  },
   "visitWebSite": {
     "message": "访问我们的网站"
   },
diff --git a/app/_locales/zh_TW/messages.json b/app/_locales/zh_TW/messages.json
index 045826588..86263d617 100644
--- a/app/_locales/zh_TW/messages.json
+++ b/app/_locales/zh_TW/messages.json
@@ -1115,15 +1115,6 @@
   "viewContact": {
     "message": "觀看聯絡資訊"
   },
-  "viewOnCustomBlockExplorer": {
-    "message": "在 $1 瀏覽"
-  },
-  "viewOnEtherscan": {
-    "message": "在 Etherscan 上瀏覽"
-  },
-  "viewinExplorer": {
-    "message": "在 Explorer 上瀏覽"
-  },
   "visitWebSite": {
     "message": "造訪我們的網站"
   },
diff --git a/ui/components/app/menu-bar/account-options-menu.js b/ui/components/app/menu-bar/account-options-menu.js
index ca9b0bfab..dfcdf2bfe 100644
--- a/ui/components/app/menu-bar/account-options-menu.js
+++ b/ui/components/app/menu-bar/account-options-menu.js
@@ -117,7 +117,9 @@ export default function AccountOptionsMenu({ anchorElement, onClose }) {
         }
         iconClassName="fas fa-external-link-alt"
       >
-        {rpcPrefs.blockExplorerUrl ? t('viewinExplorer') : t('viewOnEtherscan')}
+        {rpcPrefs.blockExplorerUrl
+          ? t('viewinExplorer', [t('blockExplorerAccountAction')])
+          : t('viewOnEtherscan', [t('blockExplorerAccountAction')])}
       </MenuItem>
       <MenuItem
         data-testid="account-options-menu__connected-sites"
diff --git a/ui/components/app/modals/account-details-modal/account-details-modal.component.js b/ui/components/app/modals/account-details-modal/account-details-modal.component.js
index 6c8778625..5943cdca5 100644
--- a/ui/components/app/modals/account-details-modal/account-details-modal.component.js
+++ b/ui/components/app/modals/account-details-modal/account-details-modal.component.js
@@ -83,7 +83,9 @@ export default class AccountDetailsModal extends Component {
             ? this.context.t('blockExplorerView', [
                 rpcPrefs.blockExplorerUrl.match(/^https?:\/\/(.+)/u)[1],
               ])
-            : this.context.t('viewOnEtherscan')}
+            : this.context.t('viewOnEtherscan', [
+                this.context.t('blockExplorerAccountAction'),
+              ])}
         </Button>
 
         {exportPrivateKeyFeatureEnabled ? (
diff --git a/ui/components/app/transaction-list-item-details/transaction-list-item-details.component.js b/ui/components/app/transaction-list-item-details/transaction-list-item-details.component.js
index 1e0266573..75f637e4d 100644
--- a/ui/components/app/transaction-list-item-details/transaction-list-item-details.component.js
+++ b/ui/components/app/transaction-list-item-details/transaction-list-item-details.component.js
@@ -181,8 +181,13 @@ export default class TransactionListItemDetails extends PureComponent {
                 containerClassName="transaction-list-item-details__header-button-tooltip-container"
                 title={
                   blockExplorerUrl
-                    ? t('viewOnCustomBlockExplorer', [blockExplorerUrl])
-                    : t('viewOnEtherscan')
+                    ? t('viewOnCustomBlockExplorer', [
+                        t('blockExplorerTransactionAction'),
+                        blockExplorerUrl,
+                      ])
+                    : t('viewOnEtherscan', [
+                        t('blockExplorerTransactionAction'),
+                      ])
                 }
               >
                 <Button
diff --git a/ui/pages/asset/components/asset-options.js b/ui/pages/asset/components/asset-options.js
index dbb4ba5c6..08ec391ab 100644
--- a/ui/pages/asset/components/asset-options.js
+++ b/ui/pages/asset/components/asset-options.js
@@ -50,7 +50,9 @@ const AssetOptions = ({
               onClickBlockExplorer();
             }}
           >
-            {isEthNetwork ? t('viewOnEtherscan') : t('viewinExplorer')}
+            {isEthNetwork
+              ? t('viewOnEtherscan', [t('blockExplorerAssetAction')])
+              : t('viewinExplorer', [t('blockExplorerAssetAction')])}
           </MenuItem>
           {isNativeAsset ? null : (
             <MenuItem
diff --git a/ui/pages/swaps/awaiting-swap/awaiting-swap.test.js b/ui/pages/swaps/awaiting-swap/awaiting-swap.test.js
index 921f86cef..079f16a01 100644
--- a/ui/pages/swaps/awaiting-swap/awaiting-swap.test.js
+++ b/ui/pages/swaps/awaiting-swap/awaiting-swap.test.js
@@ -43,7 +43,7 @@ describe('AwaitingSwap', () => {
     );
     expect(getByText('Transaction complete')).toBeInTheDocument();
     expect(getByText('tokens received: ETH')).toBeInTheDocument();
-    expect(getByText('View at etherscan.io')).toBeInTheDocument();
+    expect(getByText('View Swap at etherscan.io')).toBeInTheDocument();
     expect(getByText('Create a new swap')).toBeInTheDocument();
   });
 });
diff --git a/ui/pages/swaps/awaiting-swap/view-on-ether-scan-link/__snapshots__/view-on-ether-scan-link.test.js.snap b/ui/pages/swaps/awaiting-swap/view-on-ether-scan-link/__snapshots__/view-on-ether-scan-link.test.js.snap
index 309f67cc4..89d31d36d 100644
--- a/ui/pages/swaps/awaiting-swap/view-on-ether-scan-link/__snapshots__/view-on-ether-scan-link.test.js.snap
+++ b/ui/pages/swaps/awaiting-swap/view-on-ether-scan-link/__snapshots__/view-on-ether-scan-link.test.js.snap
@@ -5,7 +5,7 @@ exports[`ViewOnEtherScanLink renders the component with a custom block explorer
   <div
     class="awaiting-swap__view-on-etherscan awaiting-swap__view-on-etherscan--visible"
   >
-    View at custom-blockchain.explorer
+    View Swap at custom-blockchain.explorer
   </div>
 </div>
 `;
@@ -15,7 +15,7 @@ exports[`ViewOnEtherScanLink renders the component with initial props 1`] = `
   <div
     class="awaiting-swap__view-on-etherscan awaiting-swap__view-on-etherscan--visible"
   >
-    View on Etherscan
+    View Swap on Etherscan
   </div>
 </div>
 `;
diff --git a/ui/pages/swaps/awaiting-swap/view-on-ether-scan-link/view-on-ether-scan-link.js b/ui/pages/swaps/awaiting-swap/view-on-ether-scan-link/view-on-ether-scan-link.js
index ff7b275eb..5ba179505 100644
--- a/ui/pages/swaps/awaiting-swap/view-on-ether-scan-link/view-on-ether-scan-link.js
+++ b/ui/pages/swaps/awaiting-swap/view-on-ether-scan-link/view-on-ether-scan-link.js
@@ -34,8 +34,11 @@ export default function ViewOnEtherScanLink({
       }}
     >
       {isCustomBlockExplorerUrl
-        ? t('viewOnCustomBlockExplorer', [getURLHostName(blockExplorerUrl)])
-        : t('viewOnEtherscan')}
+        ? t('viewOnCustomBlockExplorer', [
+            t('blockExplorerSwapAction'),
+            getURLHostName(blockExplorerUrl),
+          ])
+        : t('viewOnEtherscan', [t('blockExplorerSwapAction')])}
     </div>
   );
 }
diff --git a/ui/pages/swaps/awaiting-swap/view-on-ether-scan-link/view-on-ether-scan-link.test.js b/ui/pages/swaps/awaiting-swap/view-on-ether-scan-link/view-on-ether-scan-link.test.js
index 91e32b82c..0fc96d87d 100644
--- a/ui/pages/swaps/awaiting-swap/view-on-ether-scan-link/view-on-ether-scan-link.test.js
+++ b/ui/pages/swaps/awaiting-swap/view-on-ether-scan-link/view-on-ether-scan-link.test.js
@@ -18,7 +18,7 @@ describe('ViewOnEtherScanLink', () => {
     const { container, getByText } = renderWithProvider(
       <ViewOnEtherScanLink {...createProps()} />,
     );
-    expect(getByText('View on Etherscan')).toBeInTheDocument();
+    expect(getByText('View Swap on Etherscan')).toBeInTheDocument();
     expect(container).toMatchSnapshot();
   });
 
@@ -31,7 +31,9 @@ describe('ViewOnEtherScanLink', () => {
         })}
       />,
     );
-    expect(getByText('View at custom-blockchain.explorer')).toBeInTheDocument();
+    expect(
+      getByText('View Swap at custom-blockchain.explorer'),
+    ).toBeInTheDocument();
     expect(container).toMatchSnapshot();
   });
 });

From db9253866e4e28e1e89510f27af488deb11168ea Mon Sep 17 00:00:00 2001
From: Alex Donesky <adonesky@gmail.com>
Date: Fri, 24 Sep 2021 14:46:38 -0500
Subject: [PATCH 08/56] fix cancel button styling bug (#12204)

---
 ui/components/app/cancel-button/cancel-button.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/ui/components/app/cancel-button/cancel-button.js b/ui/components/app/cancel-button/cancel-button.js
index 978af4076..bef23f98c 100644
--- a/ui/components/app/cancel-button/cancel-button.js
+++ b/ui/components/app/cancel-button/cancel-button.js
@@ -34,7 +34,7 @@ export default function CancelButton({
     <Button
       onClick={cancelTransaction}
       rounded={!detailsModal}
-      type={detailsModal ? 'raise' : null}
+      type={detailsModal ? 'raised' : null}
       className={classnames({
         'transaction-list-item__header-button': !detailsModal,
         'transaction-list-item-details__header-button': detailsModal,

From c076faeb8fde82d4997bca3a0955cd6b34e5450f Mon Sep 17 00:00:00 2001
From: Niranjana Binoy <43930900+NiranjanaBinoy@users.noreply.github.com>
Date: Mon, 27 Sep 2021 09:58:12 -0400
Subject: [PATCH 09/56] adding checksum conversion (#12209)

---
 ui/helpers/utils/icon-factory.js | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/ui/helpers/utils/icon-factory.js b/ui/helpers/utils/icon-factory.js
index f5a36b9f9..c7f2e5260 100644
--- a/ui/helpers/utils/icon-factory.js
+++ b/ui/helpers/utils/icon-factory.js
@@ -1,4 +1,7 @@
-import { isValidHexAddress } from '../../../shared/modules/hexstring-utils';
+import {
+  isValidHexAddress,
+  toChecksumHexAddress,
+} from '../../../shared/modules/hexstring-utils';
 
 let iconFactory;
 
@@ -25,7 +28,9 @@ IconFactory.prototype.iconForAddress = function (
   // So the flag indicates whether the address of tokens currently on the tokenList is checksum or not.
   // And since the token.address from allTokens is checksumaddress
   // tokenAddress have to be changed to lowercase when we are using dynamic list
-  const addr = useTokenDetection ? address.toLowerCase() : address;
+  const addr = useTokenDetection
+    ? address.toLowerCase()
+    : toChecksumHexAddress(address);
   if (iconExistsFor(addr, tokenList)) {
     return imageElFor(addr, useTokenDetection, tokenList);
   }

From 14f0d8245bd883b0b9e3ba021d528de38a1f2730 Mon Sep 17 00:00:00 2001
From: Alex Donesky <adonesky@gmail.com>
Date: Mon, 27 Sep 2021 09:06:07 -0500
Subject: [PATCH 10/56] fix issue where conversionRates aren't shown for tokens
 stored in non-checksum format (#12206)

---
 ui/hooks/useTokenFiatAmount.js | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/ui/hooks/useTokenFiatAmount.js b/ui/hooks/useTokenFiatAmount.js
index 1e3974525..dc52a979a 100644
--- a/ui/hooks/useTokenFiatAmount.js
+++ b/ui/hooks/useTokenFiatAmount.js
@@ -7,6 +7,7 @@ import {
 } from '../selectors';
 import { getTokenFiatAmount } from '../helpers/utils/token-util';
 import { getConversionRate } from '../ducks/metamask/metamask';
+import { isEqualCaseInsensitive } from '../helpers/utils/util';
 
 /**
  * Get the token balance converted to fiat and formatted for display
@@ -33,8 +34,13 @@ export function useTokenFiatAmount(
   const currentCurrency = useSelector(getCurrentCurrency);
   const userPrefersShownFiat = useSelector(getShouldShowFiat);
   const showFiat = overrides.showFiat ?? userPrefersShownFiat;
+  const contractExchangeTokenKey = Object.keys(
+    contractExchangeRates,
+  ).find((key) => isEqualCaseInsensitive(key, tokenAddress));
   const tokenExchangeRate =
-    overrides.exchangeRate ?? contractExchangeRates[tokenAddress];
+    overrides.exchangeRate ??
+    (contractExchangeTokenKey &&
+      contractExchangeRates[contractExchangeTokenKey]);
   const formattedFiat = useMemo(
     () =>
       getTokenFiatAmount(

From 77581256ca0872f13c54e7cff110c9234a0c5d19 Mon Sep 17 00:00:00 2001
From: Niranjana Binoy <43930900+NiranjanaBinoy@users.noreply.github.com>
Date: Mon, 27 Sep 2021 13:42:51 -0400
Subject: [PATCH 11/56] Hiding refresh list on non-Mainnet networks (#12210)

---
 app/_locales/en/messages.json                 |  3 ++
 ui/components/app/asset-list/asset-list.js    |  3 ++
 .../import-token-link.component.js            | 32 +++++++++++++------
 .../app/import-token-link/index.scss          |  2 +-
 4 files changed, 29 insertions(+), 11 deletions(-)

diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index f919903dc..1d27a4d5d 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -1632,6 +1632,9 @@
   "optionalCurrencySymbol": {
     "message": "Currency Symbol (optional)"
   },
+  "or": {
+    "message": "or"
+  },
   "origin": {
     "message": "Origin"
   },
diff --git a/ui/components/app/asset-list/asset-list.js b/ui/components/app/asset-list/asset-list.js
index 1353b8bd9..193b22619 100644
--- a/ui/components/app/asset-list/asset-list.js
+++ b/ui/components/app/asset-list/asset-list.js
@@ -13,6 +13,7 @@ import {
   getCurrentAccountWithSendEtherInfo,
   getShouldShowFiat,
   getNativeCurrencyImage,
+  getIsMainnet,
 } from '../../../selectors';
 import { getNativeCurrency } from '../../../ducks/metamask/metamask';
 import { useCurrencyDisplay } from '../../../hooks/useCurrencyDisplay';
@@ -75,6 +76,7 @@ const AssetList = ({ onClickAsset }) => {
   });
 
   const primaryTokenImage = useSelector(getNativeCurrencyImage);
+  const isMainnet = useSelector(getIsMainnet) || process.env.IN_TEST;
 
   return (
     <>
@@ -106,6 +108,7 @@ const AssetList = ({ onClickAsset }) => {
           </Typography>
         </Box>
         <ImportTokenLink
+          isMainnet={isMainnet}
           onClick={() => {
             history.push(IMPORT_TOKEN_ROUTE);
             addTokenEvent();
diff --git a/ui/components/app/import-token-link/import-token-link.component.js b/ui/components/app/import-token-link/import-token-link.component.js
index da616c170..40e153000 100644
--- a/ui/components/app/import-token-link/import-token-link.component.js
+++ b/ui/components/app/import-token-link/import-token-link.component.js
@@ -1,4 +1,5 @@
 import React from 'react';
+import PropTypes from 'prop-types';
 import { useHistory } from 'react-router-dom';
 import { useMetricEvent } from '../../../hooks/useMetricEvent';
 import { useI18nContext } from '../../../hooks/useI18nContext';
@@ -8,7 +9,7 @@ import Box from '../../ui/box/box';
 import { TEXT_ALIGN } from '../../../helpers/constants/design-system';
 import { detectNewTokens } from '../../../store/actions';
 
-export default function ImportTokenLink() {
+export default function ImportTokenLink({ isMainnet }) {
   const addTokenEvent = useMetricEvent({
     eventOpts: {
       category: 'Navigation',
@@ -21,14 +22,18 @@ export default function ImportTokenLink() {
 
   return (
     <Box className="import-token-link" textAlign={TEXT_ALIGN.CENTER}>
-      <Button
-        className="import-token-link__link"
-        type="link"
-        onClick={() => detectNewTokens()}
-      >
-        {t('refreshList')}
-      </Button>
-      {' or '}
+      {isMainnet && (
+        <>
+          <Button
+            className="import-token-link__link"
+            type="link"
+            onClick={() => detectNewTokens()}
+          >
+            {t('refreshList')}
+          </Button>
+          {t('or')}
+        </>
+      )}
       <Button
         className="import-token-link__link"
         type="link"
@@ -37,8 +42,15 @@ export default function ImportTokenLink() {
           addTokenEvent();
         }}
       >
-        {t('importTokens')}
+        {isMainnet
+          ? t('importTokens')
+          : t('importTokens').charAt(0).toUpperCase() +
+            t('importTokens').slice(1)}
       </Button>
     </Box>
   );
 }
+
+ImportTokenLink.propTypes = {
+  isMainnet: PropTypes.bool,
+};
diff --git a/ui/components/app/import-token-link/index.scss b/ui/components/app/import-token-link/index.scss
index 83586363a..a0d2a7ea7 100644
--- a/ui/components/app/import-token-link/index.scss
+++ b/ui/components/app/import-token-link/index.scss
@@ -3,6 +3,6 @@
     @include H6;
 
     display: inline;
-    padding: 0 0 16px;
+    padding: 0 5px 16px;
   }
 }

From 506fa2d74454ae841268ecbd39fc8161bae572ad Mon Sep 17 00:00:00 2001
From: Mark Stacey <markjstacey@gmail.com>
Date: Mon, 27 Sep 2021 19:05:35 -0230
Subject: [PATCH 12/56] Fix Buffer warnings during build (#10495)

The warnings about use of the unsafe Buffer constructor have been
addressed by package updates and patches.

The updates were:
 * `gulp-sourcemaps` was updated from v2 to v3, and was patched to
replace remaining uses of the `Buffer` constructor
   * Upstream PR: https://github.com/gulp-sourcemaps/gulp-sourcemaps/pull/388
 * The transitive dependency `yazl` was updated from v2.4.3 to v2.5.1
in the lockfile.
 * The abandoned packages `combine-source-map` and `inline-source-map`
were patched.
---
 lavamoat/node/policy.json                     |  29 ++---
 package.json                                  |   2 +-
 ...source-map++convert-source-map+1.1.3.patch |  22 ++++
 patches/gulp-sourcemaps+3.0.0.patch           |  44 ++++++++
 patches/inline-source-map+0.6.2.patch         |  13 +++
 yarn.lock                                     | 103 ++++++++++--------
 6 files changed, 145 insertions(+), 68 deletions(-)
 create mode 100644 patches/combine-source-map++convert-source-map+1.1.3.patch
 create mode 100644 patches/gulp-sourcemaps+3.0.0.patch
 create mode 100644 patches/inline-source-map+0.6.2.patch

diff --git a/lavamoat/node/policy.json b/lavamoat/node/policy.json
index 0cdbcf152..7497e459b 100644
--- a/lavamoat/node/policy.json
+++ b/lavamoat/node/policy.json
@@ -824,8 +824,8 @@
     "@gulp-sourcemaps/identity-map": {
       "packages": {
         "acorn": true,
-        "css": true,
         "normalize-path": true,
+        "postcss": true,
         "source-map": true,
         "through2": true
       }
@@ -962,15 +962,6 @@
         "uri-js": true
       }
     },
-    "amdefine": {
-      "builtin": {
-        "path.dirname": true
-      },
-      "globals": {
-        "__filename": true,
-        "process.nextTick": true
-      }
-    },
     "ansi-colors": {
       "packages": {
         "ansi-wrap": true
@@ -1492,7 +1483,7 @@
         "path.resolve": true
       },
       "globals": {
-        "Buffer": true
+        "Buffer.from": true
       },
       "packages": {
         "safe-buffer": true
@@ -1551,13 +1542,13 @@
     "css": {
       "builtin": {
         "fs.readFileSync": true,
-        "path.dirname": true
+        "path.dirname": true,
+        "path.sep": true
       },
       "packages": {
         "inherits": true,
         "source-map": true,
-        "source-map-resolve": true,
-        "urix": true
+        "source-map-resolve": true
       }
     },
     "d": {
@@ -2496,7 +2487,8 @@
         "path.sep": true
       },
       "globals": {
-        "Buffer": true
+        "Buffer.concat": true,
+        "Buffer.from": true
       },
       "packages": {
         "@gulp-sourcemaps/identity-map": true,
@@ -2665,7 +2657,7 @@
     },
     "inline-source-map": {
       "globals": {
-        "Buffer": true
+        "Buffer.from": true
       },
       "packages": {
         "source-map": true
@@ -4011,16 +4003,15 @@
         "console.time": true,
         "console.timeEnd": true,
         "fetch": true
-      },
-      "packages": {
-        "amdefine": true
       }
     },
     "source-map-resolve": {
       "builtin": {
+        "path.sep": true,
         "url.resolve": true
       },
       "globals": {
+        "TextDecoder": true,
         "setImmediate": true
       },
       "packages": {
diff --git a/package.json b/package.json
index ce8c9bc02..3096ec6fd 100644
--- a/package.json
+++ b/package.json
@@ -279,7 +279,7 @@
     "gulp-livereload": "4.0.0",
     "gulp-rename": "^2.0.0",
     "gulp-rtlcss": "^1.4.0",
-    "gulp-sourcemaps": "^2.6.0",
+    "gulp-sourcemaps": "^3.0.0",
     "gulp-stylelint": "^13.0.0",
     "gulp-watch": "^5.0.1",
     "gulp-zip": "^4.0.0",
diff --git a/patches/combine-source-map++convert-source-map+1.1.3.patch b/patches/combine-source-map++convert-source-map+1.1.3.patch
new file mode 100644
index 000000000..c24f12817
--- /dev/null
+++ b/patches/combine-source-map++convert-source-map+1.1.3.patch
@@ -0,0 +1,22 @@
+diff --git a/node_modules/combine-source-map/node_modules/convert-source-map/index.js b/node_modules/combine-source-map/node_modules/convert-source-map/index.js
+index bfe92d1..bee1ffe 100644
+--- a/node_modules/combine-source-map/node_modules/convert-source-map/index.js
++++ b/node_modules/combine-source-map/node_modules/convert-source-map/index.js
+@@ -9,7 +9,7 @@ var mapFileCommentRx =
+   /(?:\/\/[@#][ \t]+sourceMappingURL=([^\s'"]+?)[ \t]*$)|(?:\/\*[@#][ \t]+sourceMappingURL=([^\*]+?)[ \t]*(?:\*\/){1}[ \t]*$)/mg
+ 
+ function decodeBase64(base64) {
+-  return new Buffer(base64, 'base64').toString();
++  return Buffer.from(base64, 'base64').toString();
+ }
+ 
+ function stripComment(sm) {
+@@ -60,7 +60,7 @@ Converter.prototype.toJSON = function (space) {
+ 
+ Converter.prototype.toBase64 = function () {
+   var json = this.toJSON();
+-  return new Buffer(json).toString('base64');
++  return Buffer.from(json).toString('base64');
+ };
+ 
+ Converter.prototype.toComment = function (options) {
diff --git a/patches/gulp-sourcemaps+3.0.0.patch b/patches/gulp-sourcemaps+3.0.0.patch
new file mode 100644
index 000000000..056bcb8e2
--- /dev/null
+++ b/patches/gulp-sourcemaps+3.0.0.patch
@@ -0,0 +1,44 @@
+diff --git a/node_modules/gulp-sourcemaps/src/init/index.internals.js b/node_modules/gulp-sourcemaps/src/init/index.internals.js
+index 7104555..7dfe218 100644
+--- a/node_modules/gulp-sourcemaps/src/init/index.internals.js
++++ b/node_modules/gulp-sourcemaps/src/init/index.internals.js
+@@ -72,7 +72,7 @@ module.exports = function(options, file, fileContent) {
+ 
+       });
+       // remove source map comment from source
+-      file.contents = new Buffer(sources.content, 'utf8');
++      file.contents = Buffer.from(sources.content, 'utf8');
+     }
+ 
+   }
+diff --git a/node_modules/gulp-sourcemaps/src/write/index.internals.js b/node_modules/gulp-sourcemaps/src/write/index.internals.js
+index 89cee60..adfe8d1 100644
+--- a/node_modules/gulp-sourcemaps/src/write/index.internals.js
++++ b/node_modules/gulp-sourcemaps/src/write/index.internals.js
+@@ -99,7 +99,7 @@ module.exports = function(destPath, options) {
+ 
+     if (destPath === undefined || destPath === null) {
+       // encode source map into comment
+-      var base64Map = new Buffer(JSON.stringify(sourceMap)).toString('base64');
++      var base64Map = Buffer.from(JSON.stringify(sourceMap)).toString('base64');
+       comment = commentFormatter('data:application/json;charset=' + options.charset + ';base64,' + base64Map);
+     } else {
+       var mapFile = path.join(destPath, file.relative) + '.map';
+@@ -130,7 +130,7 @@ module.exports = function(destPath, options) {
+ 
+       var sourceMapFile = file.clone(options.clone || { deep: false, contents: false });
+       sourceMapFile.path = sourceMapPath;
+-      sourceMapFile.contents = new Buffer(JSON.stringify(sourceMap));
++      sourceMapFile.contents = Buffer.from(JSON.stringify(sourceMap));
+       sourceMapFile.stat = {
+         isFile: function() { return true; },
+         isDirectory: function() { return false; },
+@@ -164,7 +164,7 @@ module.exports = function(destPath, options) {
+ 
+     // append source map comment
+     if (options.addComment) {
+-      file.contents = Buffer.concat([file.contents, new Buffer(comment)]);
++      file.contents = Buffer.concat([file.contents, Buffer.from(comment)]);
+     }
+   }
+ 
diff --git a/patches/inline-source-map+0.6.2.patch b/patches/inline-source-map+0.6.2.patch
new file mode 100644
index 000000000..5e5ae09a0
--- /dev/null
+++ b/patches/inline-source-map+0.6.2.patch
@@ -0,0 +1,13 @@
+diff --git a/node_modules/inline-source-map/index.js b/node_modules/inline-source-map/index.js
+index df74d61..7641aad 100644
+--- a/node_modules/inline-source-map/index.js
++++ b/node_modules/inline-source-map/index.js
+@@ -91,7 +91,7 @@ Generator.prototype.addSourceContent = function (sourceFile, sourcesContent) {
+  */
+ Generator.prototype.base64Encode = function () {
+   var map = this.toString();
+-  return new Buffer(map).toString('base64');
++  return Buffer.from(map).toString('base64');
+ };
+ 
+ /**
diff --git a/yarn.lock b/yarn.lock
index dcdf5a466..ed807e29c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2129,18 +2129,18 @@
   resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.13.0.tgz#fcb113d1aca4b471b709e8c9c168674fbd6e06d9"
   integrity sha512-xKOeQEl5O47GPZYIMToj6uuA2syyFlq9EMSl2ui0uytjY9xbe8XS0pexNWmxrdcCyNGyDmLyYw5FtKsalBUeOg==
 
-"@gulp-sourcemaps/identity-map@1.X":
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/@gulp-sourcemaps/identity-map/-/identity-map-1.0.1.tgz#cfa23bc5840f9104ce32a65e74db7e7a974bbee1"
-  integrity sha1-z6I7xYQPkQTOMqZedNt+epdLvuE=
+"@gulp-sourcemaps/identity-map@^2.0.1":
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/@gulp-sourcemaps/identity-map/-/identity-map-2.0.1.tgz#a6e8b1abec8f790ec6be2b8c500e6e68037c0019"
+  integrity sha512-Tb+nSISZku+eQ4X1lAkevcQa+jknn/OVUgZ3XCxEKIsLsqYuPoJwJOPQeaOk75X3WPftb29GWY1eqE7GLsXb1Q==
   dependencies:
-    acorn "^5.0.3"
-    css "^2.2.1"
-    normalize-path "^2.1.1"
-    source-map "^0.5.6"
-    through2 "^2.0.3"
+    acorn "^6.4.1"
+    normalize-path "^3.0.0"
+    postcss "^7.0.16"
+    source-map "^0.6.0"
+    through2 "^3.0.1"
 
-"@gulp-sourcemaps/map-sources@1.X":
+"@gulp-sourcemaps/map-sources@^1.0.0":
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz#890ae7c5d8c877f6d384860215ace9d7ec945bda"
   integrity sha1-iQrnxdjId/bThIYCFazp1+yUW9o=
@@ -4805,16 +4805,16 @@ acorn-walk@^7.1.1:
   resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
   integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
 
-acorn@5.X, acorn@^5.0.3, acorn@^5.1.2, acorn@^5.2.1:
-  version "5.7.4"
-  resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e"
-  integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==
-
 acorn@^3.0.4:
   version "3.3.0"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
   integrity sha1-ReN/s56No/JbruP/U2niu18iAXo=
 
+acorn@^5.1.2, acorn@^5.2.1:
+  version "5.7.4"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e"
+  integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==
+
 acorn@^6.0.1, acorn@^6.0.2, acorn@^6.0.7, acorn@^6.4.1:
   version "6.4.2"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6"
@@ -8642,7 +8642,7 @@ continuable-cache@^0.3.1:
   resolved "https://registry.yarnpkg.com/continuable-cache/-/continuable-cache-0.3.1.tgz#bd727a7faed77e71ff3985ac93351a912733ad0f"
   integrity sha1-vXJ6f67XfnH/OYWskzUakSczrQ8=
 
-convert-source-map@1.7.0, convert-source-map@1.X, convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.5.1, convert-source-map@^1.6.0, convert-source-map@^1.7.0:
+convert-source-map@1.7.0, convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.5.1, convert-source-map@^1.6.0, convert-source-map@^1.7.0:
   version "1.7.0"
   resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
   integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==
@@ -8654,6 +8654,13 @@ convert-source-map@^0.3.3:
   resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-0.3.5.tgz#f1d802950af7dd2631a1febe0596550c86ab3190"
   integrity sha1-8dgClQr33SYxof6+BZZVDIarMZA=
 
+convert-source-map@^1.0.0:
+  version "1.8.0"
+  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369"
+  integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==
+  dependencies:
+    safe-buffer "~5.1.1"
+
 convert-source-map@~1.1.0:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3bf3673888e204699860"
@@ -9058,7 +9065,7 @@ css.escape@^1.5.1:
   resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb"
   integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=
 
-css@2.X, css@^2.0.0, css@^2.2.1:
+css@^2.0.0:
   version "2.2.3"
   resolved "https://registry.yarnpkg.com/css/-/css-2.2.3.tgz#f861f4ba61e79bedc962aa548e5780fd95cbc6be"
   integrity sha512-0W171WccAjQGGTKLhw4m2nnl0zPHUlTO/I8td4XzJgIB8Hg3ZZx71qT4G4eX8OVsSiaAKiUMy73E3nsbPlg2DQ==
@@ -9255,10 +9262,10 @@ debounce@^1.0.0:
   resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.1.0.tgz#6a1a4ee2a9dc4b7c24bb012558dbcdb05b37f408"
   integrity sha512-ZQVKfRVlwRfD150ndzEK8M90ABT+Y/JQKs4Y7U4MXdpuoUkkrr4DwKbVux3YjylA5bUMUj0Nc3pMxPJX6N2QQQ==
 
-debug-fabulous@1.X:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/debug-fabulous/-/debug-fabulous-1.0.0.tgz#57f6648646097b1b0849dcda0017362c1ec00f8b"
-  integrity sha512-dsd50qQ1atDeurcxL7XOjPp4nZCGZzWIONDujDXzl1atSyC3hMbZD+v6440etw+Vt0Pr8ce4TQzHfX3KZM05Mw==
+debug-fabulous@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/debug-fabulous/-/debug-fabulous-1.1.0.tgz#af8a08632465224ef4174a9f06308c3c2a1ebc8e"
+  integrity sha512-GZqvGIgKNlUnHUPQhepnUZFIMoi3dgZKQBzKDeL2g7oJF9SNAji/AAu36dusFUas0O+pae74lNeoIPHqXWDkLg==
   dependencies:
     debug "3.X"
     memoizee "0.4.X"
@@ -9671,7 +9678,7 @@ detect-libc@^1.0.2:
   resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
   integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
 
-detect-newline@2.X:
+detect-newline@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2"
   integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=
@@ -13764,7 +13771,7 @@ got@^7.1.0:
     url-parse-lax "^1.0.0"
     url-to-options "^1.0.1"
 
-graceful-fs@4.X, graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.3:
+graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.3:
   version "4.2.4"
   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
   integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
@@ -13874,22 +13881,22 @@ gulp-rtlcss@^1.4.0:
     through2 "^2.0.5"
     vinyl-sourcemaps-apply "^0.2.1"
 
-gulp-sourcemaps@^2.6.0:
-  version "2.6.3"
-  resolved "https://registry.yarnpkg.com/gulp-sourcemaps/-/gulp-sourcemaps-2.6.3.tgz#11b033f759f909e0a5f15b7bdf47ac29cc54efa4"
-  integrity sha1-EbAz91n5CeCl8Vt730esKcxU76Q=
+gulp-sourcemaps@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/gulp-sourcemaps/-/gulp-sourcemaps-3.0.0.tgz#2e154e1a2efed033c0e48013969e6f30337b2743"
+  integrity sha512-RqvUckJkuYqy4VaIH60RMal4ZtG0IbQ6PXMNkNsshEGJ9cldUPRb/YCgboYae+CLAs1HQNb4ADTKCx65HInquQ==
   dependencies:
-    "@gulp-sourcemaps/identity-map" "1.X"
-    "@gulp-sourcemaps/map-sources" "1.X"
-    acorn "5.X"
-    convert-source-map "1.X"
-    css "2.X"
-    debug-fabulous "1.X"
-    detect-newline "2.X"
-    graceful-fs "4.X"
-    source-map "0.X"
-    strip-bom-string "1.X"
-    through2 "2.X"
+    "@gulp-sourcemaps/identity-map" "^2.0.1"
+    "@gulp-sourcemaps/map-sources" "^1.0.0"
+    acorn "^6.4.1"
+    convert-source-map "^1.0.0"
+    css "^3.0.0"
+    debug-fabulous "^1.0.0"
+    detect-newline "^2.0.0"
+    graceful-fs "^4.0.0"
+    source-map "^0.6.0"
+    strip-bom-string "^1.0.0"
+    through2 "^2.0.0"
 
 gulp-stylelint@^13.0.0:
   version "13.0.0"
@@ -25270,11 +25277,6 @@ source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, sourc
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
   integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
 
-source-map@0.X, source-map@^0.7.2, source-map@^0.7.3, source-map@~0.7.2:
-  version "0.7.3"
-  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
-  integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
-
 source-map@^0.1.38:
   version "0.1.43"
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346"
@@ -25287,6 +25289,11 @@ source-map@^0.5.0, source-map@^0.5.1, source-map@^0.5.6, source-map@^0.5.7, sour
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
   integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
 
+source-map@^0.7.2, source-map@^0.7.3, source-map@~0.7.2:
+  version "0.7.3"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
+  integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
+
 sourcemap-codec@^1.4.1, sourcemap-codec@^1.4.4:
   version "1.4.8"
   resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
@@ -25861,7 +25868,7 @@ strip-bom-stream@^2.0.0:
     first-chunk-stream "^2.0.0"
     strip-bom "^2.0.0"
 
-strip-bom-string@1.X:
+strip-bom-string@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92"
   integrity sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=
@@ -26444,7 +26451,7 @@ through2-filter@^2.0.0:
     through2 "~2.0.0"
     xtend "~4.0.0"
 
-through2@2.X, through2@^2.0.0, through2@^2.0.1, through2@^2.0.3, through2@^2.0.5, through2@~2.0.0, through2@~2.0.3:
+through2@^2.0.0, through2@^2.0.1, through2@^2.0.3, through2@^2.0.5, through2@~2.0.0, through2@~2.0.3:
   version "2.0.5"
   resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
   integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==
@@ -28872,9 +28879,9 @@ yauzl@2.10.0, yauzl@^2.10.0:
     fd-slicer "~1.1.0"
 
 yazl@^2.1.0:
-  version "2.4.3"
-  resolved "https://registry.yarnpkg.com/yazl/-/yazl-2.4.3.tgz#ec26e5cc87d5601b9df8432dbdd3cd2e5173a071"
-  integrity sha1-7CblzIfVYBud+EMtvdPNLlFzoHE=
+  version "2.5.1"
+  resolved "https://registry.yarnpkg.com/yazl/-/yazl-2.5.1.tgz#a3d65d3dd659a5b0937850e8609f22fffa2b5c35"
+  integrity sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==
   dependencies:
     buffer-crc32 "~0.2.3"
 

From d3f7464333ae6aaa50901dc5b9b809e4bd34fa65 Mon Sep 17 00:00:00 2001
From: kumavis <kumavis@users.noreply.github.com>
Date: Mon, 27 Sep 2021 12:39:31 -1000
Subject: [PATCH 13/56] Ci test config improvement (#12223)

* ci - use same resource class for chrome and ff e2e tests

* e2e:chrome - enable logging by default

* lint fix
---
 .circleci/config.yml         | 2 +-
 test/e2e/webdriver/chrome.js | 7 ++-----
 2 files changed, 3 insertions(+), 6 deletions(-)

diff --git a/.circleci/config.yml b/.circleci/config.yml
index 973ee001a..a2ab03bfc 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -391,7 +391,7 @@ jobs:
           destination: test-artifacts
 
   test-e2e-firefox:
-    executor: node-browsers
+    executor: node-browsers-medium-plus
     steps:
       - checkout
       - run:
diff --git a/test/e2e/webdriver/chrome.js b/test/e2e/webdriver/chrome.js
index bf3c2d023..9a1e1294b 100644
--- a/test/e2e/webdriver/chrome.js
+++ b/test/e2e/webdriver/chrome.js
@@ -16,13 +16,10 @@ class ChromeDriver {
       .setChromeOptions(options);
     const service = new chrome.ServiceBuilder();
 
-    // Enables Chrome logging.
+    // Enables Chrome logging. Default: enabled
     // Especially useful for discovering why Chrome has crashed, but can also
     // be useful for revealing console errors (from the page or background).
-    if (
-      process.env.ENABLE_CHROME_LOGGING &&
-      process.env.ENABLE_CHROME_LOGGING !== 'false'
-    ) {
+    if (process.env.ENABLE_CHROME_LOGGING !== 'false') {
       service.setStdio('inherit').enableChromeLogging();
     }
     if (port) {

From 2e2bd0fce5ca48f5b2646409c727f524c0d35dc6 Mon Sep 17 00:00:00 2001
From: David Walsh <davidwalsh83@gmail.com>
Date: Mon, 27 Sep 2021 17:40:05 -0500
Subject: [PATCH 14/56] Prevent fox mesh JSON warning (#12200)

* Prevent fox mesh JSON warning

* Update ui/helpers/utils/build-types.js

Co-authored-by: Mark Stacey <markjstacey@gmail.com>

* Fix lint

Co-authored-by: Mark Stacey <markjstacey@gmail.com>
---
 ui/helpers/utils/build-types.js | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/ui/helpers/utils/build-types.js b/ui/helpers/utils/build-types.js
index d6ec658f8..37b4c139d 100644
--- a/ui/helpers/utils/build-types.js
+++ b/ui/helpers/utils/build-types.js
@@ -20,7 +20,10 @@ export function isBeta() {
 // the current metamask version (i.e. main, beta, etc.)
 export function getBuildSpecificAsset(assetName) {
   const buildType = process.env.METAMASK_BUILD_TYPE;
-  if (!assetList[buildType]?.[assetName]) {
+  if (
+    !assetList[buildType] ||
+    !Object.keys(assetList[buildType]).includes(assetName)
+  ) {
     console.warn(
       `Cannot find asset for build ${buildType}: ${assetName}, returning main build asset`,
     );

From 180ead81751b9bd92f3da62d44aed1ddea7b69b8 Mon Sep 17 00:00:00 2001
From: Jyoti Puri <jyotipuri@gmail.com>
Date: Tue, 28 Sep 2021 11:20:21 +0530
Subject: [PATCH 15/56] Adding slider component (#12219)

Adding slider component
---
 ui/components/ui/slider/index.js              |   3 +
 ui/components/ui/slider/index.scss            |  48 +++++++
 ui/components/ui/slider/slider.component.js   | 135 ++++++++++++++++++
 .../ui/slider/slider.component.test.js        |  58 ++++++++
 ui/components/ui/slider/slider.stories.js     |  33 +++++
 ui/components/ui/ui-components.scss           |   1 +
 ui/helpers/constants/design-system.js         |   2 +-
 7 files changed, 279 insertions(+), 1 deletion(-)
 create mode 100644 ui/components/ui/slider/index.js
 create mode 100644 ui/components/ui/slider/index.scss
 create mode 100644 ui/components/ui/slider/slider.component.js
 create mode 100644 ui/components/ui/slider/slider.component.test.js
 create mode 100644 ui/components/ui/slider/slider.stories.js

diff --git a/ui/components/ui/slider/index.js b/ui/components/ui/slider/index.js
new file mode 100644
index 000000000..52e836261
--- /dev/null
+++ b/ui/components/ui/slider/index.js
@@ -0,0 +1,3 @@
+import Slider from './slider.component';
+
+export default Slider;
diff --git a/ui/components/ui/slider/index.scss b/ui/components/ui/slider/index.scss
new file mode 100644
index 000000000..00ff33766
--- /dev/null
+++ b/ui/components/ui/slider/index.scss
@@ -0,0 +1,48 @@
+.slider {
+  display: inline-block;
+  width: 100%;
+
+  &__heading,
+  &__footer {
+    display: flex;
+    justify-content: space-between;
+  }
+
+  &__heading-title {
+    display: flex;
+    align-items: center;
+
+    > p {
+      margin-left: 10px;
+      font-size: 14px;
+    }
+  }
+
+  &__heading-detail > p {
+    font-size: 14px;
+  }
+
+  &__footer-info {
+    display: flex;
+    align-items: center;
+
+    > p {
+      font-size: 12px;
+    }
+  }
+
+  &__footer-edit > button {
+    border: none;
+    background: none;
+    font-size: 12px;
+    color: $Blue-500;
+
+    &:focus {
+      outline: none;
+    }
+  }
+
+  h6 {
+    margin-inline-end: 6px;
+  }
+}
diff --git a/ui/components/ui/slider/slider.component.js b/ui/components/ui/slider/slider.component.js
new file mode 100644
index 000000000..9bdb7309b
--- /dev/null
+++ b/ui/components/ui/slider/slider.component.js
@@ -0,0 +1,135 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import MaterialSlider from '@material-ui/core/Slider';
+import { withStyles } from '@material-ui/core/styles';
+
+import {
+  COLORS,
+  FONT_WEIGHT,
+  TYPOGRAPHY,
+} from '../../../helpers/constants/design-system';
+
+import InfoTooltip from '../info-tooltip/info-tooltip';
+import Typography from '../typography/typography';
+
+const styles = {
+  root: {
+    height: 6,
+    padding: '6px 0',
+  },
+  rail: {
+    borderRadius: 50,
+    background: '#D6D9DC',
+    height: 6,
+  },
+  track: {
+    borderRadius: 50,
+    background: '#037DD6',
+    height: 6,
+  },
+  thumb: {
+    'height': 20,
+    'width': 20,
+    'marginTop': -7,
+    'marginLeft': -7,
+    'backgroundColor': '#037DD6',
+    'border': '1px solid #EAF6FF',
+    'boxSizing': 'border-box',
+    'boxShadow': '0px 0px 14px 0px rgba(0, 0, 0, 0.18)',
+    '&:focus, &$active': {
+      height: 20,
+      width: 20,
+      marginTop: -7,
+      marginLeft: -7,
+      boxShadow: '0px 0px 14px 0px rgba(0, 0, 0, 0.18)',
+    },
+    '&:hover': {
+      height: 22,
+      width: 22,
+      marginTop: -8,
+      marginLeft: -8,
+      border: 'none',
+      boxShadow: '0px 0px 14px 0px rgba(0, 0, 0, 0.18)',
+    },
+  },
+};
+
+const Slider = ({
+  editText,
+  infoText,
+  onEdit,
+  titleDetail,
+  titleText,
+  tooltipText,
+  valueText,
+  ...rest
+}) => (
+  <div className="slider">
+    <div className="slider__heading">
+      <div className="slider__heading-title">
+        {titleText && (
+          <Typography
+            tag={TYPOGRAPHY.H6}
+            fontWeight={FONT_WEIGHT.BOLD}
+            variant={TYPOGRAPHY.H6}
+          >
+            {titleText}
+          </Typography>
+        )}
+        {tooltipText && (
+          <InfoTooltip position="top" contentText={tooltipText} />
+        )}
+        {valueText && (
+          <Typography tag={TYPOGRAPHY.Paragraph} color={COLORS.UI4}>
+            {valueText}
+          </Typography>
+        )}
+      </div>
+      {titleDetail && (
+        <div className="slider__heading-detail">
+          <Typography tag={TYPOGRAPHY.Paragraph} color={COLORS.UI4}>
+            {titleDetail}
+          </Typography>
+        </div>
+      )}
+    </div>
+    <MaterialSlider {...rest} />
+    <div className="slider__footer">
+      <div className="slider__footer-info">
+        {infoText && (
+          <Typography tag={TYPOGRAPHY.Paragraph} color={COLORS.UI4}>
+            {infoText}
+          </Typography>
+        )}
+      </div>
+      <div className="slider__footer-edit">
+        {onEdit && (
+          <button onClick={onEdit} aria-label="edit as numeric input">
+            {editText}
+          </button>
+        )}
+      </div>
+    </div>
+  </div>
+);
+
+Slider.defaultProps = {
+  editText: 'Edit',
+};
+
+Slider.propTypes = {
+  editText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
+  infoText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
+  titleDetail: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
+  titleText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
+  tooltipText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
+  valueText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
+  max: PropTypes.number,
+  min: PropTypes.number,
+  onChange: PropTypes.func,
+  onEdit: PropTypes.func,
+  step: PropTypes.number,
+  value: PropTypes.number,
+};
+
+export default withStyles(styles)(Slider);
diff --git a/ui/components/ui/slider/slider.component.test.js b/ui/components/ui/slider/slider.component.test.js
new file mode 100644
index 000000000..d28e2adca
--- /dev/null
+++ b/ui/components/ui/slider/slider.component.test.js
@@ -0,0 +1,58 @@
+import React from 'react';
+import { fireEvent, render } from '@testing-library/react';
+
+import Slider from './slider.component';
+
+describe('Slider Component', () => {
+  describe('rendering', () => {
+    it('should render properly', () => {
+      expect(() => {
+        render(<Slider />);
+      }).not.toThrow();
+    });
+
+    it('should contain passed header props', () => {
+      const wrapper = render(
+        <Slider
+          titleText="Slider Title Text"
+          tooltipText="Slider Tooltip Text"
+          valueText="$ 00.00"
+          titleDetail="100 GWEI"
+        />,
+      );
+
+      expect(wrapper.getAllByText('Slider Title Text')).toBeDefined();
+      expect(wrapper.getAllByText('$ 00.00')).toBeDefined();
+      expect(wrapper.getAllByText('100 GWEI')).toBeDefined();
+    });
+
+    it('should contain passed footer props', () => {
+      const wrapper = render(
+        <Slider
+          infoText="Footer Info Text"
+          editText="Edit GWEI"
+          onEdit={() => {
+            console.log('on edit click');
+          }}
+        />,
+      );
+
+      expect(wrapper.getAllByText('Footer Info Text')).toBeDefined();
+      expect(
+        wrapper.getByRole('button', { name: 'edit as numeric input' }),
+      ).toBeDefined();
+      expect(wrapper.getAllByText('Edit GWEI')).toBeDefined();
+    });
+
+    it('should call onEdit callback when edit button is clicked', () => {
+      const mockEditFn = jest.fn();
+      const wrapper = render(
+        <Slider infoText="Footer Info Text" onEdit={mockEditFn} />,
+      );
+
+      const editButton = wrapper.getByRole('button');
+      fireEvent.click(editButton);
+      expect(mockEditFn).toHaveBeenCalledTimes(1);
+    });
+  });
+});
diff --git a/ui/components/ui/slider/slider.stories.js b/ui/components/ui/slider/slider.stories.js
new file mode 100644
index 000000000..316ae3929
--- /dev/null
+++ b/ui/components/ui/slider/slider.stories.js
@@ -0,0 +1,33 @@
+import React from 'react';
+import Slider from '.';
+
+export default {
+  title: 'Slider',
+  id: __filename,
+};
+
+export const slider = () => <Slider />;
+
+export const sliderWithSteps = () => <Slider step={10} />;
+
+export const sliderWithHeader = () => (
+  <Slider
+    titleText="Slider Title Text"
+    tooltipText="Slider Tooltip Text"
+    valueText="$ 00.00"
+    titleDetail="100 GWEI"
+  />
+);
+
+export const sliderWithFooter = () => (
+  <Slider
+    titleText="Slider Title Text"
+    tooltipText="Slider Tooltip Text"
+    valueText="$ 00.00"
+    titleDetail="100 GWEI"
+    infoText="Footer Info Text"
+    onEdit={() => {
+      console.log('on edit click');
+    }}
+  />
+);
diff --git a/ui/components/ui/ui-components.scss b/ui/components/ui/ui-components.scss
index f585f9052..75cddcb90 100644
--- a/ui/components/ui/ui-components.scss
+++ b/ui/components/ui/ui-components.scss
@@ -45,6 +45,7 @@
 @import 'readonly-input/index';
 @import 'sender-to-recipient/index';
 @import 'snackbar/index';
+@import 'slider/index';
 @import 'tabs/index';
 @import 'toggle-button/index';
 @import 'token-balance/index';
diff --git a/ui/helpers/constants/design-system.js b/ui/helpers/constants/design-system.js
index 9716b5567..7edcbc0b2 100644
--- a/ui/helpers/constants/design-system.js
+++ b/ui/helpers/constants/design-system.js
@@ -45,7 +45,7 @@ export const TYPOGRAPHY = {
   H7: 'h7',
   H8: 'h8',
   H9: 'h9',
-  Paragraph: 'paragraph',
+  Paragraph: 'p',
 };
 
 const NONE = 'none';

From 92b075581ce344d2b4bd37921b206d0e8ab85baf Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 28 Sep 2021 13:38:31 -0230
Subject: [PATCH 16/56] Bump @metamask/contract-metadata from 1.29.0 to 1.30.0
 (#12207)

Bumps [@metamask/contract-metadata](https://github.com/MetaMask/contract-metadata) from 1.29.0 to 1.30.0.
- [Release notes](https://github.com/MetaMask/contract-metadata/releases)
- [Commits](https://github.com/MetaMask/contract-metadata/compare/v1.29.0...v1.30.0)

---
updated-dependencies:
- dependency-name: "@metamask/contract-metadata"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 yarn.lock | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/yarn.lock b/yarn.lock
index ed807e29c..6aaf4069e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2731,9 +2731,9 @@
     yargs "^17.0.1"
 
 "@metamask/contract-metadata@^1.19.0", "@metamask/contract-metadata@^1.28.0", "@metamask/contract-metadata@^1.29.0":
-  version "1.29.0"
-  resolved "https://registry.yarnpkg.com/@metamask/contract-metadata/-/contract-metadata-1.29.0.tgz#4ca86a2f03d4dad4350d09216a7fe92f9dd21c8e"
-  integrity sha512-wxsC0ZCyhPKqThvmsL8+2zVWGWPqofSo8HNtOuOnQM9oGbXX9294imJ3T+A/Lov8fkX4jAWZOeNV0uR80zkNtA==
+  version "1.30.0"
+  resolved "https://registry.yarnpkg.com/@metamask/contract-metadata/-/contract-metadata-1.30.0.tgz#fa8e1b0c3e7aaa963986088f691fb553ffbe3904"
+  integrity sha512-b2usYW/ptQYnE6zhUmr4T+nvOAQJK5ABcpKudyQANpy4K099elpv4aN0WcrcOcwV99NHOdMzFP3ZuG0HoAyOBQ==
 
 "@metamask/controllers@^16.0.0":
   version "16.0.0"

From 3f577700c6339c5d4fe55d21ade11e0a42e40be3 Mon Sep 17 00:00:00 2001
From: Mark Stacey <markjstacey@gmail.com>
Date: Tue, 28 Sep 2021 13:43:26 -0230
Subject: [PATCH 17/56] Replace `isBeta` with `buildType` (#12231)

This is a refactor to replace the `isBeta` boolean with `buildType`
throughout the build system. This will allow us to modify the behaviour
of each step of the build process for Flask as well.

This should result in no functional changes.
---
 development/build/etc.js      |  8 ++++++--
 development/build/index.js    | 10 ++++------
 development/build/manifest.js |  5 +++--
 development/build/static.js   |  6 ++++--
 4 files changed, 17 insertions(+), 12 deletions(-)

diff --git a/development/build/etc.js b/development/build/etc.js
index c30bbe9b8..8d2b75956 100644
--- a/development/build/etc.js
+++ b/development/build/etc.js
@@ -7,13 +7,14 @@ const pump = pify(require('pump'));
 const { version } = require('../../package.json');
 
 const { createTask, composeParallel } = require('./task');
+const { BuildTypes } = require('./utils');
 
 module.exports = createEtcTasks;
 
 function createEtcTasks({
   betaVersionsMap,
   browserPlatforms,
-  isBeta,
+  buildType,
   livereload,
 }) {
   const clean = createTask('clean', async function clean() {
@@ -34,7 +35,10 @@ function createEtcTasks({
     'zip',
     composeParallel(
       ...browserPlatforms.map((platform) =>
-        createZipTask(platform, isBeta ? betaVersionsMap[platform] : undefined),
+        createZipTask(
+          platform,
+          buildType === BuildTypes.beta ? betaVersionsMap[platform] : undefined,
+        ),
       ),
     ),
   );
diff --git a/development/build/index.js b/development/build/index.js
index 01c9b50da..d3e2ecc8d 100755
--- a/development/build/index.js
+++ b/development/build/index.js
@@ -37,7 +37,6 @@ function defineAndRunBuildTasks() {
     betaVersion,
     buildType,
     entryTask,
-    isBeta,
     isLavaMoat,
     shouldIncludeLockdown,
     shouldLintFenceFiles,
@@ -47,7 +46,7 @@ function defineAndRunBuildTasks() {
   const browserPlatforms = ['firefox', 'chrome', 'brave', 'opera'];
 
   let betaVersionsMap;
-  if (isBeta) {
+  if (buildType === BuildTypes.beta) {
     betaVersionsMap = getNextBetaVersionMap(
       version,
       betaVersion,
@@ -59,13 +58,13 @@ function defineAndRunBuildTasks() {
     livereload,
     browserPlatforms,
     shouldIncludeLockdown,
-    isBeta,
+    buildType,
   });
 
   const manifestTasks = createManifestTasks({
     browserPlatforms,
     betaVersionsMap,
-    isBeta,
+    buildType,
   });
 
   const styleTasks = createStyleTasks({ livereload });
@@ -82,7 +81,7 @@ function defineAndRunBuildTasks() {
     livereload,
     browserPlatforms,
     betaVersionsMap,
-    isBeta,
+    buildType,
   });
 
   // build for development (livereload)
@@ -201,7 +200,6 @@ function parseArgv() {
     betaVersion: String(betaVersion),
     buildType,
     entryTask,
-    isBeta: argv[NamedArgs.BuildType] === BuildTypes.beta,
     isLavaMoat: process.argv[0].includes('lavamoat'),
     shouldIncludeLockdown: argv[NamedArgs.OmitLockdown],
     shouldLintFenceFiles,
diff --git a/development/build/manifest.js b/development/build/manifest.js
index 3e80ea5b5..6f8b9e8fe 100644
--- a/development/build/manifest.js
+++ b/development/build/manifest.js
@@ -7,10 +7,11 @@ const { version } = require('../../package.json');
 const betaManifestModifications = require('../../app/manifest/_beta_modifications.json');
 
 const { createTask, composeSeries } = require('./task');
+const { BuildTypes } = require('./utils');
 
 module.exports = createManifestTasks;
 
-function createManifestTasks({ betaVersionsMap, browserPlatforms, isBeta }) {
+function createManifestTasks({ betaVersionsMap, browserPlatforms, buildType }) {
   // merge base manifest with per-platform manifests
   const prepPlatforms = async () => {
     return Promise.all(
@@ -28,7 +29,7 @@ function createManifestTasks({ betaVersionsMap, browserPlatforms, isBeta }) {
         const result = merge(
           cloneDeep(baseManifest),
           platformModifications,
-          isBeta
+          buildType === BuildTypes.beta
             ? getBetaModifications(platform, betaVersionsMap)
             : { version },
         );
diff --git a/development/build/static.js b/development/build/static.js
index 341b7d56b..c92ca709a 100644
--- a/development/build/static.js
+++ b/development/build/static.js
@@ -6,6 +6,7 @@ const glob = require('fast-glob');
 const locales = require('../../app/_locales/index.json');
 
 const { createTask, composeSeries } = require('./task');
+const { BuildTypes } = require('./utils');
 
 const EMPTY_JS_FILE = './development/empty.js';
 
@@ -13,7 +14,7 @@ module.exports = function createStaticAssetTasks({
   livereload,
   browserPlatforms,
   shouldIncludeLockdown = true,
-  isBeta,
+  buildType,
 }) {
   const [copyTargetsProd, copyTargetsDev] = getCopyTargets(
     shouldIncludeLockdown,
@@ -27,7 +28,8 @@ module.exports = function createStaticAssetTasks({
     },
   ];
 
-  const targets = isBeta ? copyTargetsBeta : copyTargetsProd;
+  const targets =
+    buildType === BuildTypes.beta ? copyTargetsBeta : copyTargetsProd;
 
   const prod = createTask(
     'static:prod',

From f52dac290b680e7dfd459113ff095706f895ddf9 Mon Sep 17 00:00:00 2001
From: ryanml <ryanlanese@gmail.com>
Date: Tue, 28 Sep 2021 12:21:19 -0700
Subject: [PATCH 18/56] Using parsed seed phrase for restore vault input
 validation (#12229)

Fixes MetaMask/metamask-extension#12225
---
 ui/pages/keychains/restore-vault.js | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/ui/pages/keychains/restore-vault.js b/ui/pages/keychains/restore-vault.js
index 625b9c71c..a8ba320ca 100644
--- a/ui/pages/keychains/restore-vault.js
+++ b/ui/pages/keychains/restore-vault.js
@@ -44,13 +44,14 @@ class RestoreVaultPage extends Component {
     const { t } = this.context;
     let seedPhraseError = null;
 
-    const wordCount = this.parseSeedPhrase(seedPhrase).split(/\s/u).length;
+    const parseSeedPhrase = this.parseSeedPhrase(seedPhrase);
+    const wordCount = parseSeedPhrase.split(/\s/u).length;
     if (
-      seedPhrase &&
+      parseSeedPhrase &&
       (wordCount % 3 !== 0 || wordCount < 12 || wordCount > 24)
     ) {
       seedPhraseError = t('seedPhraseReq');
-    } else if (!isValidMnemonic(seedPhrase)) {
+    } else if (!isValidMnemonic(parseSeedPhrase)) {
       seedPhraseError = t('invalidSeedPhrase');
     }
 

From d929ccdfa86104445b25926b941a9f7793b18b0a Mon Sep 17 00:00:00 2001
From: kumavis <kumavis@users.noreply.github.com>
Date: Tue, 28 Sep 2021 09:32:05 -1000
Subject: [PATCH 19/56] Update helpers.js (#12236)

---
 test/e2e/helpers.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test/e2e/helpers.js b/test/e2e/helpers.js
index dca25366a..5eb5ad8fb 100644
--- a/test/e2e/helpers.js
+++ b/test/e2e/helpers.js
@@ -130,7 +130,7 @@ async function withFixtures(options, testSuite) {
       if (webDriver) {
         await webDriver.quit();
       }
-      if (dappServer) {
+      if (dappServer && dappServer.listening) {
         await new Promise((resolve, reject) => {
           dappServer.close((error) => {
             if (error) {

From 4c38d12c5f02ba2d3a610884a768f1b9cde0b97b Mon Sep 17 00:00:00 2001
From: Mark Stacey <markjstacey@gmail.com>
Date: Tue, 28 Sep 2021 19:15:00 -0230
Subject: [PATCH 20/56] Fix assets for beta dev build (#12233)

The MetaMask logo used for beta development builds was wrong. The lock
screen (and any other place using the `@metamask/logo` logo) showed the
correct logo, but all of our static assets used the "regular" logo.

Now the beta logo should be used everywhere for beta development
builds.
---
 development/build/static.js | 23 +++++++++++++----------
 1 file changed, 13 insertions(+), 10 deletions(-)

diff --git a/development/build/static.js b/development/build/static.js
index c92ca709a..5e5425a58 100644
--- a/development/build/static.js
+++ b/development/build/static.js
@@ -20,21 +20,24 @@ module.exports = function createStaticAssetTasks({
     shouldIncludeLockdown,
   );
 
-  const copyTargetsBeta = [
-    ...copyTargetsProd,
-    {
-      src: './app/build-types/beta/',
-      dest: `images`,
-    },
-  ];
+  const additionalBuildTargets = {
+    [BuildTypes.beta]: [
+      {
+        src: './app/build-types/beta/',
+        dest: `images`,
+      },
+    ],
+  };
 
-  const targets =
-    buildType === BuildTypes.beta ? copyTargetsBeta : copyTargetsProd;
+  if (Object.keys(additionalBuildTargets).includes(buildType)) {
+    copyTargetsProd.push(...additionalBuildTargets[buildType]);
+    copyTargetsDev.push(...additionalBuildTargets[buildType]);
+  }
 
   const prod = createTask(
     'static:prod',
     composeSeries(
-      ...targets.map((target) => {
+      ...copyTargetsProd.map((target) => {
         return async function copyStaticAssets() {
           await performCopy(target);
         };

From 20eb35be4ee4c3665e3d9c5d722eb0a109f7d903 Mon Sep 17 00:00:00 2001
From: kumavis <kumavis@users.noreply.github.com>
Date: Tue, 28 Sep 2021 12:40:07 -1000
Subject: [PATCH 21/56] test/e2e/driver/switchToWindowWithTitle - refresh
 window handles on each retry loop (#12237)

---
 test/e2e/webdriver/driver.js | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/test/e2e/webdriver/driver.js b/test/e2e/webdriver/driver.js
index d7a742399..ad8430794 100644
--- a/test/e2e/webdriver/driver.js
+++ b/test/e2e/webdriver/driver.js
@@ -277,13 +277,13 @@ class Driver {
 
   async switchToWindowWithTitle(
     title,
-    windowHandles,
+    initialWindowHandles,
     delayStep = 1000,
     timeout = 5000,
   ) {
+    let windowHandles =
+      initialWindowHandles || (await this.driver.getAllWindowHandles());
     let timeElapsed = 0;
-    // eslint-disable-next-line no-param-reassign
-    windowHandles = windowHandles || (await this.driver.getAllWindowHandles());
     while (timeElapsed <= timeout) {
       for (const handle of windowHandles) {
         await this.driver.switchTo().window(handle);
@@ -294,6 +294,8 @@ class Driver {
       }
       await this.delay(delayStep);
       timeElapsed += delayStep;
+      // refresh the window handles
+      windowHandles = await this.driver.getAllWindowHandles();
     }
 
     throw new Error(`No window with title: ${title}`);

From d9d20160d6ef382768f417392f74879e125b71d0 Mon Sep 17 00:00:00 2001
From: kumavis <kumavis@users.noreply.github.com>
Date: Tue, 28 Sep 2021 20:56:08 -1000
Subject: [PATCH 22/56] LavaMoat Node update and various small enhancements
 (#12239)

* lavamoat - update lavamoat-node and relevant policy + two handy patches

* test/e2e - add timeout known to be flaky

* lavamoat-viz - rename npm script
---
 .circleci/scripts/create-lavamoat-viz.sh     |   2 +-
 lavamoat/node/policy.json                    | 201 -------------------
 package.json                                 |   4 +-
 patches/abort-controller+3.0.0.patch         |  18 ++
 patches/regenerator-runtime+0.13.7.patch     |  11 +-
 test/e2e/tests/contract-interactions.spec.js |   3 +-
 yarn.lock                                    |  30 ++-
 7 files changed, 58 insertions(+), 211 deletions(-)
 create mode 100644 patches/abort-controller+3.0.0.patch

diff --git a/.circleci/scripts/create-lavamoat-viz.sh b/.circleci/scripts/create-lavamoat-viz.sh
index 53e520f27..db1dc3979 100755
--- a/.circleci/scripts/create-lavamoat-viz.sh
+++ b/.circleci/scripts/create-lavamoat-viz.sh
@@ -11,7 +11,7 @@ BUILD_DEST="./build-artifacts/build-viz/"
 mkdir -p "${BUILD_DEST}"
 
 # generate lavamoat debug config
-yarn lavamoat:debug
+yarn lavamoat:debug:build
 
 # generate viz
 npx lavamoat-viz --dest "${BUILD_DEST}"
\ No newline at end of file
diff --git a/lavamoat/node/policy.json b/lavamoat/node/policy.json
index 7497e459b..c3acbd59d 100644
--- a/lavamoat/node/policy.json
+++ b/lavamoat/node/policy.json
@@ -1008,16 +1008,6 @@
         "buffer-equal": true
       }
     },
-    "are-we-there-yet": {
-      "builtin": {
-        "events.EventEmitter": true,
-        "util.inherits": true
-      },
-      "packages": {
-        "delegates": true,
-        "readable-stream": true
-      }
-    },
     "arr-diff": {
       "packages": {
         "arr-flatten": true,
@@ -1377,7 +1367,6 @@
         "anymatch": true,
         "async-each": true,
         "braces": true,
-        "fsevents": true,
         "glob-parent": true,
         "inherits": true,
         "is-binary-path": true,
@@ -1629,16 +1618,6 @@
         "through2": true
       }
     },
-    "detect-libc": {
-      "builtin": {
-        "child_process.spawnSync": true,
-        "fs.readdirSync": true,
-        "os.platform": true
-      },
-      "globals": {
-        "process.env": true
-      }
-    },
     "detective": {
       "packages": {
         "acorn-node": true,
@@ -2175,45 +2154,6 @@
         "process.version": true
       }
     },
-    "fsevents": {
-      "builtin": {
-        "events.EventEmitter": true,
-        "fs.stat": true,
-        "path.join": true,
-        "util.inherits": true
-      },
-      "globals": {
-        "__dirname": true,
-        "process.nextTick": true,
-        "process.platform": true,
-        "setImmediate": true
-      },
-      "native": true,
-      "packages": {
-        "node-pre-gyp": true
-      }
-    },
-    "gauge": {
-      "builtin": {
-        "util.format": true
-      },
-      "globals": {
-        "clearInterval": true,
-        "process": true,
-        "setImmediate": true,
-        "setInterval": true
-      },
-      "packages": {
-        "aproba": true,
-        "console-control-strings": true,
-        "has-unicode": true,
-        "object-assign": true,
-        "signal-exit": true,
-        "string-width": true,
-        "strip-ansi": true,
-        "wide-align": true
-      }
-    },
     "get-assigned-identifiers": {
       "builtin": {
         "assert.equal": true
@@ -2575,16 +2515,6 @@
         "process.argv": true
       }
     },
-    "has-unicode": {
-      "builtin": {
-        "os.type": true
-      },
-      "globals": {
-        "process.env.LANG": true,
-        "process.env.LC_ALL": true,
-        "process.env.LC_CTYPE": true
-      }
-    },
     "has-value": {
       "packages": {
         "get-value": true,
@@ -2738,11 +2668,6 @@
         "is-plain-object": true
       }
     },
-    "is-fullwidth-code-point": {
-      "packages": {
-        "number-is-nan": true
-      }
-    },
     "is-glob": {
       "packages": {
         "is-extglob": true
@@ -3138,56 +3063,6 @@
         "setTimeout": true
       }
     },
-    "node-pre-gyp": {
-      "builtin": {
-        "events.EventEmitter": true,
-        "fs.existsSync": true,
-        "fs.readFileSync": true,
-        "fs.renameSync": true,
-        "path.dirname": true,
-        "path.existsSync": true,
-        "path.join": true,
-        "path.resolve": true,
-        "url.parse": true,
-        "url.resolve": true,
-        "util.inherits": true
-      },
-      "globals": {
-        "__dirname": true,
-        "console.log": true,
-        "process.arch": true,
-        "process.cwd": true,
-        "process.env": true,
-        "process.platform": true,
-        "process.version.substr": true,
-        "process.versions": true
-      },
-      "packages": {
-        "detect-libc": true,
-        "nopt": true,
-        "npmlog": true,
-        "rimraf": true,
-        "semver": true
-      }
-    },
-    "nopt": {
-      "builtin": {
-        "path": true,
-        "stream.Stream": true,
-        "url": true
-      },
-      "globals": {
-        "console": true,
-        "process.argv": true,
-        "process.env.DEBUG_NOPT": true,
-        "process.env.NOPT_DEBUG": true,
-        "process.platform": true
-      },
-      "packages": {
-        "abbrev": true,
-        "osenv": true
-      }
-    },
     "normalize-path": {
       "packages": {
         "remove-trailing-separator": true
@@ -3203,22 +3078,6 @@
         "once": true
       }
     },
-    "npmlog": {
-      "builtin": {
-        "events.EventEmitter": true,
-        "util": true
-      },
-      "globals": {
-        "process.nextTick": true,
-        "process.stderr": true
-      },
-      "packages": {
-        "are-we-there-yet": true,
-        "console-control-strings": true,
-        "gauge": true,
-        "set-blocking": true
-      }
-    },
     "object-copy": {
       "packages": {
         "copy-descriptor": true,
@@ -3285,54 +3144,6 @@
         "readable-stream": true
       }
     },
-    "os-homedir": {
-      "builtin": {
-        "os.homedir": true
-      },
-      "globals": {
-        "process.env": true,
-        "process.getuid": true,
-        "process.platform": true
-      }
-    },
-    "os-tmpdir": {
-      "globals": {
-        "process.env.SystemRoot": true,
-        "process.env.TEMP": true,
-        "process.env.TMP": true,
-        "process.env.TMPDIR": true,
-        "process.env.windir": true,
-        "process.platform": true
-      }
-    },
-    "osenv": {
-      "builtin": {
-        "child_process.exec": true,
-        "path": true
-      },
-      "globals": {
-        "process.env.COMPUTERNAME": true,
-        "process.env.ComSpec": true,
-        "process.env.EDITOR": true,
-        "process.env.HOSTNAME": true,
-        "process.env.PATH": true,
-        "process.env.PROMPT": true,
-        "process.env.PS1": true,
-        "process.env.Path": true,
-        "process.env.SHELL": true,
-        "process.env.USER": true,
-        "process.env.USERDOMAIN": true,
-        "process.env.USERNAME": true,
-        "process.env.VISUAL": true,
-        "process.env.path": true,
-        "process.nextTick": true,
-        "process.platform": true
-      },
-      "packages": {
-        "os-homedir": true,
-        "os-tmpdir": true
-      }
-    },
     "parent-module": {
       "packages": {
         "callsites": true
@@ -3915,12 +3726,6 @@
         "process": true
       }
     },
-    "set-blocking": {
-      "globals": {
-        "process.stderr": true,
-        "process.stdout": true
-      }
-    },
     "set-value": {
       "packages": {
         "extend-shallow": true,
@@ -4122,7 +3927,6 @@
     },
     "string-width": {
       "packages": {
-        "code-point-at": true,
         "emoji-regex": true,
         "is-fullwidth-code-point": true,
         "strip-ansi": true
@@ -4699,11 +4503,6 @@
         "isexe": true
       }
     },
-    "wide-align": {
-      "packages": {
-        "string-width": true
-      }
-    },
     "write": {
       "builtin": {
         "fs.createWriteStream": true,
diff --git a/package.json b/package.json
index 3096ec6fd..bfb493184 100644
--- a/package.json
+++ b/package.json
@@ -65,7 +65,7 @@
     "update-changelog": "auto-changelog update",
     "generate:migration": "./development/generate-migration.sh",
     "lavamoat:auto": "lavamoat ./development/build/index.js --writeAutoPolicy",
-    "lavamoat:debug": "lavamoat ./development/build/index.js --writeAutoPolicyDebug"
+    "lavamoat:debug:build": "lavamoat ./development/build/index.js --writeAutoPolicyDebug"
   },
   "resolutions": {
     "**/regenerator-runtime": "^0.13.7",
@@ -287,7 +287,7 @@
     "jest": "^26.6.3",
     "jsdom": "^11.2.0",
     "koa": "^2.7.0",
-    "lavamoat": "^5.3.1",
+    "lavamoat": "^5.3.4",
     "lavamoat-viz": "^6.0.9",
     "lockfile-lint": "^4.0.0",
     "loose-envify": "^1.4.0",
diff --git a/patches/abort-controller+3.0.0.patch b/patches/abort-controller+3.0.0.patch
new file mode 100644
index 000000000..2210446d2
--- /dev/null
+++ b/patches/abort-controller+3.0.0.patch
@@ -0,0 +1,18 @@
+diff --git a/node_modules/abort-controller/browser.js b/node_modules/abort-controller/browser.js
+index b0c5ec3..c8c8018 100644
+--- a/node_modules/abort-controller/browser.js
++++ b/node_modules/abort-controller/browser.js
+@@ -2,12 +2,7 @@
+ "use strict"
+ 
+ /*eslint-disable @mysticatea/prettier */
+-const { AbortController, AbortSignal } =
+-    typeof self !== "undefined" ? self :
+-    typeof window !== "undefined" ? window :
+-    /* otherwise */ undefined
++const { AbortController } = globalThis;
+ /*eslint-enable @mysticatea/prettier */
+ 
+ module.exports = AbortController
+-module.exports.AbortSignal = AbortSignal
+-module.exports.default = AbortController
diff --git a/patches/regenerator-runtime+0.13.7.patch b/patches/regenerator-runtime+0.13.7.patch
index 1710d779a..778bc62ca 100644
--- a/patches/regenerator-runtime+0.13.7.patch
+++ b/patches/regenerator-runtime+0.13.7.patch
@@ -1,5 +1,5 @@
 diff --git a/node_modules/regenerator-runtime/runtime.js b/node_modules/regenerator-runtime/runtime.js
-index 547b8c6..c53a471 100644
+index 547b8c6..885626e 100644
 --- a/node_modules/regenerator-runtime/runtime.js
 +++ b/node_modules/regenerator-runtime/runtime.js
 @@ -5,7 +5,7 @@
@@ -65,3 +65,12 @@ index 547b8c6..c53a471 100644
  
    function pushTryEntry(locs) {
      var entry = { tryLoc: locs[0] };
+@@ -733,7 +734,7 @@ var runtime = (function (exports) {
+ ));
+ 
+ try {
+-  regeneratorRuntime = runtime;
++  globalThis.regeneratorRuntime = runtime;
+ } catch (accidentalStrictMode) {
+   // This module should not be running in strict mode, so the above
+   // assignment should always work unless something is misconfigured. Just
diff --git a/test/e2e/tests/contract-interactions.spec.js b/test/e2e/tests/contract-interactions.spec.js
index e94c18f0a..c4e0537c5 100644
--- a/test/e2e/tests/contract-interactions.spec.js
+++ b/test/e2e/tests/contract-interactions.spec.js
@@ -1,5 +1,5 @@
 const { strict: assert } = require('assert');
-const { withFixtures } = require('../helpers');
+const { withFixtures, regularDelayMs } = require('../helpers');
 
 describe('Deploy contract and call contract methods', function () {
   let windowHandles;
@@ -91,6 +91,7 @@ describe('Deploy contract and call contract methods', function () {
           'MetaMask Notification',
           windowHandles,
         );
+        await driver.delay(regularDelayMs);
         await driver.clickElement({ text: 'Confirm', tag: 'button' });
         await driver.waitUntilXWindowHandles(2);
         await driver.switchToWindow(extension);
diff --git a/yarn.lock b/yarn.lock
index 6aaf4069e..3828c4441 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -17418,6 +17418,17 @@ lavamoat-core@^10.0.1:
     merge-deep "^3.0.2"
     resolve "^1.15.1"
 
+lavamoat-core@^11.0.0:
+  version "11.0.0"
+  resolved "https://registry.yarnpkg.com/lavamoat-core/-/lavamoat-core-11.0.0.tgz#d1decbabae0429149fb7bbeb7f8a74fa054dbdf9"
+  integrity sha512-D/ULw8cbQ+0Rr/2IT38XedH8CWZsfIy0WThJHLjPbxwdPuHasFw9GHs88vjYv7a6UJ1uac8x5mhyOlbGoOJcIw==
+  dependencies:
+    fromentries "^1.2.0"
+    json-stable-stringify "^1.0.1"
+    lavamoat-tofu "^6.0.0"
+    merge-deep "^3.0.2"
+    resolve "^1.15.1"
+
 lavamoat-tofu@^5.1.3:
   version "5.1.3"
   resolved "https://registry.yarnpkg.com/lavamoat-tofu/-/lavamoat-tofu-5.1.3.tgz#8e89a7ac206f3b2dc9c8ea52ecfd4f92f90285ae"
@@ -17426,6 +17437,14 @@ lavamoat-tofu@^5.1.3:
     "@babel/parser" "^7.10.1"
     "@babel/traverse" "^7.10.1"
 
+lavamoat-tofu@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/lavamoat-tofu/-/lavamoat-tofu-6.0.0.tgz#26d37c14864eb98df247195c5419a3a762ef650b"
+  integrity sha512-tgXf+/lplow/Ao2cOsbQ4ZqTuKBLwNnSSjPFIiGAHAM6xs+c+jEr60UAqOflyBg94VOE47G6o8zYZOAylZpUrw==
+  dependencies:
+    "@babel/parser" "^7.10.1"
+    "@babel/traverse" "^7.10.1"
+
 lavamoat-viz@^6.0.9:
   version "6.0.9"
   resolved "https://registry.yarnpkg.com/lavamoat-viz/-/lavamoat-viz-6.0.9.tgz#ddec64becdf62448f7d0ea57c5258246ee14304c"
@@ -17438,16 +17457,17 @@ lavamoat-viz@^6.0.9:
     serve-handler "^6.1.3"
     yargs "^16.0.0"
 
-lavamoat@^5.3.1:
-  version "5.3.1"
-  resolved "https://registry.yarnpkg.com/lavamoat/-/lavamoat-5.3.1.tgz#66f140210f406c69ec58f18ebcd827b52e3a3276"
-  integrity sha512-XXMDX7dV2lKEymlC3+rAAWp7/j1Qe+j2lgyRMvg1xI2Vux24iW837grHDIyx1qs7LOhu5F98Z772z7jmGjBjfg==
+lavamoat@^5.3.4:
+  version "5.3.4"
+  resolved "https://registry.yarnpkg.com/lavamoat/-/lavamoat-5.3.4.tgz#cf0778a5fc8b2bc14bce79d46dc730df3d8c21ad"
+  integrity sha512-0p7B8+6Hs/WGlkBaVQl6RGdAlYd0pL6eFTh6/Oc3kdyi4wVc2Fz5vFOJqrZi8XkvjMuCLln7udYbiCapr74yZQ==
   dependencies:
     "@babel/code-frame" "^7.10.4"
+    "@babel/highlight" "^7.10.4"
     bindings "^1.5.0"
     htmlescape "^1.1.1"
     json-stable-stringify "^1.0.1"
-    lavamoat-core "^10.0.1"
+    lavamoat-core "^11.0.0"
     lavamoat-tofu "^5.1.3"
     node-gyp-build "^4.2.3"
     object.fromentries "^2.0.2"

From 53f2c84209a66680ff04dfeea63f2877ddf2774b Mon Sep 17 00:00:00 2001
From: Matthew Epps <mepps32@gmail.com>
Date: Wed, 29 Sep 2021 08:11:19 -0500
Subject: [PATCH 23/56] Add client id to GasFeeController (#12221)

* chore: Add client id to GasFeeController

* chore: change EXTENSION_CLIENT_ID to SWAPS_CLIENT_ID in constants file
---
 app/scripts/metamask-controller.js | 2 ++
 package.json                       | 2 +-
 shared/constants/swaps.js          | 2 ++
 ui/pages/swaps/swaps.util.js       | 3 ++-
 yarn.lock                          | 8 ++++----
 5 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index ae05d10d8..3608732fc 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -32,6 +32,7 @@ import { TRANSACTION_STATUSES } from '../../shared/constants/transaction';
 import {
   GAS_API_BASE_URL,
   GAS_DEV_API_BASE_URL,
+  SWAPS_CLIENT_ID,
 } from '../../shared/constants/swaps';
 import { MAINNET_CHAIN_ID } from '../../shared/constants/network';
 import { KEYRING_TYPES } from '../../shared/constants/hardware-wallets';
@@ -201,6 +202,7 @@ export default class MetamaskController extends EventEmitter {
     this.gasFeeController = new GasFeeController({
       interval: 10000,
       messenger: gasFeeMessenger,
+      clientId: SWAPS_CLIENT_ID,
       getProvider: () =>
         this.networkController.getProviderAndBlockTracker().provider,
       onNetworkStateChange: this.networkController.on.bind(
diff --git a/package.json b/package.json
index bfb493184..bcb036048 100644
--- a/package.json
+++ b/package.json
@@ -103,7 +103,7 @@
     "@fortawesome/fontawesome-free": "^5.13.0",
     "@material-ui/core": "^4.11.0",
     "@metamask/contract-metadata": "^1.28.0",
-    "@metamask/controllers": "^16.0.0",
+    "@metamask/controllers": "^17.0.0",
     "@metamask/eth-ledger-bridge-keyring": "^0.7.0",
     "@metamask/eth-token-tracker": "^3.0.1",
     "@metamask/etherscan-link": "^2.1.0",
diff --git a/shared/constants/swaps.js b/shared/constants/swaps.js
index 5da0d08d8..7f9262d1e 100644
--- a/shared/constants/swaps.js
+++ b/shared/constants/swaps.js
@@ -177,3 +177,5 @@ export const ETHEREUM = 'ethereum';
 export const POLYGON = 'polygon';
 export const BSC = 'bsc';
 export const RINKEBY = 'rinkeby';
+
+export const SWAPS_CLIENT_ID = 'extension';
diff --git a/ui/pages/swaps/swaps.util.js b/ui/pages/swaps/swaps.util.js
index aee102070..74203b411 100644
--- a/ui/pages/swaps/swaps.util.js
+++ b/ui/pages/swaps/swaps.util.js
@@ -14,6 +14,7 @@ import {
   SWAPS_DEV_API_V2_BASE_URL,
   GAS_API_BASE_URL,
   GAS_DEV_API_BASE_URL,
+  SWAPS_CLIENT_ID,
 } from '../../../shared/constants/swaps';
 import { TRANSACTION_ENVELOPE_TYPES } from '../../../shared/constants/transaction';
 import {
@@ -53,7 +54,7 @@ const TOKEN_TRANSFER_LOG_TOPIC_HASH =
 
 const CACHE_REFRESH_FIVE_MINUTES = 300000;
 
-const clientIdHeader = { 'X-Client-Id': 'extension' };
+const clientIdHeader = { 'X-Client-Id': SWAPS_CLIENT_ID };
 
 /**
  * @param {string} type Type of an API call, e.g. "tokens"
diff --git a/yarn.lock b/yarn.lock
index 3828c4441..9133ce6ce 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2735,10 +2735,10 @@
   resolved "https://registry.yarnpkg.com/@metamask/contract-metadata/-/contract-metadata-1.30.0.tgz#fa8e1b0c3e7aaa963986088f691fb553ffbe3904"
   integrity sha512-b2usYW/ptQYnE6zhUmr4T+nvOAQJK5ABcpKudyQANpy4K099elpv4aN0WcrcOcwV99NHOdMzFP3ZuG0HoAyOBQ==
 
-"@metamask/controllers@^16.0.0":
-  version "16.0.0"
-  resolved "https://registry.yarnpkg.com/@metamask/controllers/-/controllers-16.0.0.tgz#2c13550a5c7d47a0061a3f3de25e6becdc8531ad"
-  integrity sha512-zqByf/KXlSK+WdCh5AvFIbmiqpdMGfXxZAVHruKRqxsW9QFOXaIHpshiBZlSH+QLCJuli0sjyheRMFufeTuqkQ==
+"@metamask/controllers@^17.0.0":
+  version "17.0.0"
+  resolved "https://registry.yarnpkg.com/@metamask/controllers/-/controllers-17.0.0.tgz#7ef00b4f7583d8075115e8a2f074d7b66646bbe8"
+  integrity sha512-myPlAk8SpNm5SwHHKGgm2XDLP4bxNR2UsKoQlYtV7bJq3l8FV1agSFwHBwDhg61/52Xvqdqy+1YDVdV3kOwPgg==
   dependencies:
     "@ethereumjs/common" "^2.3.1"
     "@ethereumjs/tx" "^3.2.1"

From acce73c9437855886ca9ff16e61ef7a7cff1a844 Mon Sep 17 00:00:00 2001
From: Daniel <80175477+dan437@users.noreply.github.com>
Date: Wed, 29 Sep 2021 18:13:34 +0200
Subject: [PATCH 24/56] Fix form prefilling in Swaps on the Build Quote page
 (#12244)

* Fix form prefilling in Swaps on the Build Quote page

* Fix UTs

* Clean up tokens when resetting swaps state, in case a user is changing a network
---
 app/scripts/controllers/swaps.js      | 25 ++++++++++++++++++++++++-
 app/scripts/controllers/swaps.test.js |  1 +
 2 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/app/scripts/controllers/swaps.js b/app/scripts/controllers/swaps.js
index 150b4fece..5f3ec9851 100644
--- a/app/scripts/controllers/swaps.js
+++ b/app/scripts/controllers/swaps.js
@@ -79,6 +79,7 @@ const initialState = {
     routeState: '',
     swapsFeatureIsLive: true,
     useNewSwapsApi: false,
+    isFetchingQuotes: false,
     swapsQuoteRefreshTime: FALLBACK_QUOTE_REFRESH_TIME,
     swapsQuotePrefetchingRefreshTime: FALLBACK_QUOTE_REFRESH_TIME,
   },
@@ -229,6 +230,8 @@ export default class SwapsController {
     const indexOfCurrentCall = this.indexOfNewestCallInFlight + 1;
     this.indexOfNewestCallInFlight = indexOfCurrentCall;
 
+    this.setIsFetchingQuotes(true);
+
     let [newQuotes] = await Promise.all([
       this._fetchTradesInfo(fetchParams, {
         ...fetchParamsMetaData,
@@ -237,6 +240,20 @@ export default class SwapsController {
       this._setSwapsRefreshRates(),
     ]);
 
+    const {
+      swapsState: { isFetchingQuotes },
+    } = this.store.getState();
+
+    // If isFetchingQuotes is false, it means a user left Swaps (we cleaned the state)
+    // and we don't want to set any API response with quotes into state.
+    if (!isFetchingQuotes) {
+      return [
+        {}, // quotes
+        null, // selectedAggId
+      ];
+    }
+    this.setIsFetchingQuotes(false);
+
     newQuotes = mapValues(newQuotes, (quote) => ({
       ...quote,
       sourceTokenInfo: fetchParamsMetaData.sourceTokenInfo,
@@ -540,6 +557,13 @@ export default class SwapsController {
     this.store.updateState({ swapsState: { ...swapsState, routeState } });
   }
 
+  setIsFetchingQuotes(status) {
+    const { swapsState } = this.store.getState();
+    this.store.updateState({
+      swapsState: { ...swapsState, isFetchingQuotes: status },
+    });
+  }
+
   setSwapsLiveness(swapsLiveness) {
     const { swapsState } = this.store.getState();
     const { swapsFeatureIsLive, useNewSwapsApi } = swapsLiveness;
@@ -570,7 +594,6 @@ export default class SwapsController {
     this.store.updateState({
       swapsState: {
         ...initialState.swapsState,
-        tokens: swapsState.tokens,
         swapsQuoteRefreshTime: swapsState.swapsQuoteRefreshTime,
         swapsQuotePrefetchingRefreshTime:
           swapsState.swapsQuotePrefetchingRefreshTime,
diff --git a/app/scripts/controllers/swaps.test.js b/app/scripts/controllers/swaps.test.js
index d6127c6db..978c705a8 100644
--- a/app/scripts/controllers/swaps.test.js
+++ b/app/scripts/controllers/swaps.test.js
@@ -135,6 +135,7 @@ const EMPTY_INIT_STATE = {
     swapsQuoteRefreshTime: 60000,
     swapsQuotePrefetchingRefreshTime: 60000,
     swapsUserFeeLevel: '',
+    isFetchingQuotes: false,
   },
 };
 

From b5103bf317ddfe41d823d184b69203f7de949e33 Mon Sep 17 00:00:00 2001
From: Etienne Dusseault <etienne.dusseault@gmail.com>
Date: Wed, 29 Sep 2021 15:42:38 -0300
Subject: [PATCH 25/56] Add select-hardware component to Storybook (#12227)

---
 .../select-hardware.stories.js                | 29 +++++++++++++++++++
 1 file changed, 29 insertions(+)
 create mode 100644 ui/pages/create-account/connect-hardware/select-hardware.stories.js

diff --git a/ui/pages/create-account/connect-hardware/select-hardware.stories.js b/ui/pages/create-account/connect-hardware/select-hardware.stories.js
new file mode 100644
index 000000000..2748c87ad
--- /dev/null
+++ b/ui/pages/create-account/connect-hardware/select-hardware.stories.js
@@ -0,0 +1,29 @@
+import React from 'react';
+import { action } from '@storybook/addon-actions';
+import SelectHardware from './select-hardware';
+
+export default {
+  title: 'Connect Hardware Wallet',
+  id: __filename,
+};
+
+export const SelectHardwareComponent = () => {
+  return (
+    <SelectHardware
+      browserSupported
+      connectToHardwareWallet={(selectedDevice) =>
+        action(`Continue connect to ${selectedDevice}`)()
+      }
+      useLedgerLive
+    />
+  );
+};
+export const BrowserNotSupported = () => {
+  return (
+    <SelectHardware
+      browserSupported={false}
+      connectToHardwareWallet={() => undefined}
+      useLedgerLive
+    />
+  );
+};

From c2bbbdd19ccddaae949f6226bb82b89abc7b10ee Mon Sep 17 00:00:00 2001
From: Etienne Dusseault <etienne.dusseault@gmail.com>
Date: Wed, 29 Sep 2021 15:42:49 -0300
Subject: [PATCH 26/56] Add account-list component to Storybook (#12228)

---
 .storybook/test-data.js                       | 15 ++++
 .../connect-hardware/account-list.stories.js  | 68 +++++++++++++++++++
 2 files changed, 83 insertions(+)
 create mode 100644 ui/pages/create-account/connect-hardware/account-list.stories.js

diff --git a/.storybook/test-data.js b/.storybook/test-data.js
index 376182e34..80608dadc 100644
--- a/.storybook/test-data.js
+++ b/.storybook/test-data.js
@@ -37,6 +37,21 @@ const state = {
       swapsFeatureIsLive: false,
       swapsQuoteRefreshTime: 60000,
     },
+    accountArray: [
+      {
+        name: 'This is a Really Long Account Name',
+        address: '0x64a845a5b02460acf8a3d84503b0d68d028b4bb4',
+        index: 0,
+        balance: '0x176e5b6f173ebe66',
+      },
+      {
+        name: 'Account 2',
+        address: '0xb19ac54efa18cc3a14a5b821bfec73d284bf0c5e',
+        index: 1,
+        balance: '0x2d3142f5000',
+      },
+    ],
+    connectedAccounts: ['0x64a845a5b02460acf8a3d84503b0d68d028b4bb4'],
     isInitialized: true,
     isUnlocked: true,
     isAccountMenuOpen: false,
diff --git a/ui/pages/create-account/connect-hardware/account-list.stories.js b/ui/pages/create-account/connect-hardware/account-list.stories.js
new file mode 100644
index 000000000..4efa39166
--- /dev/null
+++ b/ui/pages/create-account/connect-hardware/account-list.stories.js
@@ -0,0 +1,68 @@
+import React, { useState } from 'react';
+import { Provider } from 'react-redux';
+import { action } from '@storybook/addon-actions';
+import configureStore from '../../../store/store';
+import testData from '../../../../.storybook/test-data';
+
+import AccountList from './account-list';
+
+const store = configureStore(testData);
+
+export default {
+  title: 'Account List',
+  id: __filename,
+  decorators: [(story) => <Provider store={store}>{story()}</Provider>],
+};
+global.platform = {
+  openTab: () => action('Open Tab')(),
+};
+
+export const AccountListComponent = () => {
+  const [selectedAccounts, setSelectedAccounts] = useState([
+    {
+      name: 'This is a Really Long Account Name',
+      address: '0x64a845a5b02460acf8a3d84503b0d68d028b4bb4',
+      index: 0,
+      balance: '0x176e5b6f173ebe66',
+    },
+  ]);
+  const { metamask } = store.getState();
+  const { accountArray, connectedAccounts } = metamask;
+  const LEDGER_LIVE_PATH = `m/44'/60'/0'/0/0`;
+  const MEW_PATH = `m/44'/60'/0'`;
+  const BIP44_PATH = `m/44'/60'/0'/0`;
+
+  const HD_PATHS = [
+    { name: 'Ledger Live', value: LEDGER_LIVE_PATH },
+    { name: 'Legacy (MEW / MyCrypto)', value: MEW_PATH },
+    { name: `BIP44 Standard (e.g. MetaMask, Trezor)`, value: BIP44_PATH },
+  ];
+
+  const onAccountChange = (account) => {
+    let accounts = [];
+    if (selectedAccounts.includes(account)) {
+      accounts = selectedAccounts.filter((acc) => account !== acc);
+    } else {
+      accounts.push(account);
+    }
+    setSelectedAccounts(accounts);
+  };
+
+  return (
+    <AccountList
+      onPathChange={() => undefined}
+      selectedPath="/"
+      device="null"
+      accounts={accountArray}
+      connectedAccounts={connectedAccounts}
+      onAccountChange={onAccountChange}
+      onForgetDevice={() => action('On Forget Device')()}
+      getPage={() => action('Get Page')()}
+      selectedAccounts={selectedAccounts}
+      hdPaths={HD_PATHS}
+      onCancel={() => action('On Cancel')()}
+      onUnlockAccounts={() => action('On Unlock Accounts')()}
+      onAccountRestriction={() => action('On Account Restriction')()}
+    />
+  );
+};

From bf89226ca1489090943a9dcd3ad193f0f6c67bfb Mon Sep 17 00:00:00 2001
From: Etienne Dusseault <etienne.dusseault@gmail.com>
Date: Wed, 29 Sep 2021 15:43:18 -0300
Subject: [PATCH 27/56] Add send-header to Storybook (#12084)

---
 .storybook/actions/sb-send-action.js          |  9 ++++
 .storybook/reducers/sb-history-reducer.js     |  9 ++++
 .storybook/reducers/sb-send-reducer.js        | 19 +++++++
 .storybook/test-data.js                       | 10 ++++
 .../send/send-header/send-header.stories.js   | 54 +++++++++++++++++++
 5 files changed, 101 insertions(+)
 create mode 100644 .storybook/actions/sb-send-action.js
 create mode 100644 .storybook/reducers/sb-history-reducer.js
 create mode 100644 .storybook/reducers/sb-send-reducer.js
 create mode 100644 ui/pages/send/send-header/send-header.stories.js

diff --git a/.storybook/actions/sb-send-action.js b/.storybook/actions/sb-send-action.js
new file mode 100644
index 000000000..c1e66e41c
--- /dev/null
+++ b/.storybook/actions/sb-send-action.js
@@ -0,0 +1,9 @@
+export const updateSendAsset = (type) => ({
+  type: 'send/updateSendAsset',
+  payload: type,
+});
+
+export const updateSendStage = (stage) => ({
+  type: 'send/updateSendStage',
+  payload: stage,
+});
diff --git a/.storybook/reducers/sb-history-reducer.js b/.storybook/reducers/sb-history-reducer.js
new file mode 100644
index 000000000..a3d282a27
--- /dev/null
+++ b/.storybook/reducers/sb-history-reducer.js
@@ -0,0 +1,9 @@
+import testData from '../test-data';
+
+const initialState = { ...testData.history };
+export default function historySBReducer(state = initialState, action) {
+  switch (action.type) {
+    default:
+      return state;
+  }
+}
diff --git a/.storybook/reducers/sb-send-reducer.js b/.storybook/reducers/sb-send-reducer.js
new file mode 100644
index 000000000..c53cd1bb7
--- /dev/null
+++ b/.storybook/reducers/sb-send-reducer.js
@@ -0,0 +1,19 @@
+import testData from '../test-data';
+
+const initialState = { ...testData.send };
+export default function sendSBReducer(state = initialState, action) {
+  switch (action.type) {
+    case 'send/updateSendStage':
+      return {
+        ...state,
+        stage: action.payload,
+      };
+    case 'send/updateSendAsset':
+      return {
+        ...state,
+        asset: { ...state.asset, type: action.payload },
+      };
+    default:
+      return state;
+  }
+}
diff --git a/.storybook/test-data.js b/.storybook/test-data.js
index 80608dadc..1aac5c8ba 100644
--- a/.storybook/test-data.js
+++ b/.storybook/test-data.js
@@ -379,6 +379,15 @@ const state = {
               value: '0x9c2686',
             },
           ],
+          [
+            {
+              note: 'txStateManager: setting status to confirmed',
+              op: 'replace',
+              path: '/status',
+              timestamp: 1629582721178,
+              value: 'confirmed',
+            },
+          ],
           [
             {
               note: 'txStateManager: setting status to confirmed',
@@ -1178,6 +1187,7 @@ const state = {
       balance: '0x0',
       details: null,
     },
+    stage: 'ADD_RECIPIENT',
     amount: '3782dace9d900000',
     gas: {
       price: null,
diff --git a/ui/pages/send/send-header/send-header.stories.js b/ui/pages/send/send-header/send-header.stories.js
new file mode 100644
index 000000000..034623bb3
--- /dev/null
+++ b/ui/pages/send/send-header/send-header.stories.js
@@ -0,0 +1,54 @@
+import React, { useEffect } from 'react';
+import { combineReducers, createStore } from 'redux';
+import { Provider } from 'react-redux';
+
+import { select } from '@storybook/addon-knobs';
+import {
+  updateSendStage,
+  updateSendAsset,
+} from '../../../../.storybook/actions/sb-send-action';
+
+import sendSBReducer from '../../../../.storybook/reducers/sb-send-reducer';
+import historySBReducer from '../../../../.storybook/reducers/sb-history-reducer';
+
+import { ASSET_TYPES, SEND_STAGES } from '../../../ducks/send';
+import SendHeader from './send-header.component';
+
+export default {
+  title: 'SendHeader',
+  id: __filename,
+};
+
+export const SendHeaderComponent = () => {
+  const store = createStore(
+    combineReducers({ send: sendSBReducer, history: historySBReducer }),
+  );
+  const state = store.getState();
+  const { send } = state;
+  const asset =
+    select('Asset', [ASSET_TYPES.NATIVE, ASSET_TYPES.TOKEN]) || send.asset;
+
+  const stage =
+    select('Stage', [
+      SEND_STAGES.ADD_RECIPIENT,
+      SEND_STAGES.DRAFT,
+      SEND_STAGES.EDIT,
+      SEND_STAGES.INACTIVE,
+    ]) || send.stage;
+
+  useEffect(() => {
+    store.dispatch(updateSendAsset(asset));
+  }, [store, asset]);
+
+  useEffect(() => {
+    store.dispatch(updateSendStage(stage));
+  }, [store, stage]);
+
+  return (
+    <Provider store={store}>
+      <div style={{ width: 600 }}>
+        <SendHeader />
+      </div>
+    </Provider>
+  );
+};

From e238cbc568c2ab3461f2596ae2cf9e0a78274679 Mon Sep 17 00:00:00 2001
From: Niranjana Binoy <43930900+NiranjanaBinoy@users.noreply.github.com>
Date: Wed, 29 Sep 2021 14:53:56 -0400
Subject: [PATCH 28/56] Turning OFF token detection for test nets (#12232)

---
 app/scripts/controllers/detect-tokens.js      | 9 ++++++++-
 app/scripts/controllers/detect-tokens.test.js | 1 +
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/app/scripts/controllers/detect-tokens.js b/app/scripts/controllers/detect-tokens.js
index 246114657..ea647b59f 100644
--- a/app/scripts/controllers/detect-tokens.js
+++ b/app/scripts/controllers/detect-tokens.js
@@ -4,6 +4,7 @@ import SINGLE_CALL_BALANCES_ABI from 'single-call-balance-checker-abi';
 import { SINGLE_CALL_BALANCES_ADDRESS } from '../constants/contracts';
 import { MINUTE } from '../../../shared/constants/time';
 import { isEqualCaseInsensitive } from '../../../ui/helpers/utils/util';
+import { MAINNET_CHAIN_ID } from '../../../shared/constants/network';
 
 // By default, poll every 3 minutes
 const DEFAULT_INTERVAL = MINUTE * 3;
@@ -79,7 +80,13 @@ export default class DetectTokensController {
     }
 
     const { tokenList } = this._tokenList.state;
-    if (Object.keys(tokenList).length === 0) {
+    // since the token detection is currently enabled only on Mainnet
+    // we can use the chainId check to ensure token detection is not triggered for any other network
+    // but once the balance check contract for other networks are deploayed and ready to use, we need to update this check.
+    if (
+      this._network.store.getState().provider.chainId !== MAINNET_CHAIN_ID ||
+      Object.keys(tokenList).length === 0
+    ) {
       return;
     }
 
diff --git a/app/scripts/controllers/detect-tokens.test.js b/app/scripts/controllers/detect-tokens.test.js
index 4a739aff1..4fd5605b0 100644
--- a/app/scripts/controllers/detect-tokens.test.js
+++ b/app/scripts/controllers/detect-tokens.test.js
@@ -42,6 +42,7 @@ describe('DetectTokensController', function () {
       '0x7e57e2',
       '0xbc86727e770de68b1060c91f6bb6945c73e10388',
     ]);
+    preferences.setUseTokenDetection(true);
     sandbox
       .stub(network, 'getLatestBlock')
       .callsFake(() => Promise.resolve({}));

From 6187ab9b01ea757366a42d22b7292413c0f2b119 Mon Sep 17 00:00:00 2001
From: Jyoti Puri <jyotipuri@gmail.com>
Date: Thu, 30 Sep 2021 17:27:59 +0530
Subject: [PATCH 29/56] Fix gas control flicker on send screen when switching
 between EIP-1559 networks (#12230)

Fix gas control flicker on send screen when switching between EIP-1559 networks
---
 test/data/mock-state.json                     |  5 ++
 ui/ducks/metamask/metamask.js                 | 10 ++++
 ui/ducks/metamask/metamask.test.js            | 36 +++++++++++
 .../send-content/send-content.component.js    |  6 +-
 .../send-content/send-content.container.js    |  6 +-
 ui/selectors/selectors.js                     | 16 +++++
 ui/selectors/selectors.test.js                | 59 +++++++++++++++++++
 7 files changed, 133 insertions(+), 5 deletions(-)

diff --git a/test/data/mock-state.json b/test/data/mock-state.json
index 8b6171339..e8c979fd1 100644
--- a/test/data/mock-state.json
+++ b/test/data/mock-state.json
@@ -31,6 +31,11 @@
         "name": "Test Account 2"
       }
     },
+    "networkDetails": {
+      "EIPS": {
+        "1559": true
+      }
+    },
     "cachedBalances": {},
     "incomingTransactions": {},
     "unapprovedTxs": {
diff --git a/ui/ducks/metamask/metamask.js b/ui/ducks/metamask/metamask.js
index d78726a6a..b5867a1ff 100644
--- a/ui/ducks/metamask/metamask.js
+++ b/ui/ducks/metamask/metamask.js
@@ -279,6 +279,16 @@ export function getUnapprovedTxs(state) {
   return state.metamask.unapprovedTxs;
 }
 
+/**
+ * Function returns true if network details are fetched and it is found to not support EIP-1559
+ */
+export function isNotEIP1559Network(state) {
+  return state.metamask.networkDetails?.EIPS[1559] === false;
+}
+
+/**
+ * Function returns true if network details are fetched and it is found to support EIP-1559
+ */
 export function isEIP1559Network(state) {
   return state.metamask.networkDetails?.EIPS[1559] === true;
 }
diff --git a/ui/ducks/metamask/metamask.test.js b/ui/ducks/metamask/metamask.test.js
index 51c88033d..0c75e7f47 100644
--- a/ui/ducks/metamask/metamask.test.js
+++ b/ui/ducks/metamask/metamask.test.js
@@ -7,6 +7,7 @@ import reduceMetamask, {
   getSendHexDataFeatureFlagState,
   getSendToAccounts,
   getUnapprovedTxs,
+  isNotEIP1559Network,
 } from './metamask';
 
 describe('MetaMask Reducers', () => {
@@ -99,6 +100,9 @@ describe('MetaMask Reducers', () => {
             gasPrice: '4a817c800',
           },
         },
+        networkDetails: {
+          EIPS: { 1559: true },
+        },
       },
       {},
     ),
@@ -378,4 +382,36 @@ describe('MetaMask Reducers', () => {
       });
     });
   });
+
+  describe('isNotEIP1559Network()', () => {
+    it('should return true if network does not supports EIP-1559', () => {
+      expect(
+        isNotEIP1559Network({
+          ...mockState,
+          metamask: {
+            ...mockState.metamask,
+            networkDetails: {
+              EIPS: { 1559: false },
+            },
+          },
+        }),
+      ).toStrictEqual(true);
+    });
+
+    it('should return false if networkDetails.EIPS.1559 is not false', () => {
+      expect(isNotEIP1559Network(mockState)).toStrictEqual(false);
+
+      expect(
+        isNotEIP1559Network({
+          ...mockState,
+          metamask: {
+            ...mockState.metamask,
+            networkDetails: {
+              EIPS: { 1559: undefined },
+            },
+          },
+        }),
+      ).toStrictEqual(false);
+    });
+  });
 });
diff --git a/ui/pages/send/send-content/send-content.component.js b/ui/pages/send/send-content/send-content.component.js
index 9ae57736d..0e80abbc0 100644
--- a/ui/pages/send/send-content/send-content.component.js
+++ b/ui/pages/send/send-content/send-content.component.js
@@ -29,7 +29,7 @@ export default class SendContent extends Component {
     gasIsExcessive: PropTypes.bool.isRequired,
     isEthGasPrice: PropTypes.bool,
     noGasPrice: PropTypes.bool,
-    networkAndAccountSupports1559: PropTypes.bool,
+    networkOrAccountNotSupports1559: PropTypes.bool,
   };
 
   render() {
@@ -40,7 +40,7 @@ export default class SendContent extends Component {
       isEthGasPrice,
       noGasPrice,
       isAssetSendable,
-      networkAndAccountSupports1559,
+      networkOrAccountNotSupports1559,
     } = this.props;
 
     let gasError;
@@ -59,7 +59,7 @@ export default class SendContent extends Component {
           {this.maybeRenderAddContact()}
           <SendAssetRow />
           <SendAmountRow />
-          {!networkAndAccountSupports1559 && <SendGasRow />}
+          {networkOrAccountNotSupports1559 && <SendGasRow />}
           {this.props.showHexData && <SendHexDataRow />}
         </div>
       </PageContainerContent>
diff --git a/ui/pages/send/send-content/send-content.container.js b/ui/pages/send/send-content/send-content.container.js
index a1f4daf46..83b38dfeb 100644
--- a/ui/pages/send/send-content/send-content.container.js
+++ b/ui/pages/send/send-content/send-content.container.js
@@ -4,7 +4,7 @@ import {
   getAddressBookEntry,
   getIsEthGasPriceFetched,
   getNoGasPriceFetched,
-  checkNetworkAndAccountSupports1559,
+  checkNetworkOrAccountNotSupports1559,
 } from '../../../selectors';
 import { getIsAssetSendable, getSendTo } from '../../../ducks/send';
 
@@ -25,7 +25,9 @@ function mapStateToProps(state) {
     isEthGasPrice: getIsEthGasPriceFetched(state),
     noGasPrice: getNoGasPriceFetched(state),
     to,
-    networkAndAccountSupports1559: checkNetworkAndAccountSupports1559(state),
+    networkOrAccountNotSupports1559: checkNetworkOrAccountNotSupports1559(
+      state,
+    ),
   };
 }
 
diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js
index 124e39bc4..eae899294 100644
--- a/ui/selectors/selectors.js
+++ b/ui/selectors/selectors.js
@@ -32,6 +32,7 @@ import { DAY } from '../../shared/constants/time';
 import {
   getNativeCurrency,
   getConversionRate,
+  isNotEIP1559Network,
   isEIP1559Network,
 } from '../ducks/metamask/metamask';
 
@@ -92,6 +93,10 @@ export function isEIP1559Account(state) {
   return currentKeyring && currentKeyring.type !== KEYRING_TYPES.TREZOR;
 }
 
+/**
+ * The function returns true if network and account details are fetched and
+ * both of them support EIP-1559.
+ */
 export function checkNetworkAndAccountSupports1559(state) {
   const networkSupports1559 = isEIP1559Network(state);
   const accountSupports1559 = isEIP1559Account(state);
@@ -99,6 +104,17 @@ export function checkNetworkAndAccountSupports1559(state) {
   return networkSupports1559 && accountSupports1559;
 }
 
+/**
+ * The function returns true if network and account details are fetched and
+ * either of them do not support EIP-1559.
+ */
+export function checkNetworkOrAccountNotSupports1559(state) {
+  const networkNotSupports1559 = isNotEIP1559Network(state);
+  const accountSupports1559 = isEIP1559Account(state);
+
+  return networkNotSupports1559 || accountSupports1559 === false;
+}
+
 /**
  * Checks if the current wallet is a hardware wallet.
  * @param {Object} state
diff --git a/ui/selectors/selectors.test.js b/ui/selectors/selectors.test.js
index 75a43ea49..edc572f26 100644
--- a/ui/selectors/selectors.test.js
+++ b/ui/selectors/selectors.test.js
@@ -78,6 +78,65 @@ describe('Selectors', () => {
     });
   });
 
+  describe('#checkNetworkOrAccountNotSupports1559', () => {
+    it('returns false if network and account supports EIP-1559', () => {
+      const not1559Network = selectors.checkNetworkOrAccountNotSupports1559({
+        ...mockState,
+        metamask: {
+          ...mockState.metamask,
+          keyrings: [
+            {
+              type: 'Ledger Hardware',
+              accounts: ['0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'],
+            },
+          ],
+        },
+      });
+      expect(not1559Network).toStrictEqual(false);
+    });
+
+    it('returns true if network does not support EIP-1559', () => {
+      let not1559Network = selectors.checkNetworkOrAccountNotSupports1559({
+        ...mockState,
+        metamask: {
+          ...mockState.metamask,
+          networkDetails: {
+            EIPS: { 1559: undefined },
+          },
+        },
+      });
+      expect(not1559Network).toStrictEqual(true);
+      not1559Network = selectors.checkNetworkOrAccountNotSupports1559({
+        ...mockState,
+        metamask: {
+          ...mockState.metamask,
+          networkDetails: {
+            EIPS: { 1559: false },
+          },
+        },
+      });
+      expect(not1559Network).toStrictEqual(true);
+    });
+
+    it('returns true if account does not support EIP-1559', () => {
+      const networkOrAccountNotSupports1559 = selectors.checkNetworkOrAccountNotSupports1559(
+        {
+          ...mockState,
+          metamask: {
+            ...mockState.metamask,
+            keyrings: [
+              {
+                type: 'Trezor Hardware',
+                accounts: ['0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'],
+              },
+            ],
+          },
+        },
+      );
+      expect(networkOrAccountNotSupports1559).toStrictEqual(true);
+    });
+  });
+
   describe('#getAddressBook', () => {
     it('should return the address book', () => {
       expect(selectors.getAddressBook(mockState)).toStrictEqual([

From 1c5489e046ab557634184007dcd5498c57272c77 Mon Sep 17 00:00:00 2001
From: Bogdan A <arsenebp@gmail.com>
Date: Thu, 30 Sep 2021 19:04:57 +0300
Subject: [PATCH 30/56] Locale en message fix- "not connected this site"
 (#12186)

---
 app/_locales/en/messages.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 1d27a4d5d..9d2aa6aad 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -421,7 +421,7 @@
     "message": "You have 1 account connected to this site."
   },
   "connectedAccountsEmptyDescription": {
-    "message": "MetaMask is not connected this site. To connect to a web3 site, find the connect button on their site."
+    "message": "MetaMask is not connected to this site. To connect to a web3 site, find and click the connect button."
   },
   "connectedSites": {
     "message": "Connected sites"

From 730605353a88ab41e29a6cf3c6a609211af510e1 Mon Sep 17 00:00:00 2001
From: ryanml <ryanlanese@gmail.com>
Date: Thu, 30 Sep 2021 09:14:20 -0700
Subject: [PATCH 31/56] Using URL util to parse hostname for block explorer
 link (#12241)

---
 .../account-details-modal/account-details-modal.component.js    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/ui/components/app/modals/account-details-modal/account-details-modal.component.js b/ui/components/app/modals/account-details-modal/account-details-modal.component.js
index 5943cdca5..72767f310 100644
--- a/ui/components/app/modals/account-details-modal/account-details-modal.component.js
+++ b/ui/components/app/modals/account-details-modal/account-details-modal.component.js
@@ -81,7 +81,7 @@ export default class AccountDetailsModal extends Component {
         >
           {rpcPrefs.blockExplorerUrl
             ? this.context.t('blockExplorerView', [
-                rpcPrefs.blockExplorerUrl.match(/^https?:\/\/(.+)/u)[1],
+                getURLHostName(rpcPrefs.blockExplorerUrl),
               ])
             : this.context.t('viewOnEtherscan', [
                 this.context.t('blockExplorerAccountAction'),

From 9355fb21c70948443372992c07ae8174e829bd7d Mon Sep 17 00:00:00 2001
From: Alex Donesky <adonesky@gmail.com>
Date: Thu, 30 Sep 2021 16:34:11 -0500
Subject: [PATCH 32/56] Establish onboarding-flow wrapper/router base and
 feature flag env variable (#12247)

* establish onboarding-flow wrapper/router base and feature flag env variable

* small cleanup

* addressing feedback
---
 development/build/scripts.js                  |   2 +
 ui/ducks/metamask/metamask.js                 |  15 +++
 ui/helpers/constants/routes.js                |  28 +++++
 .../authenticated/authenticated.component.js  |  22 +++-
 .../initialized/initialized.component.js      |  10 +-
 ui/pages/onboarding-flow/index.scss           |  18 +++
 .../onboarding-flow-switch.js                 |  41 +++++++
 ui/pages/onboarding-flow/onboarding-flow.js   | 109 ++++++++++++++++++
 ui/pages/pages.scss                           |   1 +
 ui/pages/routes/routes.component.js           |   8 +-
 ui/selectors/first-time-flow.js               |  10 +-
 11 files changed, 255 insertions(+), 9 deletions(-)
 create mode 100644 ui/pages/onboarding-flow/index.scss
 create mode 100644 ui/pages/onboarding-flow/onboarding-flow-switch/onboarding-flow-switch.js
 create mode 100644 ui/pages/onboarding-flow/onboarding-flow.js

diff --git a/development/build/scripts.js b/development/build/scripts.js
index da7e73e26..618485ad7 100644
--- a/development/build/scripts.js
+++ b/development/build/scripts.js
@@ -27,6 +27,7 @@ const bifyModuleGroups = require('bify-module-groups');
 
 const metamaskrc = require('rc')('metamask', {
   INFURA_PROJECT_ID: process.env.INFURA_PROJECT_ID,
+  ONBOARDING_V2: process.env.ONBOARDING_V2,
   SEGMENT_HOST: process.env.SEGMENT_HOST,
   SEGMENT_WRITE_KEY: process.env.SEGMENT_WRITE_KEY,
   SEGMENT_LEGACY_WRITE_KEY: process.env.SEGMENT_LEGACY_WRITE_KEY,
@@ -612,6 +613,7 @@ function getEnvironmentVariables({ buildType, devMode, testing }) {
         ? process.env.SEGMENT_PROD_LEGACY_WRITE_KEY
         : metamaskrc.SEGMENT_LEGACY_WRITE_KEY,
     SWAPS_USE_DEV_APIS: process.env.SWAPS_USE_DEV_APIS === '1',
+    ONBOARDING_V2: metamaskrc.ONBOARDING_V2 === '1',
   };
 }
 
diff --git a/ui/ducks/metamask/metamask.js b/ui/ducks/metamask/metamask.js
index b5867a1ff..e1314d5c2 100644
--- a/ui/ducks/metamask/metamask.js
+++ b/ui/ducks/metamask/metamask.js
@@ -325,3 +325,18 @@ export function getIsGasEstimatesLoading(state) {
 
   return isGasEstimatesLoading;
 }
+
+export function getCompletedOnboarding(state) {
+  return state.metamask.completedOnboarding;
+}
+export function getIsInitialized(state) {
+  return state.metamask.isInitialized;
+}
+
+export function getIsUnlocked(state) {
+  return state.metamask.isUnlocked;
+}
+
+export function getSeedPhraseBackedUp(state) {
+  return state.metamask.seedPhraseBackedUp;
+}
diff --git a/ui/helpers/constants/routes.js b/ui/helpers/constants/routes.js
index 800a4badb..1ae2ef494 100644
--- a/ui/helpers/constants/routes.js
+++ b/ui/helpers/constants/routes.js
@@ -52,6 +52,21 @@ const INITIALIZE_END_OF_FLOW_ROUTE = '/initialize/end-of-flow';
 const INITIALIZE_CONFIRM_SEED_PHRASE_ROUTE = '/initialize/seed-phrase/confirm';
 const INITIALIZE_METAMETRICS_OPT_IN_ROUTE = '/initialize/metametrics-opt-in';
 
+const ONBOARDING_ROUTE = '/onboarding';
+const ONBOARDING_REVIEW_SRP_ROUTE = '/onboarding/review-srp';
+const ONBOARDING_CONFIRM_SRP_ROUTE = '/onboarding/confirm-srp';
+const ONBOARDING_CREATE_PASSWORD_ROUTE = '/onboarding/create-password';
+const ONBOARDING_COMPLETION_ROUTE = '/onboarding/completion';
+const ONBOARDING_UNLOCK_ROUTE = '/onboarding/unlock';
+const ONBOARDING_GET_STARTED_ROUTE = '/onboarding/get-started';
+const ONBOARDING_HELP_US_IMPROVE_ROUTE = '/onboarding/help-us-improve';
+const ONBOARDING_IMPORT_WITH_SRP_ROUTE =
+  '/onboarding/create-password/import-with-sre';
+const ONBOARDING_IMPORT_MOBILE_ROUTE = '/onboarding/create-password';
+const ONBOARDING_SECURE_YOUR_WALLET_ROUTE = '/onboarding/secure-your-wallet';
+const ONBOARDING_PRIVACY_SETTINGS_ROUTE = '/onboarding/privacy-settings';
+const ONBOARDING_PIN_EXTENSION_ROUTE = '/onboarding/pin-extension';
+
 const CONFIRM_TRANSACTION_ROUTE = '/confirm-transaction';
 const CONFIRM_SEND_ETHER_PATH = '/send-ether';
 const CONFIRM_SEND_TOKEN_PATH = '/send-token';
@@ -199,4 +214,17 @@ export {
   AWAITING_SIGNATURES_ROUTE,
   SWAPS_ERROR_ROUTE,
   SWAPS_MAINTENANCE_ROUTE,
+  ONBOARDING_ROUTE,
+  ONBOARDING_GET_STARTED_ROUTE,
+  ONBOARDING_HELP_US_IMPROVE_ROUTE,
+  ONBOARDING_CREATE_PASSWORD_ROUTE,
+  ONBOARDING_IMPORT_WITH_SRP_ROUTE,
+  ONBOARDING_IMPORT_MOBILE_ROUTE,
+  ONBOARDING_SECURE_YOUR_WALLET_ROUTE,
+  ONBOARDING_REVIEW_SRP_ROUTE,
+  ONBOARDING_CONFIRM_SRP_ROUTE,
+  ONBOARDING_PRIVACY_SETTINGS_ROUTE,
+  ONBOARDING_COMPLETION_ROUTE,
+  ONBOARDING_UNLOCK_ROUTE,
+  ONBOARDING_PIN_EXTENSION_ROUTE,
 };
diff --git a/ui/helpers/higher-order-components/authenticated/authenticated.component.js b/ui/helpers/higher-order-components/authenticated/authenticated.component.js
index 2feebfdb0..1fe151047 100644
--- a/ui/helpers/higher-order-components/authenticated/authenticated.component.js
+++ b/ui/helpers/higher-order-components/authenticated/authenticated.component.js
@@ -1,16 +1,32 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import { Redirect, Route } from 'react-router-dom';
-import { UNLOCK_ROUTE, INITIALIZE_ROUTE } from '../../constants/routes';
+import {
+  UNLOCK_ROUTE,
+  INITIALIZE_ROUTE,
+  ONBOARDING_ROUTE,
+} from '../../constants/routes';
 
 export default function Authenticated(props) {
   const { isUnlocked, completedOnboarding } = props;
-
   switch (true) {
+    // For ONBOARDING_V2 dev purposes,
+    // Remove when ONBOARDING_V2 dev complete
+    case process.env.ONBOARDING_V2 === true:
+      return <Redirect to={{ pathname: ONBOARDING_ROUTE }} />;
+
     case isUnlocked && completedOnboarding:
       return <Route {...props} />;
     case !completedOnboarding:
-      return <Redirect to={{ pathname: INITIALIZE_ROUTE }} />;
+      return (
+        <Redirect
+          to={{
+            pathname: process.env.ONBOARDING_V2
+              ? ONBOARDING_ROUTE
+              : INITIALIZE_ROUTE,
+          }}
+        />
+      );
     default:
       return <Redirect to={{ pathname: UNLOCK_ROUTE }} />;
   }
diff --git a/ui/helpers/higher-order-components/initialized/initialized.component.js b/ui/helpers/higher-order-components/initialized/initialized.component.js
index a953403fd..5773d605a 100644
--- a/ui/helpers/higher-order-components/initialized/initialized.component.js
+++ b/ui/helpers/higher-order-components/initialized/initialized.component.js
@@ -1,13 +1,19 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import { Redirect, Route } from 'react-router-dom';
-import { INITIALIZE_ROUTE } from '../../constants/routes';
+import { INITIALIZE_ROUTE, ONBOARDING_ROUTE } from '../../constants/routes';
 
 export default function Initialized(props) {
   return props.completedOnboarding ? (
     <Route {...props} />
   ) : (
-    <Redirect to={{ pathname: INITIALIZE_ROUTE }} />
+    <Redirect
+      to={{
+        pathname: process.env.ONBOARDING_V2
+          ? ONBOARDING_ROUTE
+          : INITIALIZE_ROUTE,
+      }}
+    />
   );
 }
 
diff --git a/ui/pages/onboarding-flow/index.scss b/ui/pages/onboarding-flow/index.scss
new file mode 100644
index 000000000..aebae2fbd
--- /dev/null
+++ b/ui/pages/onboarding-flow/index.scss
@@ -0,0 +1,18 @@
+@import 'recovery-phrase/index';
+@import 'new-account/index';
+
+.onboarding-flow {
+  width: 100%;
+  background-color: $white;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+
+  &__wrapper {
+    margin-top: 40px;
+    padding: 32px;
+    border: 1px solid $Grey-100;
+    border-radius: 20px;
+  }
+}
diff --git a/ui/pages/onboarding-flow/onboarding-flow-switch/onboarding-flow-switch.js b/ui/pages/onboarding-flow/onboarding-flow-switch/onboarding-flow-switch.js
new file mode 100644
index 000000000..3512f6f2f
--- /dev/null
+++ b/ui/pages/onboarding-flow/onboarding-flow-switch/onboarding-flow-switch.js
@@ -0,0 +1,41 @@
+import React from 'react';
+import { useSelector } from 'react-redux';
+import { Redirect } from 'react-router-dom';
+import {
+  DEFAULT_ROUTE,
+  ONBOARDING_COMPLETION_ROUTE,
+  ONBOARDING_GET_STARTED_ROUTE,
+  ONBOARDING_UNLOCK_ROUTE,
+  LOCK_ROUTE,
+} from '../../../helpers/constants/routes';
+import {
+  getCompletedOnboarding,
+  getIsInitialized,
+  getIsUnlocked,
+  getSeedPhraseBackedUp,
+} from '../../../ducks/metamask/metamask';
+
+export default function OnboardingFlowSwitch() {
+  const completedOnboarding = useSelector(getCompletedOnboarding);
+  const isInitialized = useSelector(getIsInitialized);
+  const seedPhraseBackedUp = useSelector(getSeedPhraseBackedUp);
+  const isUnlocked = useSelector(getIsUnlocked);
+
+  if (completedOnboarding) {
+    return <Redirect to={{ pathname: DEFAULT_ROUTE }} />;
+  }
+
+  if (seedPhraseBackedUp !== null) {
+    return <Redirect to={{ pathname: ONBOARDING_COMPLETION_ROUTE }} />;
+  }
+
+  if (isUnlocked) {
+    return <Redirect to={{ pathname: LOCK_ROUTE }} />;
+  }
+
+  if (!isInitialized) {
+    return <Redirect to={{ pathname: ONBOARDING_GET_STARTED_ROUTE }} />;
+  }
+
+  return <Redirect to={{ pathname: ONBOARDING_UNLOCK_ROUTE }} />;
+}
diff --git a/ui/pages/onboarding-flow/onboarding-flow.js b/ui/pages/onboarding-flow/onboarding-flow.js
new file mode 100644
index 000000000..fed13cbad
--- /dev/null
+++ b/ui/pages/onboarding-flow/onboarding-flow.js
@@ -0,0 +1,109 @@
+import React, { useEffect, useState } from 'react';
+import { Switch, Route, useHistory } from 'react-router-dom';
+import { useDispatch, useSelector } from 'react-redux';
+import Unlock from '../unlock-page';
+import {
+  ONBOARDING_CREATE_PASSWORD_ROUTE,
+  ONBOARDING_REVIEW_SRP_ROUTE,
+  ONBOARDING_CONFIRM_SRP_ROUTE,
+  ONBOARDING_UNLOCK_ROUTE,
+  DEFAULT_ROUTE,
+} from '../../helpers/constants/routes';
+import {
+  getCompletedOnboarding,
+  getIsInitialized,
+  getIsUnlocked,
+  getSeedPhraseBackedUp,
+} from '../../ducks/metamask/metamask';
+import {
+  createNewVaultAndGetSeedPhrase,
+  unlockAndGetSeedPhrase,
+} from '../../store/actions';
+import { getFirstTimeFlowTypeRoute } from '../../selectors';
+import OnboardingFlowSwitch from './onboarding-flow-switch/onboarding-flow-switch';
+import NewAccount from './new-account/new-account';
+import ReviewRecoveryPhrase from './recovery-phrase/review-recovery-phrase';
+import ConfirmRecoveryPhrase from './recovery-phrase/confirm-recovery-phrase';
+
+export default function OnboardingFlow() {
+  const [seedPhrase, setSeedPhrase] = useState('');
+  const dispatch = useDispatch();
+  const history = useHistory();
+  const isInitialized = useSelector(getIsInitialized);
+  const isUnlocked = useSelector(getIsUnlocked);
+  const completedOnboarding = useSelector(getCompletedOnboarding);
+  const seedPhraseBackedUp = useSelector(getSeedPhraseBackedUp);
+  const nextRoute = useSelector(getFirstTimeFlowTypeRoute);
+
+  useEffect(() => {
+    // For ONBOARDING_V2 dev purposes,
+    // Remove when ONBOARDING_V2 dev complete
+    if (process.env.ONBOARDING_V2) {
+      history.push(ONBOARDING_CREATE_PASSWORD_ROUTE);
+      return;
+    }
+
+    if (completedOnboarding && seedPhraseBackedUp) {
+      history.push(DEFAULT_ROUTE);
+      return;
+    }
+
+    if (isInitialized && !isUnlocked) {
+      history.push(ONBOARDING_UNLOCK_ROUTE);
+    }
+  }, [
+    history,
+    completedOnboarding,
+    isInitialized,
+    isUnlocked,
+    seedPhraseBackedUp,
+  ]);
+
+  const handleCreateNewAccount = async (password) => {
+    const newSeedPhrase = await dispatch(
+      createNewVaultAndGetSeedPhrase(password),
+    );
+    setSeedPhrase(newSeedPhrase);
+  };
+
+  const handleUnlock = async (password) => {
+    const retreivedSeedPhrase = await dispatch(
+      unlockAndGetSeedPhrase(password),
+    );
+    setSeedPhrase(retreivedSeedPhrase);
+    history.push(nextRoute);
+  };
+
+  return (
+    <div className="onboarding-flow">
+      <div className="onboarding-flow__wrapper">
+        <Switch>
+          <Route
+            path={ONBOARDING_CREATE_PASSWORD_ROUTE}
+            render={(routeProps) => (
+              <NewAccount
+                {...routeProps}
+                createNewAccount={handleCreateNewAccount}
+              />
+            )}
+          />
+          <Route
+            path={ONBOARDING_REVIEW_SRP_ROUTE}
+            render={() => <ReviewRecoveryPhrase seedPhrase={seedPhrase} />}
+          />
+          <Route
+            path={ONBOARDING_CONFIRM_SRP_ROUTE}
+            render={() => <ConfirmRecoveryPhrase seedPhrase={seedPhrase} />}
+          />
+          <Route
+            path={ONBOARDING_UNLOCK_ROUTE}
+            render={(routeProps) => (
+              <Unlock {...routeProps} onSubmit={handleUnlock} />
+            )}
+          />
+          <Route exact path="*" component={OnboardingFlowSwitch} />
+        </Switch>
+      </div>
+    </div>
+  );
+}
diff --git a/ui/pages/pages.scss b/ui/pages/pages.scss
index 5cbc7932e..300bdcf4a 100644
--- a/ui/pages/pages.scss
+++ b/ui/pages/pages.scss
@@ -19,3 +19,4 @@
 @import 'settings/index';
 @import 'swaps/index';
 @import 'unlock-page/index';
+@import 'onboarding-flow/index';
diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js
index 4b631c302..4c37ca5bb 100644
--- a/ui/pages/routes/routes.component.js
+++ b/ui/pages/routes/routes.component.js
@@ -53,6 +53,7 @@ import {
   BUILD_QUOTE_ROUTE,
   CONFIRMATION_V_NEXT_ROUTE,
   CONFIRM_IMPORT_TOKEN_ROUTE,
+  ONBOARDING_ROUTE,
 } from '../../helpers/constants/routes';
 
 import {
@@ -62,6 +63,7 @@ import {
 import { getEnvironmentType } from '../../../app/scripts/lib/util';
 import { isBeta } from '../../helpers/utils/build-types';
 import ConfirmationPage from '../confirmation';
+import OnboardingFlow from '../onboarding-flow/onboarding-flow';
 
 export default class Routes extends Component {
   static propTypes = {
@@ -114,9 +116,11 @@ export default class Routes extends Component {
 
   renderRoutes() {
     const { autoLockTimeLimit, setLastActiveTime } = this.props;
-
     const routes = (
       <Switch>
+        {process.env.ONBOARDING_V2 && (
+          <Route path={ONBOARDING_ROUTE} component={OnboardingFlow} />
+        )}
         <Route path={LOCK_ROUTE} component={Lock} exact />
         <Route path={INITIALIZE_ROUTE} component={FirstTimeFlow} />
         <Initialized path={UNLOCK_ROUTE} component={UnlockPage} exact />
@@ -225,7 +229,7 @@ export default class Routes extends Component {
 
     const isInitializing = Boolean(
       matchPath(location.pathname, {
-        path: INITIALIZE_ROUTE,
+        path: process.env.ONBOARDING_V2 ? ONBOARDING_ROUTE : INITIALIZE_ROUTE,
         exact: false,
       }),
     );
diff --git a/ui/selectors/first-time-flow.js b/ui/selectors/first-time-flow.js
index b56bdc861..bbebfbd3a 100644
--- a/ui/selectors/first-time-flow.js
+++ b/ui/selectors/first-time-flow.js
@@ -2,6 +2,8 @@ import {
   INITIALIZE_CREATE_PASSWORD_ROUTE,
   INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE,
   DEFAULT_ROUTE,
+  ONBOARDING_CREATE_PASSWORD_ROUTE,
+  ONBOARDING_IMPORT_WITH_SRP_ROUTE,
 } from '../helpers/constants/routes';
 
 export function getFirstTimeFlowTypeRoute(state) {
@@ -9,9 +11,13 @@ export function getFirstTimeFlowTypeRoute(state) {
 
   let nextRoute;
   if (firstTimeFlowType === 'create') {
-    nextRoute = INITIALIZE_CREATE_PASSWORD_ROUTE;
+    nextRoute = process.env.ONBOARDING_V2
+      ? ONBOARDING_CREATE_PASSWORD_ROUTE
+      : INITIALIZE_CREATE_PASSWORD_ROUTE;
   } else if (firstTimeFlowType === 'import') {
-    nextRoute = INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE;
+    nextRoute = process.env.ONBOARDING_V2
+      ? ONBOARDING_IMPORT_WITH_SRP_ROUTE
+      : INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE;
   } else {
     nextRoute = DEFAULT_ROUTE;
   }

From c14f46eb929b4ff5322f84446bfed0c4b1480871 Mon Sep 17 00:00:00 2001
From: ryanml <ryanlanese@gmail.com>
Date: Fri, 1 Oct 2021 09:07:29 -0700
Subject: [PATCH 33/56] Fixing confirm-add-suggested-token storybook entry
 (#12261)

---
 .../approval-screens/add-suggested-token.js   | 130 ++++++++++--------
 .storybook/test-data.js                       |   2 +-
 .../confirm-add-suggested-token.stories.js    |  22 ++-
 3 files changed, 84 insertions(+), 70 deletions(-)

diff --git a/.storybook/initial-states/approval-screens/add-suggested-token.js b/.storybook/initial-states/approval-screens/add-suggested-token.js
index 0b623e3f7..5cd10451c 100644
--- a/.storybook/initial-states/approval-screens/add-suggested-token.js
+++ b/.storybook/initial-states/approval-screens/add-suggested-token.js
@@ -1,65 +1,83 @@
-export const suggestedTokens = {
-    "0x6b175474e89094c44da98b954eedeac495271d0f": {
-      "address": "0x6b175474e89094c44da98b954eedeac495271d0f",
-      "symbol": "META",
-      "decimals": 18,
-      "image": "metamark.svg",
-      "unlisted": false
+export const suggestedAssets = [
+  {
+    asset: {
+      address: '0x6b175474e89094c44da98b954eedeac495271d0f',
+      symbol: 'META',
+      decimals: 18,
+      image: 'metamark.svg',
+      unlisted: false
     },
-    "0xB8c77482e45F1F44dE1745F52C74426C631bDD52": {
-      "address": "0xB8c77482e45F1F44dE1745F52C74426C631bDD52",
-      "symbol": "0X",
-      "decimals": 18,
-      "image": "0x.svg",
-      "unlisted": false
+  },
+  {
+    asset: {
+      'address': '0xB8c77482e45F1F44dE1745F52C74426C631bDD52',
+      'symbol': '0X',
+      'decimals': 18,
+      'image': '0x.svg',
+      'unlisted': false
     },
-    "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984": {
-      "address": "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984",
-      "symbol": "AST",
-      "decimals": 18,
-      "image": "ast.png",
-      "unlisted": false
+  },
+  {
+    asset: {
+      'address': '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984',
+      'symbol': 'AST',
+      'decimals': 18,
+      'image': 'ast.png',
+      'unlisted': false
     },
-    "0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2": {
-      "address": "0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2",
-      "symbol": "BAT",
-      "decimals": 18,
-      "image": "BAT_icon.svg",
-      "unlisted": false
+  },
+  {
+    asset: {
+      'address': '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2',
+      'symbol': 'BAT',
+      'decimals': 18,
+      'image': 'BAT_icon.svg',
+      'unlisted': false
     },
-    "0xe83cccfabd4ed148903bf36d4283ee7c8b3494d1": {
-      "address": "0xe83cccfabd4ed148903bf36d4283ee7c8b3494d1",
-      "symbol": "CVL",
-      "decimals": 18,
-      "image": "CVL_token.svg",
-      "unlisted": false
+  },
+  {
+    asset: {
+      'address': '0xe83cccfabd4ed148903bf36d4283ee7c8b3494d1',
+      'symbol': 'CVL',
+      'decimals': 18,
+      'image': 'CVL_token.svg',
+      'unlisted': false
     },
-    "0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e": {
-      "address": "0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e",
-      "symbol": "GLA",
-      "decimals": 18,
-      "image": "gladius.svg",
-      "unlisted": false
+  },
+  {
+    asset: {
+      'address': '0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e',
+      'symbol': 'GLA',
+      'decimals': 18,
+      'image': 'gladius.svg',
+      'unlisted': false
     },
-    "0x467Bccd9d29f223BcE8043b84E8C8B282827790F": {
-      "address": "0x467Bccd9d29f223BcE8043b84E8C8B282827790F",
-      "symbol": "GNO",
-      "decimals": 18,
-      "image": "gnosis.svg",
-      "unlisted": false
+  },
+  {
+    asset: {
+      'address': '0x467Bccd9d29f223BcE8043b84E8C8B282827790F',
+      'symbol': 'GNO',
+      'decimals': 18,
+      'image': 'gnosis.svg',
+      'unlisted': false
     },
-    "0xff20817765cb7f73d4bde2e66e067e58d11095c2": {
-      "address": "0xff20817765cb7f73d4bde2e66e067e58d11095c2",
-      "symbol": "OMG",
-      "decimals": 18,
-      "image": "omg.jpg",
-      "unlisted": false
+  },
+  {
+    asset: {
+      'address': '0xff20817765cb7f73d4bde2e66e067e58d11095c2',
+      'symbol': 'OMG',
+      'decimals': 18,
+      'image': 'omg.jpg',
+      'unlisted': false
     },
-    "0x8e870d67f660d95d5be530380d0ec0bd388289e1": {
-      "address": "0x8e870d67f660d95d5be530380d0ec0bd388289e1",
-      "symbol": "WED",
-      "decimals": 18,
-      "image": "wed.png",
-      "unlisted": false
+  },
+  {
+    asset: {
+      'address': '0x8e870d67f660d95d5be530380d0ec0bd388289e1',
+      'symbol': 'WED',
+      'decimals': 18,
+      'image': 'wed.png',
+      'unlisted': false
     },
-  }
\ No newline at end of file
+  },
+]
diff --git a/.storybook/test-data.js b/.storybook/test-data.js
index 1aac5c8ba..045aa298c 100644
--- a/.storybook/test-data.js
+++ b/.storybook/test-data.js
@@ -816,7 +816,7 @@ const state = {
       '0xaD6D458402F60fD3Bd25163575031ACDce07538D': './sai.svg',
     },
     hiddenTokens: [],
-    suggestedTokens: {},
+    suggestedAssets: {},
     useNonceField: false,
     usePhishDetect: true,
     lostIdentities: {},
diff --git a/ui/pages/confirm-add-suggested-token/confirm-add-suggested-token.stories.js b/ui/pages/confirm-add-suggested-token/confirm-add-suggested-token.stories.js
index cdd525aa5..bad912176 100644
--- a/ui/pages/confirm-add-suggested-token/confirm-add-suggested-token.stories.js
+++ b/ui/pages/confirm-add-suggested-token/confirm-add-suggested-token.stories.js
@@ -2,7 +2,7 @@
 import React, { useEffect } from 'react';
 import { text } from '@storybook/addon-knobs';
 import { store, getNewState } from '../../../.storybook/preview';
-import { suggestedTokens } from '../../../.storybook/initial-states/approval-screens/add-suggested-token';
+import { suggestedAssets } from '../../../.storybook/initial-states/approval-screens/add-suggested-token';
 import { updateMetamaskState } from '../../store/actions';
 import ConfirmAddSuggestedToken from '.';
 
@@ -16,32 +16,28 @@ const PageSet = ({ children }) => {
   const image = text('Icon URL', 'metamark.svg');
 
   const state = store.getState();
-  const suggestedTokensState = state.metamask.suggestedTokens;
+  const suggestedAssetsState = state.metamask.suggestedAssets;
 
   useEffect(() => {
-    suggestedTokensState[
-      '0x6b175474e89094c44da98b954eedeac495271d0f'
-    ].symbol = symbol;
+    suggestedAssetsState[0].symbol = symbol;
     store.dispatch(
       updateMetamaskState(
         getNewState(state.metamask, {
-          suggestedTokens: suggestedTokensState,
+          suggestedAssets: suggestedAssetsState,
         }),
       ),
     );
-  }, [symbol, suggestedTokensState, state.metamask]);
+  }, [symbol, suggestedAssetsState, state.metamask]);
   useEffect(() => {
-    suggestedTokensState[
-      '0x6b175474e89094c44da98b954eedeac495271d0f'
-    ].image = image;
+    suggestedAssetsState[0].image = image;
     store.dispatch(
       updateMetamaskState(
         getNewState(state.metamask, {
-          suggestedTokens: suggestedTokensState,
+          suggestedAssets: suggestedAssetsState,
         }),
       ),
     );
-  }, [image, suggestedTokensState, state.metamask]);
+  }, [image, suggestedAssetsState, state.metamask]);
 
   return children;
 };
@@ -51,7 +47,7 @@ export const AddSuggestedToken = () => {
   store.dispatch(
     updateMetamaskState(
       getNewState(state.metamask, {
-        suggestedTokens,
+        suggestedAssets,
       }),
     ),
   );

From f9ea9e4b432176d940001365938aa88253d54024 Mon Sep 17 00:00:00 2001
From: kumavis <kumavis@users.noreply.github.com>
Date: Fri, 1 Oct 2021 08:53:12 -1000
Subject: [PATCH 34/56] lockdown - breakout making globalThis properties
 non-writable (#12258)

* lockdown - breakout making globalThis properties non-writable into lockdown-more.js

* Update app/scripts/lockdown-more.js

Co-authored-by: David Walsh <davidwalsh83@gmail.com>

* Update app/scripts/lockdown-more.js

Co-authored-by: Erik Marks <25517051+rekmarks@users.noreply.github.com>

Co-authored-by: David Walsh <davidwalsh83@gmail.com>
Co-authored-by: Erik Marks <25517051+rekmarks@users.noreply.github.com>
---
 .eslintrc.js                                |  2 +
 app/background.html                         |  1 +
 app/home.html                               |  1 +
 app/manifest/_base.json                     |  1 +
 app/notification.html                       |  1 +
 app/phishing.html                           |  1 +
 app/popup.html                              |  1 +
 app/scripts/lockdown-more.js                | 91 ++++++++++++++++++++
 app/scripts/lockdown-run.js                 | 92 ---------------------
 development/build/static.js                 |  6 ++
 test/unit-global/protect-intrinsics.test.js |  1 +
 11 files changed, 106 insertions(+), 92 deletions(-)
 create mode 100644 app/scripts/lockdown-more.js

diff --git a/.eslintrc.js b/.eslintrc.js
index ca9fe4677..7f8d6695e 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -183,6 +183,7 @@ module.exports = {
         'nyc.config.js',
         'stylelint.config.js',
         'app/scripts/lockdown-run.js',
+        'app/scripts/lockdown-more.js',
         'development/**/*.js',
         'test/e2e/**/*.js',
         'test/lib/wait-until-called.js',
@@ -197,6 +198,7 @@ module.exports = {
     {
       files: [
         'app/scripts/lockdown-run.js',
+        'app/scripts/lockdown-more.js',
         'test/unit-global/protect-intrinsics.test.js',
       ],
       globals: {
diff --git a/app/background.html b/app/background.html
index 2faa31411..4f80b2d7f 100644
--- a/app/background.html
+++ b/app/background.html
@@ -8,6 +8,7 @@
     <script src="./sentry-install.js" type="text/javascript" charset="utf-8"></script>
     <script src="./lockdown-install.js" type="text/javascript" charset="utf-8"></script>
     <script src="./lockdown-run.js" type="text/javascript" charset="utf-8"></script>
+    <script src="./lockdown-more.js" type="text/javascript" charset="utf-8"></script>
     <script src="./runtime-cjs.js" type="text/javascript" charset="utf-8"></script>
     {{@each(it.jsBundles) => val}}
     <script src="{{val}}" type="text/javascript" charset="utf-8"></script>
diff --git a/app/home.html b/app/home.html
index 5350b31dd..c55a6446f 100644
--- a/app/home.html
+++ b/app/home.html
@@ -14,6 +14,7 @@
     <script src="./sentry-install.js" type="text/javascript" charset="utf-8"></script>
     <script src="./lockdown-install.js" type="text/javascript" charset="utf-8"></script>
     <script src="./lockdown-run.js" type="text/javascript" charset="utf-8"></script>
+    <script src="./lockdown-more.js" type="text/javascript" charset="utf-8"></script>
     <script src="./runtime-cjs.js" type="text/javascript" charset="utf-8"></script>
     {{@each(it.jsBundles) => val}}
     <script src="{{val}}" type="text/javascript" charset="utf-8"></script>
diff --git a/app/manifest/_base.json b/app/manifest/_base.json
index 6c328d0c6..e4f55da26 100644
--- a/app/manifest/_base.json
+++ b/app/manifest/_base.json
@@ -35,6 +35,7 @@
         "globalthis.js",
         "lockdown-install.js",
         "lockdown-run.js",
+        "lockdown-more.js",
         "contentscript.js"
       ],
       "run_at": "document_start",
diff --git a/app/notification.html b/app/notification.html
index 55b98c960..7ff5d73cc 100644
--- a/app/notification.html
+++ b/app/notification.html
@@ -37,6 +37,7 @@
     <script src="./sentry-install.js" type="text/javascript" charset="utf-8"></script>
     <script src="./lockdown-install.js" type="text/javascript" charset="utf-8"></script>
     <script src="./lockdown-run.js" type="text/javascript" charset="utf-8"></script>
+    <script src="./lockdown-more.js" type="text/javascript" charset="utf-8"></script>
     <script src="./runtime-cjs.js" type="text/javascript" charset="utf-8"></script>
     {{@each(it.jsBundles) => val}}
     <script src="{{val}}" type="text/javascript" charset="utf-8"></script>
diff --git a/app/phishing.html b/app/phishing.html
index dd180fb77..30accb9bf 100644
--- a/app/phishing.html
+++ b/app/phishing.html
@@ -5,6 +5,7 @@
     <script src="./globalthis.js" type="text/javascript" charset="utf-8"></script>
     <script src="./lockdown-install.js" type="text/javascript" charset="utf-8"></script>
     <script src="./lockdown-run.js" type="text/javascript" charset="utf-8"></script>
+    <script src="./lockdown-more.js" type="text/javascript" charset="utf-8"></script>
     <script src="./phishing-detect.js"></script>
     <link rel="stylesheet" type="text/css" href="./index.css" title="ltr">
     <link rel="stylesheet" type="text/css" href="./index-rtl.css" title="rtl" disabled>
diff --git a/app/popup.html b/app/popup.html
index c94b82df8..378efbfb4 100644
--- a/app/popup.html
+++ b/app/popup.html
@@ -14,6 +14,7 @@
     <script src="./sentry-install.js" type="text/javascript" charset="utf-8"></script>
     <script src="./lockdown-install.js" type="text/javascript" charset="utf-8"></script>
     <script src="./lockdown-run.js" type="text/javascript" charset="utf-8"></script>
+    <script src="./lockdown-more.js" type="text/javascript" charset="utf-8"></script>
     <script src="./runtime-cjs.js" type="text/javascript" charset="utf-8"></script>
     {{@each(it.jsBundles) => val}}
     <script src="{{val}}" type="text/javascript" charset="utf-8"></script>
diff --git a/app/scripts/lockdown-more.js b/app/scripts/lockdown-more.js
new file mode 100644
index 000000000..7db50e43f
--- /dev/null
+++ b/app/scripts/lockdown-more.js
@@ -0,0 +1,91 @@
+// Make all "object" and "function" own properties of globalThis
+// non-configurable and non-writable, when possible.
+// We call a property that is non-configurable and non-writable,
+// "non-modifiable".
+try {
+  /**
+   * `lockdown` only hardens the properties enumerated by the
+   * universalPropertyNames constant specified in 'ses/src/whitelist'. This
+   * function makes all function and object properties on the start compartment
+   * global non-configurable and non-writable, unless they are already
+   * non-configurable.
+   *
+   * It is critical that this function runs at the right time during
+   * initialization, which should always be immediately after `lockdown` has been
+   * called. At the time of writing, the modifications this function makes to the
+   * runtime environment appear to be non-breaking, but that could change with
+   * the addition of dependencies, or the order of our scripts in our HTML files.
+   * Exercise caution.
+   *
+   * See inline comments for implementation details.
+   *
+   * We write this function in IIFE format to avoid polluting global scope.
+   */
+  (function protectIntrinsics() {
+    const namedIntrinsics = Reflect.ownKeys(new Compartment().globalThis);
+
+    // These named intrinsics are not automatically hardened by `lockdown`
+    const shouldHardenManually = new Set(['eval', 'Function']);
+
+    const globalProperties = new Set([
+      // universalPropertyNames is a constant added by lockdown to global scope
+      // at the time of writing, it is initialized in 'ses/src/whitelist'.
+      // These properties tend to be non-enumerable.
+      ...namedIntrinsics,
+
+      // TODO: Also include the named platform globals
+      // This grabs every enumerable property on globalThis.
+      // ...Object.keys(globalThis),
+    ]);
+
+    globalProperties.forEach((propertyName) => {
+      const descriptor = Reflect.getOwnPropertyDescriptor(
+        globalThis,
+        propertyName,
+      );
+
+      if (descriptor) {
+        if (descriptor.configurable) {
+          // If the property on globalThis is configurable, make it
+          // non-configurable. If it has no accessor properties, also make it
+          // non-writable.
+          if (hasAccessor(descriptor)) {
+            Object.defineProperty(globalThis, propertyName, {
+              configurable: false,
+            });
+          } else {
+            Object.defineProperty(globalThis, propertyName, {
+              configurable: false,
+              writable: false,
+            });
+          }
+        }
+
+        if (shouldHardenManually.has(propertyName)) {
+          harden(globalThis[propertyName]);
+        }
+      }
+    });
+
+    /**
+     * Checks whether the given propertyName descriptor has any accessors, i.e. the
+     * properties `get` or `set`.
+     *
+     * We want to make globals non-writable, and we can't set the `writable`
+     * property and accessor properties at the same time.
+     *
+     * @param {Object} descriptor - The propertyName descriptor to check.
+     * @returns {boolean} Whether the propertyName descriptor has any accessors.
+     */
+    function hasAccessor(descriptor) {
+      return 'set' in descriptor || 'get' in descriptor;
+    }
+  })();
+} catch (error) {
+  console.error('Protecting intrinsics failed:', error);
+  if (globalThis?.sentry.captureException) {
+    globalThis.sentry.captureException(
+      new Error(`Protecting intrinsics failed: ${error.message}`),
+    );
+  }
+}
diff --git a/app/scripts/lockdown-run.js b/app/scripts/lockdown-run.js
index 3c7276fdf..0d869cc7f 100644
--- a/app/scripts/lockdown-run.js
+++ b/app/scripts/lockdown-run.js
@@ -20,95 +20,3 @@ try {
     );
   }
 }
-
-// Make all "object" and "function" own properties of globalThis
-// non-configurable and non-writable, when possible.
-// We call the a property that is non-configurable and non-writable,
-// "non-modifiable".
-try {
-  /**
-   * `lockdown` only hardens the properties enumerated by the
-   * universalPropertyNames constant specified in 'ses/src/whitelist'. This
-   * function makes all function and object properties on the start compartment
-   * global non-configurable and non-writable, unless they are already
-   * non-configurable.
-   *
-   * It is critical that this function runs at the right time during
-   * initialization, which should always be immediately after `lockdown` has been
-   * called. At the time of writing, the modifications this function makes to the
-   * runtime environment appear to be non-breaking, but that could change with
-   * the addition of dependencies, or the order of our scripts in our HTML files.
-   * Exercise caution.
-   *
-   * See inline comments for implementation details.
-   *
-   * We write this function in IIFE format to avoid polluting global scope.
-   */
-  (function protectIntrinsics() {
-    const namedIntrinsics = Reflect.ownKeys(new Compartment().globalThis);
-
-    // These named intrinsics are not automatically hardened by `lockdown`
-    const shouldHardenManually = new Set(['eval', 'Function']);
-
-    const globalProperties = new Set([
-      // universalPropertyNames is a constant added by lockdown to global scope
-      // at the time of writing, it is initialized in 'ses/src/whitelist'.
-      // These properties tend to be non-enumerable.
-      ...namedIntrinsics,
-
-      // TODO: Also include the named platform globals
-      // This grabs every enumerable property on globalThis.
-      // ...Object.keys(globalThis),
-    ]);
-
-    globalProperties.forEach((propertyName) => {
-      const descriptor = Reflect.getOwnPropertyDescriptor(
-        globalThis,
-        propertyName,
-      );
-
-      if (descriptor) {
-        if (descriptor.configurable) {
-          // If the property on globalThis is configurable, make it
-          // non-configurable. If it has no accessor properties, also make it
-          // non-writable.
-          if (hasAccessor(descriptor)) {
-            Object.defineProperty(globalThis, propertyName, {
-              configurable: false,
-            });
-          } else {
-            Object.defineProperty(globalThis, propertyName, {
-              configurable: false,
-              writable: false,
-            });
-          }
-        }
-
-        if (shouldHardenManually.has(propertyName)) {
-          harden(globalThis[propertyName]);
-        }
-      }
-    });
-
-    /**
-     * Checks whether the given propertyName descriptor has any accessors, i.e. the
-     * properties `get` or `set`.
-     *
-     * We want to make globals non-writable, and we can't set the `writable`
-     * property and accessor properties at the same time.
-     *
-     * @param {Object} descriptor - The propertyName descriptor to check.
-     * @returns {boolean} Whether the propertyName descriptor has any accessors.
-     */
-    function hasAccessor(descriptor) {
-      return 'set' in descriptor || 'get' in descriptor;
-    }
-  })();
-} catch (error) {
-  console.error('Protecting intrinsics failed:', error);
-  if (globalThis.sentry && globalThis.sentry.captureException) {
-    globalThis.sentry.captureException(
-      new Error(`Protecting intrinsics failed: ${error.message}`),
-    );
-  }
-}
diff --git a/development/build/static.js b/development/build/static.js
index 5e5425a58..3926cefb0 100644
--- a/development/build/static.js
+++ b/development/build/static.js
@@ -148,6 +148,12 @@ function getCopyTargets(shouldIncludeLockdown) {
         : EMPTY_JS_FILE,
       dest: `lockdown-run.js`,
     },
+    {
+      src: shouldIncludeLockdown
+        ? `./app/scripts/lockdown-more.js`
+        : EMPTY_JS_FILE,
+      dest: `lockdown-more.js`,
+    },
     {
       // eslint-disable-next-line node/no-extraneous-require
       src: require.resolve('@lavamoat/lavapack/src/runtime-cjs.js'),
diff --git a/test/unit-global/protect-intrinsics.test.js b/test/unit-global/protect-intrinsics.test.js
index 5075080f9..3958d7762 100644
--- a/test/unit-global/protect-intrinsics.test.js
+++ b/test/unit-global/protect-intrinsics.test.js
@@ -1,5 +1,6 @@
 import 'ses/lockdown';
 import '../../app/scripts/lockdown-run';
+import '../../app/scripts/lockdown-more';
 import { strict as assert } from 'assert';
 
 // These are Agoric inventions, and we don't care about them.

From a174d50ba5c578f279c57343d5e10ebd3a69053b Mon Sep 17 00:00:00 2001
From: kumavis <kumavis@users.noreply.github.com>
Date: Fri, 1 Oct 2021 09:39:28 -1000
Subject: [PATCH 35/56] ci - improve lavamoat validation debug info (#12259)

* ci - improve lavamoat validation debug info

* Update validate-allow-scripts.sh

* Update validate-lavamoat-policy.sh
---
 .circleci/scripts/validate-allow-scripts.sh   | 2 +-
 .circleci/scripts/validate-lavamoat-policy.sh | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/.circleci/scripts/validate-allow-scripts.sh b/.circleci/scripts/validate-allow-scripts.sh
index e466f039c..de45520ad 100755
--- a/.circleci/scripts/validate-allow-scripts.sh
+++ b/.circleci/scripts/validate-allow-scripts.sh
@@ -6,7 +6,7 @@ set -o pipefail
 
 yarn allow-scripts auto
 
-if git diff --exit-code --quiet
+if git diff --exit-code
 then
   echo "allow-scripts configuration is up-to-date"
 else
diff --git a/.circleci/scripts/validate-lavamoat-policy.sh b/.circleci/scripts/validate-lavamoat-policy.sh
index 4eab30b75..d674cd3f0 100755
--- a/.circleci/scripts/validate-lavamoat-policy.sh
+++ b/.circleci/scripts/validate-lavamoat-policy.sh
@@ -6,7 +6,7 @@ set -o pipefail
 
 yarn lavamoat:auto
 
-if git diff --exit-code --quiet
+if git diff --exit-code
 then
   echo "LavaMoat policy is up-to-date"
 else

From 7c4bd78f2e48a208fe635019ab9bbb83ac9c7854 Mon Sep 17 00:00:00 2001
From: Elliot Winkler <elliot.winkler@gmail.com>
Date: Fri, 1 Oct 2021 16:23:54 -0600
Subject: [PATCH 36/56] Suppress prop types warning in RadioGroupComponent
 (#12249)

When editing the gas fee for a transaction, the following warning is
being output to the console:

    Warning: Failed prop type: Connector: prop type `isFirst` is invalid; it must be a function, usually from the `prop-types` package, but received `undefined`.

This commit fixes this issue.
---
 ui/components/ui/radio-group/radio-group.component.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/ui/components/ui/radio-group/radio-group.component.js b/ui/components/ui/radio-group/radio-group.component.js
index 7bc0dae6b..90c136fb9 100644
--- a/ui/components/ui/radio-group/radio-group.component.js
+++ b/ui/components/ui/radio-group/radio-group.component.js
@@ -24,8 +24,8 @@ function Connector({ isFirst, isLast }) {
 }
 
 Connector.propTypes = {
-  isFirst: PropTypes.boolean,
-  isLast: PropTypes.boolean,
+  isFirst: PropTypes.bool,
+  isLast: PropTypes.bool,
 };
 
 export default function RadioGroup({ options, name, selectedValue, onChange }) {

From f741712255b60462321ec99882fcb2cf8efcf723 Mon Sep 17 00:00:00 2001
From: ryanml <ryanlanese@gmail.com>
Date: Mon, 4 Oct 2021 06:46:18 -0700
Subject: [PATCH 37/56] Fixing confirmation screen storybook views (#12271)

---
 .storybook/test-data.js                       | 65 +++++++++++++++++++
 .../confirm-import-token.stories.js           | 22 +++++--
 2 files changed, 83 insertions(+), 4 deletions(-)

diff --git a/.storybook/test-data.js b/.storybook/test-data.js
index 045aa298c..a725361a0 100644
--- a/.storybook/test-data.js
+++ b/.storybook/test-data.js
@@ -14,6 +14,71 @@ const state = {
     url: 'https://metamask.github.io/test-dapp/',
   },
   metamask: {
+    tokenList: {
+      '0x6b175474e89094c44da98b954eedeac495271d0f': {
+        address: '0x6b175474e89094c44da98b954eedeac495271d0f',
+        symbol: 'META',
+        decimals: 18,
+        image: 'metamark.svg',
+        unlisted: false
+      },
+      '0xB8c77482e45F1F44dE1745F52C74426C631bDD52': {
+        address: '0xB8c77482e45F1F44dE1745F52C74426C631bDD52',
+        symbol: '0X',
+        decimals: 18,
+        image: '0x.svg',
+        unlisted: false
+      },
+      '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984': {
+        address: '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984',
+        symbol: 'AST',
+        decimals: 18,
+        image: 'ast.png',
+        unlisted: false
+      },
+      '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2': {
+        address: '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2',
+        symbol: 'BAT',
+        decimals: 18,
+        image: 'BAT_icon.svg',
+        unlisted: false
+      },
+      '0xe83cccfabd4ed148903bf36d4283ee7c8b3494d1': {
+        address: '0xe83cccfabd4ed148903bf36d4283ee7c8b3494d1',
+        symbol: 'CVL',
+        decimals: 18,
+        image: 'CVL_token.svg',
+        unlisted: false
+      },
+      '0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e': {
+        address: '0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e',
+        symbol: 'GLA',
+        decimals: 18,
+        image: 'gladius.svg',
+        unlisted: false
+      },
+      '0x467Bccd9d29f223BcE8043b84E8C8B282827790F': {
+        address: '0x467Bccd9d29f223BcE8043b84E8C8B282827790F',
+        symbol: 'GNO',
+        decimals: 18,
+        image: 'gnosis.svg',
+        unlisted: false
+      },
+      '0xff20817765cb7f73d4bde2e66e067e58d11095c2': {
+        address: '0xff20817765cb7f73d4bde2e66e067e58d11095c2',
+        symbol: 'OMG',
+        decimals: 18,
+        image: 'omg.jpg',
+        unlisted: false
+      },
+      '0x8e870d67f660d95d5be530380d0ec0bd388289e1': {
+        address: '0x8e870d67f660d95d5be530380d0ec0bd388289e1',
+        symbol: 'WED',
+        decimals: 18,
+        image: 'wed.png',
+        unlisted: false
+      },
+    },
     networkDetails: {
       EIPS: {
         1559: true,
diff --git a/ui/pages/confirm-import-token/confirm-import-token.stories.js b/ui/pages/confirm-import-token/confirm-import-token.stories.js
index 87acd2f59..dcc1d1fcc 100644
--- a/ui/pages/confirm-import-token/confirm-import-token.stories.js
+++ b/ui/pages/confirm-import-token/confirm-import-token.stories.js
@@ -3,7 +3,7 @@ import React, { useEffect } from 'react';
 
 import { createBrowserHistory } from 'history';
 import { text } from '@storybook/addon-knobs';
-import { store } from '../../../.storybook/preview';
+import { store, getNewState } from '../../../.storybook/preview';
 import { tokens } from '../../../.storybook/initial-states/approval-screens/add-token';
 import { updateMetamaskState } from '../../store/actions';
 import ConfirmAddToken from '.';
@@ -23,14 +23,28 @@ const PageSet = ({ children }) => {
   useEffect(() => {
     const pendingTokens = { ...pendingTokensState };
     pendingTokens['0x33f90dee07c6e8b9682dd20f73e6c358b2ed0f03'].symbol = symbol;
-    store.dispatch(updateMetamaskState({ pendingTokens }));
-  }, [symbol, pendingTokensState]);
+    store.dispatch(
+      updateMetamaskState(
+        getNewState(state.metamask, {
+          pendingTokens,
+        }),
+      ),
+    );
+  }, [symbol, pendingTokensState, state.metamask]);
 
   return children;
 };
 
 export const AddToken = () => {
-  store.dispatch(updateMetamaskState({ pendingTokens: tokens }));
+  const { metamask: state } = store.getState();
+  store.dispatch(
+    updateMetamaskState(
+      getNewState(state, {
+        pendingTokens: tokens,
+      }),
+    ),
+  );
+
   return (
     <PageSet>
       <ConfirmAddToken history={history} />

From 9de632e9d142c3c99a556bd88d6b41623f59aeab Mon Sep 17 00:00:00 2001
From: Etienne Dusseault <etienne.dusseault@gmail.com>
Date: Tue, 5 Oct 2021 11:13:07 -0300
Subject: [PATCH 38/56] Fix contact-list Storybook (#12262)

---
 .storybook/test-data.js | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/.storybook/test-data.js b/.storybook/test-data.js
index a725361a0..1e1e0e397 100644
--- a/.storybook/test-data.js
+++ b/.storybook/test-data.js
@@ -199,6 +199,18 @@ const state = {
         },
       },
     },
+    addresses: [
+      {
+        address: '0x39a4e4Af7cCB654dB9500F258c64781c8FbD39F0',
+        name: 'DAI',
+        isEns: false,
+      },
+      {
+        address: '1x39a4e4Af7cCB654dB9500F258c64781c8FbD39F0',
+        name: 'ETH',
+        isEns: true,
+      },
+    ],
     contractExchangeRates: {
       '0xaD6D458402F60fD3Bd25163575031ACDce07538D': 0,
     },

From 68d7ff73c54c029cbf1f864b3bb1b45d63e935bb Mon Sep 17 00:00:00 2001
From: Jyoti Puri <jyotipuri@gmail.com>
Date: Tue, 5 Oct 2021 21:46:08 +0530
Subject: [PATCH 39/56] Remove autofocus from gas limit input on advance gas
 popup (#12279)

---
 .../app/advanced-gas-controls/advanced-gas-controls.component.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js b/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js
index 93f3e5ee3..d6676f93e 100644
--- a/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js
+++ b/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js
@@ -54,7 +54,6 @@ export default function AdvancedGasControls({
         value={gasLimit}
         allowDecimals={false}
         numeric
-        autoFocus
       />
       {showFeeMarketFields ? (
         <>

From 68259ee3e261986f24261bff37b1fb298ac7aea9 Mon Sep 17 00:00:00 2001
From: Niranjana Binoy <43930900+NiranjanaBinoy@users.noreply.github.com>
Date: Tue, 5 Oct 2021 15:20:42 -0400
Subject: [PATCH 40/56] UX Papercuts Epic: Create a consistent representation
 of the Buttons (#12096)

---
 test/e2e/metamask-ui.spec.js                           |  2 +-
 test/e2e/tests/custom-rpc-history.spec.js              |  6 +++---
 test/e2e/tests/from-import-ui.spec.js                  |  2 +-
 test/e2e/tests/incremental-security.spec.js            |  2 +-
 .../app/account-menu/account-menu.component.js         |  5 +++--
 ui/components/app/account-menu/account-menu.test.js    |  3 ++-
 ui/components/app/account-menu/index.scss              |  6 +++---
 .../invalid-custom-network-alert.scss                  |  1 -
 .../unconnected-account-alert.js                       |  1 -
 .../unconnected-account-alert.scss                     |  1 -
 ui/components/app/app-components.scss                  |  1 +
 ui/components/app/cancel-button/cancel-button.js       |  5 ++---
 .../confirm-page-container-content.component.js        |  1 -
 .../confirm-page-container.component.js                |  1 -
 ui/components/app/home-notification/index.scss         |  2 --
 ui/components/app/loading-network-screen/index.scss    |  6 ++++++
 .../loading-network-screen.component.js                |  2 +-
 .../metamask-template-renderer.stories.js              |  2 --
 ui/components/app/modal/modal.component.js             |  9 ++-------
 ui/components/app/modal/modal.component.test.js        | 10 +++++-----
 .../cancel-transaction/cancel-transaction.component.js |  1 -
 .../confirm-delete-network.component.js                |  2 +-
 .../confirm-delete-network.test.js                     |  4 ++--
 .../confirm-remove-account.component.js                |  1 -
 .../confirm-remove-account.test.js                     |  4 ++--
 .../confirm-reset-account.component.js                 |  2 +-
 .../confirm-reset-account.test.js                      |  6 ++++--
 .../customize-nonce/customize-nonce.component.js       |  3 ---
 .../edit-approval-permission.component.js              |  1 -
 .../export-private-key-modal.component.js              |  6 +++---
 .../hide-token-confirmation-modal.js                   |  4 ++--
 .../metametrics-opt-in-modal.component.js              |  1 -
 .../metametrics-opt-in-modal.test.js                   |  4 +++-
 .../app/modals/qr-scanner/qr-scanner.component.js      |  1 -
 .../reject-transactions.component.js                   |  1 -
 .../reject-transactions/reject-transactions.test.js    |  4 ++--
 .../transaction-confirmed.test.js                      |  4 +---
 .../permission-page-container.component.js             |  1 -
 .../recovery-phrase-reminder.js                        |  2 +-
 .../signature-request-original.component.js            |  4 ++--
 .../signature-request-footer/index.scss                |  1 -
 .../signature-request-footer.component.js              |  2 +-
 .../signature-request.container.test.js                |  2 +-
 .../app/transaction-list-item-details/index.scss       |  7 ++++++-
 .../transaction-list-item-details.component.js         |  4 ++--
 .../transaction-list-item.component.js                 |  3 +--
 .../app/transaction-list/transaction-list.component.js |  1 -
 ui/components/app/whats-new-popup/whats-new-popup.js   |  1 -
 ui/components/ui/button/button.component.js            |  6 +++---
 .../page-container-footer.component.js                 |  4 ++--
 .../page-container-footer.component.test.js            |  2 +-
 .../ui/page-container/page-container.component.js      |  1 -
 .../truncated-definition-list.js                       |  1 -
 ui/css/itcss/components/send.scss                      |  3 ++-
 .../confirm-add-suggested-token.component.js           |  4 ++--
 .../confirm-decrypt-message.component.js               |  4 ++--
 .../confirm-encryption-public-key.component.js         |  4 ++--
 .../confirm-import-token.component.js                  |  4 ++--
 .../confirmation-footer/confirmation-footer.js         |  4 ++--
 .../create-account/connect-hardware/account-list.js    |  2 +-
 ui/pages/create-account/import-account/json.js         |  4 ++--
 ui/pages/create-account/import-account/private-key.js  |  4 ++--
 ui/pages/create-account/new-account.component.js       |  4 ++--
 .../metametrics-opt-in/metametrics-opt-in.component.js |  1 -
 .../metametrics-opt-in/metametrics-opt-in.test.js      |  2 +-
 ui/pages/keychains/index.scss                          |  5 +++++
 ui/pages/keychains/restore-vault.js                    |  2 +-
 ui/pages/keychains/reveal-seed.js                      |  8 ++++----
 ui/pages/mobile-sync/mobile-sync.component.js          |  8 ++++----
 ui/pages/onboarding-flow/new-account/new-account.js    |  1 -
 .../recovery-phrase/confirm-recovery-phrase.js         |  1 -
 .../recovery-phrase/review-recovery-phrase.js          |  2 --
 .../choose-account/choose-account.component.js         |  2 +-
 ui/pages/permissions-connect/choose-account/index.scss |  2 +-
 .../permissions-connect/permissions-connect.stories.js |  1 -
 .../add-contact/add-contact.component.js               |  1 -
 .../contact-list-tab/contact-list-tab.component.js     |  1 -
 .../edit-contact/edit-contact.component.js             |  1 -
 ui/pages/settings/contact-list-tab/index.scss          |  1 -
 ui/pages/settings/networks-tab/index.scss              |  4 ++--
 .../network-form/network-form.component.js             |  4 ++--
 .../settings/networks-tab/networks-tab.component.js    |  2 +-
 ui/pages/swaps/import-token/import-token.js            |  4 +---
 .../item-list/item-list.component.js                   |  2 +-
 ui/pages/swaps/select-quote-popover/index.scss         |  1 -
 .../swaps/select-quote-popover/select-quote-popover.js |  4 ++--
 .../__snapshots__/swaps-footer.test.js.snap            |  4 ++--
 ui/pages/swaps/swaps-footer/swaps-footer.js            |  1 -
 ui/pages/unlock-page/index.scss                        |  1 +
 ui/pages/unlock-page/unlock-page.component.js          |  4 ++--
 90 files changed, 122 insertions(+), 144 deletions(-)
 create mode 100644 ui/components/app/loading-network-screen/index.scss

diff --git a/test/e2e/metamask-ui.spec.js b/test/e2e/metamask-ui.spec.js
index 74ea431f2..3f57d00ae 100644
--- a/test/e2e/metamask-ui.spec.js
+++ b/test/e2e/metamask-ui.spec.js
@@ -100,7 +100,7 @@ describe('MetaMask', function () {
     });
 
     it('clicks the "No thanks" option on the metametrics opt-in screen', async function () {
-      await driver.clickElement('.btn-default');
+      await driver.clickElement('.btn-secondary');
       await driver.delay(largeDelayMs);
     });
 
diff --git a/test/e2e/tests/custom-rpc-history.spec.js b/test/e2e/tests/custom-rpc-history.spec.js
index 08a2bdd31..078cb124f 100644
--- a/test/e2e/tests/custom-rpc-history.spec.js
+++ b/test/e2e/tests/custom-rpc-history.spec.js
@@ -48,7 +48,7 @@ describe('Stores custom RPC history', function () {
         await chainIdInput.clear();
         await chainIdInput.sendKeys(chainId.toString());
 
-        await driver.clickElement('.network-form__footer .btn-secondary');
+        await driver.clickElement('.network-form__footer .btn-primary');
         await driver.findElement({ text: networkName, tag: 'span' });
       },
     );
@@ -192,7 +192,7 @@ describe('Stores custom RPC history', function () {
         await driver.clickElement({ text: 'Custom RPC', tag: 'span' });
 
         // cancel new custom rpc
-        await driver.clickElement('.network-form__footer button.btn-default');
+        await driver.clickElement('.network-form__footer button.btn-secondary');
 
         const networkListItems = await driver.findClickableElements(
           '.networks-tab__networks-list-name',
@@ -209,7 +209,7 @@ describe('Stores custom RPC history', function () {
         );
 
         await driver.clickElement(
-          '.button.btn-danger.modal-container__footer-button',
+          '.button.btn-danger-primary.modal-container__footer-button',
         );
 
         // wait for confirm delete modal to be removed from DOM.
diff --git a/test/e2e/tests/from-import-ui.spec.js b/test/e2e/tests/from-import-ui.spec.js
index 176b51e48..a7fba5982 100644
--- a/test/e2e/tests/from-import-ui.spec.js
+++ b/test/e2e/tests/from-import-ui.spec.js
@@ -38,7 +38,7 @@ describe('Metamask Import UI', function () {
         await driver.clickElement({ text: 'Import wallet', tag: 'button' });
 
         // clicks the "No thanks" option on the metametrics opt-in screen
-        await driver.clickElement('.btn-default');
+        await driver.clickElement('.btn-secondary');
 
         // Import Secret Recovery Phrase
         await driver.fill(
diff --git a/test/e2e/tests/incremental-security.spec.js b/test/e2e/tests/incremental-security.spec.js
index 185a0d8c0..f6e5e5066 100644
--- a/test/e2e/tests/incremental-security.spec.js
+++ b/test/e2e/tests/incremental-security.spec.js
@@ -42,7 +42,7 @@ describe('Incremental Security', function () {
         await driver.clickElement({ text: 'Create a Wallet', tag: 'button' });
 
         // clicks the "No thanks" option on the metametrics opt-in screen
-        await driver.clickElement('.btn-default');
+        await driver.clickElement('.btn-secondary');
 
         // accepts a secure password
         await driver.fill(
diff --git a/ui/components/app/account-menu/account-menu.component.js b/ui/components/app/account-menu/account-menu.component.js
index 41c809be8..c391a08aa 100644
--- a/ui/components/app/account-menu/account-menu.component.js
+++ b/ui/components/app/account-menu/account-menu.component.js
@@ -19,6 +19,7 @@ import {
 } from '../../../helpers/constants/routes';
 import TextField from '../../ui/text-field';
 import SearchIcon from '../../ui/search-icon';
+import Button from '../../ui/button';
 
 import { isBeta } from '../../../helpers/utils/build-types';
 
@@ -324,7 +325,7 @@ export default class AccountMenu extends Component {
         <div className="account-menu__close-area" onClick={toggleAccountMenu} />
         <AccountMenuItem className="account-menu__header">
           {t('myAccounts')}
-          <button
+          <Button
             className="account-menu__lock-button"
             onClick={() => {
               lockMetamask();
@@ -332,7 +333,7 @@ export default class AccountMenu extends Component {
             }}
           >
             {t('lock')}
-          </button>
+          </Button>
         </AccountMenuItem>
         <div className="account-menu__divider" />
         <div className="account-menu__accounts-container">
diff --git a/ui/components/app/account-menu/account-menu.test.js b/ui/components/app/account-menu/account-menu.test.js
index fc715ead1..501c187dd 100644
--- a/ui/components/app/account-menu/account-menu.test.js
+++ b/ui/components/app/account-menu/account-menu.test.js
@@ -3,6 +3,7 @@ import sinon from 'sinon';
 import configureMockStore from 'redux-mock-store';
 import { Provider } from 'react-redux';
 import { mountWithRouter } from '../../../../test/lib/render-helpers';
+import Button from '../../ui/button';
 import AccountMenu from '.';
 
 describe('Account Menu', () => {
@@ -103,7 +104,7 @@ describe('Account Menu', () => {
     let logout;
 
     it('logout', () => {
-      logout = wrapper.find('.account-menu__lock-button');
+      logout = wrapper.find(Button);
       expect(logout).toHaveLength(1);
     });
 
diff --git a/ui/components/app/account-menu/index.scss b/ui/components/app/account-menu/index.scss
index 8f1f5c8c8..18059a7e8 100644
--- a/ui/components/app/account-menu/index.scss
+++ b/ui/components/app/account-menu/index.scss
@@ -97,14 +97,14 @@
     align-items: center;
   }
 
-  &__lock-button {
+  & &__lock-button {
     @include H7;
 
-    border: 1px solid $dusty-gray;
+    border: 1px solid $ui-white;
     background-color: transparent;
     color: $white;
-    border-radius: 4px;
     padding: 3.5px 24px;
+    width: 59px;
   }
 
   &__item-icon {
diff --git a/ui/components/app/alerts/invalid-custom-network-alert/invalid-custom-network-alert.scss b/ui/components/app/alerts/invalid-custom-network-alert/invalid-custom-network-alert.scss
index de7010883..a1a170466 100644
--- a/ui/components/app/alerts/invalid-custom-network-alert/invalid-custom-network-alert.scss
+++ b/ui/components/app/alerts/invalid-custom-network-alert/invalid-custom-network-alert.scss
@@ -37,7 +37,6 @@
     & &-button {
       height: 40px;
       width: 50%;
-      border-radius: 100px;
       margin-right: 24px;
 
       &:last-of-type {
diff --git a/ui/components/app/alerts/unconnected-account-alert/unconnected-account-alert.js b/ui/components/app/alerts/unconnected-account-alert/unconnected-account-alert.js
index 1b887bba4..cbcac50eb 100644
--- a/ui/components/app/alerts/unconnected-account-alert/unconnected-account-alert.js
+++ b/ui/components/app/alerts/unconnected-account-alert/unconnected-account-alert.js
@@ -76,7 +76,6 @@ const UnconnectedAccountAlert = () => {
           disabled={alertState === LOADING}
           onClick={onClose}
           type="primary"
-          rounded
           className="unconnected-account-alert__dismiss-button"
         >
           {t('dismiss')}
diff --git a/ui/components/app/alerts/unconnected-account-alert/unconnected-account-alert.scss b/ui/components/app/alerts/unconnected-account-alert/unconnected-account-alert.scss
index 11c12f420..cedd14617 100644
--- a/ui/components/app/alerts/unconnected-account-alert/unconnected-account-alert.scss
+++ b/ui/components/app/alerts/unconnected-account-alert/unconnected-account-alert.scss
@@ -22,7 +22,6 @@
     height: 40px;
     width: 100px;
     border: 0;
-    border-radius: 100px;
   }
 
   &__error {
diff --git a/ui/components/app/app-components.scss b/ui/components/app/app-components.scss
index aabadf245..d76c2998f 100644
--- a/ui/components/app/app-components.scss
+++ b/ui/components/app/app-components.scss
@@ -46,3 +46,4 @@
 @import 'transaction-total-banner/index';
 @import 'wallet-overview/index';
 @import 'whats-new-popup/index';
+@import 'loading-network-screen/index'
diff --git a/ui/components/app/cancel-button/cancel-button.js b/ui/components/app/cancel-button/cancel-button.js
index bef23f98c..0efce2212 100644
--- a/ui/components/app/cancel-button/cancel-button.js
+++ b/ui/components/app/cancel-button/cancel-button.js
@@ -33,11 +33,10 @@ export default function CancelButton({
   const btn = (
     <Button
       onClick={cancelTransaction}
-      rounded={!detailsModal}
-      type={detailsModal ? 'raised' : null}
+      type="secondary"
       className={classnames({
         'transaction-list-item__header-button': !detailsModal,
-        'transaction-list-item-details__header-button': detailsModal,
+        'transaction-list-item-details__header-button-rounded-button': detailsModal,
       })}
       disabled={!hasEnoughCancelGas}
     >
diff --git a/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.js b/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.js
index 0ab2af228..030cb14f2 100644
--- a/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.js
+++ b/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.js
@@ -122,7 +122,6 @@ export default class ConfirmPageContainerContent extends Component {
           cancelText={cancelText}
           onSubmit={onSubmit}
           submitText={submitText}
-          submitButtonType="confirm"
           disabled={disabled}
         >
           {unapprovedTxCount > 1 && <a onClick={onCancelAll}>{rejectNText}</a>}
diff --git a/ui/components/app/confirm-page-container/confirm-page-container.component.js b/ui/components/app/confirm-page-container/confirm-page-container.component.js
index 491235122..397fc7ff0 100644
--- a/ui/components/app/confirm-page-container/confirm-page-container.component.js
+++ b/ui/components/app/confirm-page-container/confirm-page-container.component.js
@@ -198,7 +198,6 @@ export default class ConfirmPageContainer extends Component {
             cancelText={this.context.t('reject')}
             onSubmit={onSubmit}
             submitText={this.context.t('confirm')}
-            submitButtonType="confirm"
             disabled={disabled}
           >
             {unapprovedTxCount > 1 && (
diff --git a/ui/components/app/home-notification/index.scss b/ui/components/app/home-notification/index.scss
index 8d522a0e4..62a556fbb 100644
--- a/ui/components/app/home-notification/index.scss
+++ b/ui/components/app/home-notification/index.scss
@@ -71,7 +71,6 @@
   & &__ignore-button {
     border-color: #6a737d;
     box-sizing: border-box;
-    border-radius: 6px;
     color: $white;
     background-color: inherit;
     height: 34px;
@@ -95,7 +94,6 @@
   & &__accept-button {
     border-color: #6a737d;
     box-sizing: border-box;
-    border-radius: 6px;
     color: $white;
     background-color: inherit;
     height: 34px;
diff --git a/ui/components/app/loading-network-screen/index.scss b/ui/components/app/loading-network-screen/index.scss
new file mode 100644
index 000000000..95039f422
--- /dev/null
+++ b/ui/components/app/loading-network-screen/index.scss
@@ -0,0 +1,6 @@
+.loading-overlay__error-buttons {
+  button {
+    margin: 5px;
+    padding: 5px 30px;
+  }
+}
diff --git a/ui/components/app/loading-network-screen/loading-network-screen.component.js b/ui/components/app/loading-network-screen/loading-network-screen.component.js
index 7f2f2df48..0b3f05229 100644
--- a/ui/components/app/loading-network-screen/loading-network-screen.component.js
+++ b/ui/components/app/loading-network-screen/loading-network-screen.component.js
@@ -71,7 +71,7 @@ export default class LoadingNetworkScreen extends PureComponent {
         <span>{this.context.t('somethingWentWrong')}</span>
         <div className="loading-overlay__error-buttons">
           <Button
-            type="default"
+            type="secondary"
             onClick={() => {
               window.clearTimeout(this.cancelCallTimeout);
               showNetworkDropdown();
diff --git a/ui/components/app/metamask-template-renderer/metamask-template-renderer.stories.js b/ui/components/app/metamask-template-renderer/metamask-template-renderer.stories.js
index 030f20af3..a877f33d7 100644
--- a/ui/components/app/metamask-template-renderer/metamask-template-renderer.stories.js
+++ b/ui/components/app/metamask-template-renderer/metamask-template-renderer.stories.js
@@ -61,7 +61,6 @@ const SECTIONS = {
           children: 'Cancel',
           key: 'cancel-button',
           props: {
-            rounded: true,
             type: 'outlined',
             style: {
               width: '45%',
@@ -73,7 +72,6 @@ const SECTIONS = {
           children: 'OK',
           key: 'ok-button',
           props: {
-            rounded: true,
             type: 'primary',
             style: {
               width: '45%',
diff --git a/ui/components/app/modal/modal.component.js b/ui/components/app/modal/modal.component.js
index 3ad24196b..14335c634 100644
--- a/ui/components/app/modal/modal.component.js
+++ b/ui/components/app/modal/modal.component.js
@@ -21,13 +21,11 @@ export default class Modal extends PureComponent {
     onCancel: PropTypes.func,
     cancelType: PropTypes.string,
     cancelText: PropTypes.string,
-    rounded: PropTypes.bool,
   };
 
   static defaultProps = {
-    submitType: 'secondary',
-    cancelType: 'default',
-    rounded: false,
+    submitType: 'primary',
+    cancelType: 'secondary',
   };
 
   render() {
@@ -45,7 +43,6 @@ export default class Modal extends PureComponent {
       contentClass,
       containerClass,
       hideFooter,
-      rounded,
     } = this.props;
 
     return (
@@ -64,7 +61,6 @@ export default class Modal extends PureComponent {
             {onCancel && (
               <Button
                 type={cancelType}
-                rounded={rounded}
                 onClick={onCancel}
                 className="modal-container__footer-button"
               >
@@ -73,7 +69,6 @@ export default class Modal extends PureComponent {
             )}
             <Button
               type={submitType}
-              rounded={rounded || false}
               onClick={onSubmit}
               disabled={submitDisabled}
               className="modal-container__footer-button"
diff --git a/ui/components/app/modal/modal.component.test.js b/ui/components/app/modal/modal.component.test.js
index 31fb1ce8a..646c9b11a 100644
--- a/ui/components/app/modal/modal.component.test.js
+++ b/ui/components/app/modal/modal.component.test.js
@@ -11,7 +11,7 @@ describe('Modal Component', () => {
     expect(wrapper.find('.modal-container')).toHaveLength(1);
     const buttons = wrapper.find(Button);
     expect(buttons).toHaveLength(1);
-    expect(buttons.at(0).props().type).toStrictEqual('secondary');
+    expect(buttons.at(0).props().type).toStrictEqual('primary');
   });
 
   it('should render a modal with a cancel and a submit button', () => {
@@ -31,13 +31,13 @@ describe('Modal Component', () => {
     const cancelButton = buttons.at(0);
     const submitButton = buttons.at(1);
 
-    expect(cancelButton.props().type).toStrictEqual('default');
+    expect(cancelButton.props().type).toStrictEqual('secondary');
     expect(cancelButton.props().children).toStrictEqual('Cancel');
     expect(handleCancel.callCount).toStrictEqual(0);
     cancelButton.simulate('click');
     expect(handleCancel.callCount).toStrictEqual(1);
 
-    expect(submitButton.props().type).toStrictEqual('secondary');
+    expect(submitButton.props().type).toStrictEqual('primary');
     expect(submitButton.props().children).toStrictEqual('Submit');
     expect(handleSubmit.callCount).toStrictEqual(0);
     submitButton.simulate('click');
@@ -49,7 +49,7 @@ describe('Modal Component', () => {
       <Modal
         onCancel={() => undefined}
         cancelText="Cancel"
-        cancelType="secondary"
+        cancelType="default"
         onSubmit={() => undefined}
         submitText="Submit"
         submitType="confirm"
@@ -58,7 +58,7 @@ describe('Modal Component', () => {
 
     const buttons = wrapper.find(Button);
     expect(buttons).toHaveLength(2);
-    expect(buttons.at(0).props().type).toStrictEqual('secondary');
+    expect(buttons.at(0).props().type).toStrictEqual('default');
     expect(buttons.at(1).props().type).toStrictEqual('confirm');
   });
 
diff --git a/ui/components/app/modals/cancel-transaction/cancel-transaction.component.js b/ui/components/app/modals/cancel-transaction/cancel-transaction.component.js
index 9b436f7d9..f98ce7c7b 100644
--- a/ui/components/app/modals/cancel-transaction/cancel-transaction.component.js
+++ b/ui/components/app/modals/cancel-transaction/cancel-transaction.component.js
@@ -55,7 +55,6 @@ export default class CancelTransaction extends PureComponent {
         onCancel={this.handleCancel}
         submitText={t('yesLetsTry')}
         cancelText={t('nevermind')}
-        submitType="secondary"
         submitDisabled={busy}
       >
         <div>
diff --git a/ui/components/app/modals/confirm-delete-network/confirm-delete-network.component.js b/ui/components/app/modals/confirm-delete-network/confirm-delete-network.component.js
index a8be936a5..74b48bb85 100644
--- a/ui/components/app/modals/confirm-delete-network/confirm-delete-network.component.js
+++ b/ui/components/app/modals/confirm-delete-network/confirm-delete-network.component.js
@@ -30,7 +30,7 @@ export default class ConfirmDeleteNetwork extends PureComponent {
         onCancel={() => this.props.hideModal()}
         submitText={t('delete')}
         cancelText={t('cancel')}
-        submitType="danger"
+        submitType="danger-primary"
       >
         <ModalContent
           title={t('deleteNetwork')}
diff --git a/ui/components/app/modals/confirm-delete-network/confirm-delete-network.test.js b/ui/components/app/modals/confirm-delete-network/confirm-delete-network.test.js
index 701f01a84..64d6689d5 100644
--- a/ui/components/app/modals/confirm-delete-network/confirm-delete-network.test.js
+++ b/ui/components/app/modals/confirm-delete-network/confirm-delete-network.test.js
@@ -34,7 +34,7 @@ describe('Confirm Delete Network', () => {
 
   it('clicks cancel to hide modal', () => {
     const cancelButton = wrapper.find(
-      '.button.btn-default.modal-container__footer-button',
+      '.button.btn-secondary.modal-container__footer-button',
     );
     cancelButton.simulate('click');
 
@@ -43,7 +43,7 @@ describe('Confirm Delete Network', () => {
 
   it('clicks delete to delete the target and hides modal', async () => {
     const deleteButton = wrapper.find(
-      '.button.btn-danger.modal-container__footer-button',
+      '.button.btn-danger-primary.modal-container__footer-button',
     );
 
     deleteButton.simulate('click');
diff --git a/ui/components/app/modals/confirm-remove-account/confirm-remove-account.component.js b/ui/components/app/modals/confirm-remove-account/confirm-remove-account.component.js
index e7bfd69cd..891e40f87 100644
--- a/ui/components/app/modals/confirm-remove-account/confirm-remove-account.component.js
+++ b/ui/components/app/modals/confirm-remove-account/confirm-remove-account.component.js
@@ -95,7 +95,6 @@ export default class ConfirmRemoveAccount extends Component {
         onCancel={this.handleCancel}
         submitText={t('remove')}
         cancelText={t('nevermind')}
-        submitType="secondary"
       >
         <div>
           {this.renderSelectedAccount()}
diff --git a/ui/components/app/modals/confirm-remove-account/confirm-remove-account.test.js b/ui/components/app/modals/confirm-remove-account/confirm-remove-account.test.js
index da59aeffa..f55fec4be 100644
--- a/ui/components/app/modals/confirm-remove-account/confirm-remove-account.test.js
+++ b/ui/components/app/modals/confirm-remove-account/confirm-remove-account.test.js
@@ -51,14 +51,14 @@ describe('Confirm Remove Account', () => {
   });
 
   it('nevermind', () => {
-    const nevermind = wrapper.find({ type: 'default' });
+    const nevermind = wrapper.find({ type: 'secondary' });
     nevermind.simulate('click');
 
     expect(props.hideModal.calledOnce).toStrictEqual(true);
   });
 
   it('remove', async () => {
-    const remove = wrapper.find({ type: 'secondary' });
+    const remove = wrapper.find({ type: 'primary' });
     remove.simulate('click');
 
     expect(await props.removeAccount.calledOnce).toStrictEqual(true);
diff --git a/ui/components/app/modals/confirm-reset-account/confirm-reset-account.component.js b/ui/components/app/modals/confirm-reset-account/confirm-reset-account.component.js
index c4e5a9a17..f9c4fe8fb 100644
--- a/ui/components/app/modals/confirm-reset-account/confirm-reset-account.component.js
+++ b/ui/components/app/modals/confirm-reset-account/confirm-reset-account.component.js
@@ -25,7 +25,7 @@ export default class ConfirmResetAccount extends PureComponent {
         onCancel={() => this.props.hideModal()}
         submitText={t('reset')}
         cancelText={t('nevermind')}
-        submitType="danger"
+        submitType="danger-primary"
       >
         <ModalContent
           title={`${t('resetAccount')}?`}
diff --git a/ui/components/app/modals/confirm-reset-account/confirm-reset-account.test.js b/ui/components/app/modals/confirm-reset-account/confirm-reset-account.test.js
index 9dd3b40f7..eda960772 100644
--- a/ui/components/app/modals/confirm-reset-account/confirm-reset-account.test.js
+++ b/ui/components/app/modals/confirm-reset-account/confirm-reset-account.test.js
@@ -25,7 +25,7 @@ describe('Confirm Reset Account', () => {
 
   it('hides modal when nevermind button is clicked', () => {
     const nevermind = wrapper.find(
-      '.btn-default.modal-container__footer-button',
+      '.btn-secondary.modal-container__footer-button',
     );
     nevermind.simulate('click');
 
@@ -33,7 +33,9 @@ describe('Confirm Reset Account', () => {
   });
 
   it('resets account and hides modal when reset button is clicked', async () => {
-    const reset = wrapper.find('.btn-danger.modal-container__footer-button');
+    const reset = wrapper.find(
+      '.btn-danger-primary.modal-container__footer-button',
+    );
     reset.simulate('click');
 
     expect(await props.resetAccount.calledOnce).toStrictEqual(true);
diff --git a/ui/components/app/modals/customize-nonce/customize-nonce.component.js b/ui/components/app/modals/customize-nonce/customize-nonce.component.js
index d2cacc816..91357d89e 100644
--- a/ui/components/app/modals/customize-nonce/customize-nonce.component.js
+++ b/ui/components/app/modals/customize-nonce/customize-nonce.component.js
@@ -37,11 +37,8 @@ const CustomizeNonce = ({
         hideModal();
       }}
       submitText={t('save')}
-      submitType="primary"
       onCancel={() => hideModal()}
       cancelText={t('cancel')}
-      cancelType="secondary"
-      rounded
       contentClass="customize-nonce-modal-content"
       containerClass="customize-nonce-modal-container"
     >
diff --git a/ui/components/app/modals/edit-approval-permission/edit-approval-permission.component.js b/ui/components/app/modals/edit-approval-permission/edit-approval-permission.component.js
index 2e100353f..b6cfec569 100644
--- a/ui/components/app/modals/edit-approval-permission/edit-approval-permission.component.js
+++ b/ui/components/app/modals/edit-approval-permission/edit-approval-permission.component.js
@@ -226,7 +226,6 @@ export default class EditApprovalPermission extends PureComponent {
           hideModal();
         }}
         submitText={t('save')}
-        submitType="primary"
         contentClass="edit-approval-permission-modal-content"
         containerClass="edit-approval-permission-modal-container"
         submitDisabled={disabled}
diff --git a/ui/components/app/modals/export-private-key-modal/export-private-key-modal.component.js b/ui/components/app/modals/export-private-key-modal/export-private-key-modal.component.js
index ba6e4ca8b..ae378f3ce 100644
--- a/ui/components/app/modals/export-private-key-modal/export-private-key-modal.component.js
+++ b/ui/components/app/modals/export-private-key-modal/export-private-key-modal.component.js
@@ -93,7 +93,7 @@ export default class ExportPrivateKeyModal extends Component {
       <div className="export-private-key-modal__buttons">
         {!privateKey && (
           <Button
-            type="default"
+            type="secondary"
             large
             className="export-private-key-modal__button export-private-key-modal__button--cancel"
             onClick={() => hideModal()}
@@ -104,7 +104,7 @@ export default class ExportPrivateKeyModal extends Component {
         {privateKey ? (
           <Button
             onClick={() => hideModal()}
-            type="secondary"
+            type="primary"
             large
             className="export-private-key-modal__button"
           >
@@ -115,7 +115,7 @@ export default class ExportPrivateKeyModal extends Component {
             onClick={() =>
               this.exportAccountAndGetPrivateKey(this.state.password, address)
             }
-            type="secondary"
+            type="primary"
             large
             className="export-private-key-modal__button"
             disabled={!this.state.password}
diff --git a/ui/components/app/modals/hide-token-confirmation-modal/hide-token-confirmation-modal.js b/ui/components/app/modals/hide-token-confirmation-modal/hide-token-confirmation-modal.js
index 934ca70d0..2c0965ebf 100644
--- a/ui/components/app/modals/hide-token-confirmation-modal/hide-token-confirmation-modal.js
+++ b/ui/components/app/modals/hide-token-confirmation-modal/hide-token-confirmation-modal.js
@@ -61,7 +61,7 @@ class HideTokenConfirmationModal extends Component {
           </div>
           <div className="hide-token-confirmation__buttons">
             <Button
-              type="default"
+              type="secondary"
               className="hide-token-confirmation__button"
               data-testid="hide-token-confirmation__cancel"
               onClick={() => hideModal()}
@@ -69,7 +69,7 @@ class HideTokenConfirmationModal extends Component {
               {this.context.t('cancel')}
             </Button>
             <Button
-              type="secondary"
+              type="primary"
               className="hide-token-confirmation__button"
               data-testid="hide-token-confirmation__hide"
               onClick={() => hideToken(address)}
diff --git a/ui/components/app/modals/metametrics-opt-in-modal/metametrics-opt-in-modal.component.js b/ui/components/app/modals/metametrics-opt-in-modal/metametrics-opt-in-modal.component.js
index 9daf60e56..847a57bf6 100644
--- a/ui/components/app/modals/metametrics-opt-in-modal/metametrics-opt-in-modal.component.js
+++ b/ui/components/app/modals/metametrics-opt-in-modal/metametrics-opt-in-modal.component.js
@@ -140,7 +140,6 @@ export default class MetaMetricsOptInModal extends Component {
                 });
               }}
               submitText={t('affirmAgree')}
-              submitButtonType="confirm"
               disabled={false}
             />
           </div>
diff --git a/ui/components/app/modals/metametrics-opt-in-modal/metametrics-opt-in-modal.test.js b/ui/components/app/modals/metametrics-opt-in-modal/metametrics-opt-in-modal.test.js
index 7b90b24ff..ff6650652 100644
--- a/ui/components/app/modals/metametrics-opt-in-modal/metametrics-opt-in-modal.test.js
+++ b/ui/components/app/modals/metametrics-opt-in-modal/metametrics-opt-in-modal.test.js
@@ -28,7 +28,9 @@ describe('MetaMetrics Opt In', () => {
   });
 
   it('passes false to setParticipateInMetaMetrics and hides modal', async () => {
-    const noThanks = wrapper.find('.btn-default.page-container__footer-button');
+    const noThanks = wrapper.find(
+      '.btn-secondary.page-container__footer-button',
+    );
     noThanks.simulate('click');
 
     expect(await props.setParticipateInMetaMetrics.calledOnce).toStrictEqual(
diff --git a/ui/components/app/modals/qr-scanner/qr-scanner.component.js b/ui/components/app/modals/qr-scanner/qr-scanner.component.js
index fac593717..610c2631b 100644
--- a/ui/components/app/modals/qr-scanner/qr-scanner.component.js
+++ b/ui/components/app/modals/qr-scanner/qr-scanner.component.js
@@ -221,7 +221,6 @@ export default class QrScanner extends Component {
           onSubmit={this.tryAgain}
           cancelText={t('cancel')}
           submitText={t('tryAgain')}
-          submitButtonType="confirm"
         />
       </>
     );
diff --git a/ui/components/app/modals/reject-transactions/reject-transactions.component.js b/ui/components/app/modals/reject-transactions/reject-transactions.component.js
index 3bb4cd9d9..6ef0daa91 100644
--- a/ui/components/app/modals/reject-transactions/reject-transactions.component.js
+++ b/ui/components/app/modals/reject-transactions/reject-transactions.component.js
@@ -32,7 +32,6 @@ export default class RejectTransactionsModal extends PureComponent {
         onCancel={hideModal}
         submitText={t('rejectAll')}
         cancelText={t('cancel')}
-        submitType="secondary"
       >
         <div>
           <div className="reject-transactions__description">
diff --git a/ui/components/app/modals/reject-transactions/reject-transactions.test.js b/ui/components/app/modals/reject-transactions/reject-transactions.test.js
index 043f9bab7..e8ec7a7b5 100644
--- a/ui/components/app/modals/reject-transactions/reject-transactions.test.js
+++ b/ui/components/app/modals/reject-transactions/reject-transactions.test.js
@@ -26,7 +26,7 @@ describe('Reject Transactions Model', () => {
 
   it('hides modal when cancel button is clicked', () => {
     const cancelButton = wrapper.find(
-      '.btn-default.modal-container__footer-button',
+      '.btn-secondary.modal-container__footer-button',
     );
     cancelButton.simulate('click');
 
@@ -35,7 +35,7 @@ describe('Reject Transactions Model', () => {
 
   it('onSubmit is called and hides modal when reject all clicked', async () => {
     const rejectAllButton = wrapper.find(
-      '.btn-secondary.modal-container__footer-button',
+      '.btn-primary.modal-container__footer-button',
     );
     rejectAllButton.simulate('click');
 
diff --git a/ui/components/app/modals/transaction-confirmed/transaction-confirmed.test.js b/ui/components/app/modals/transaction-confirmed/transaction-confirmed.test.js
index 84111a9ee..0e1658242 100644
--- a/ui/components/app/modals/transaction-confirmed/transaction-confirmed.test.js
+++ b/ui/components/app/modals/transaction-confirmed/transaction-confirmed.test.js
@@ -17,9 +17,7 @@ describe('Transaction Confirmed', () => {
         },
       },
     );
-    const submit = wrapper.find(
-      '.btn-secondary.modal-container__footer-button',
-    );
+    const submit = wrapper.find('.btn-primary.modal-container__footer-button');
     submit.simulate('click');
 
     expect(props.onSubmit.calledOnce).toStrictEqual(true);
diff --git a/ui/components/app/permission-page-container/permission-page-container.component.js b/ui/components/app/permission-page-container/permission-page-container.component.js
index a8378f746..2a8daa2f6 100644
--- a/ui/components/app/permission-page-container/permission-page-container.component.js
+++ b/ui/components/app/permission-page-container/permission-page-container.component.js
@@ -141,7 +141,6 @@ export default class PermissionPageContainer extends Component {
             cancelText={this.context.t('cancel')}
             onSubmit={() => this.onSubmit()}
             submitText={this.context.t('connect')}
-            submitButtonType="confirm"
             buttonSizeLarge={false}
           />
         </div>
diff --git a/ui/components/app/recovery-phrase-reminder/recovery-phrase-reminder.js b/ui/components/app/recovery-phrase-reminder/recovery-phrase-reminder.js
index 1b8b66ba3..23dd36442 100644
--- a/ui/components/app/recovery-phrase-reminder/recovery-phrase-reminder.js
+++ b/ui/components/app/recovery-phrase-reminder/recovery-phrase-reminder.js
@@ -75,7 +75,7 @@ export default function RecoveryPhraseReminder({ onConfirm, hasBackedUp }) {
         </Box>
         <Box justifyContent={JUSTIFY_CONTENT.CENTER}>
           <Box width={BLOCK_SIZES.TWO_FIFTHS}>
-            <Button rounded type="primary" onClick={onConfirm}>
+            <Button type="primary" onClick={onConfirm}>
               {t('recoveryPhraseReminderConfirm')}
             </Button>
           </Box>
diff --git a/ui/components/app/signature-request-original/signature-request-original.component.js b/ui/components/app/signature-request-original/signature-request-original.component.js
index c999cf282..41312296c 100644
--- a/ui/components/app/signature-request-original/signature-request-original.component.js
+++ b/ui/components/app/signature-request-original/signature-request-original.component.js
@@ -290,7 +290,7 @@ export default class SignatureRequestOriginal extends Component {
     return (
       <div className="request-signature__footer">
         <Button
-          type="default"
+          type="secondary"
           large
           className="request-signature__footer__cancel-button"
           onClick={async (event) => {
@@ -311,7 +311,7 @@ export default class SignatureRequestOriginal extends Component {
         </Button>
         <Button
           data-testid="request-signature__sign"
-          type="secondary"
+          type="primary"
           large
           className="request-signature__footer__sign-button"
           onClick={async (event) => {
diff --git a/ui/components/app/signature-request/signature-request-footer/index.scss b/ui/components/app/signature-request/signature-request-footer/index.scss
index b0c07f0b1..b1470d3e3 100644
--- a/ui/components/app/signature-request/signature-request-footer/index.scss
+++ b/ui/components/app/signature-request/signature-request-footer/index.scss
@@ -6,7 +6,6 @@
     text-transform: uppercase;
     flex: 1;
     margin: 1rem 0.5rem;
-    border-radius: 3px;
   }
 
   button:first-child {
diff --git a/ui/components/app/signature-request/signature-request-footer/signature-request-footer.component.js b/ui/components/app/signature-request/signature-request-footer/signature-request-footer.component.js
index c0a08812b..0ca254079 100644
--- a/ui/components/app/signature-request/signature-request-footer/signature-request-footer.component.js
+++ b/ui/components/app/signature-request/signature-request-footer/signature-request-footer.component.js
@@ -16,7 +16,7 @@ export default class SignatureRequestFooter extends PureComponent {
     const { cancelAction, signAction } = this.props;
     return (
       <div className="signature-request-footer">
-        <Button onClick={cancelAction} type="default" large>
+        <Button onClick={cancelAction} type="secondary" large>
           {this.context.t('cancel')}
         </Button>
         <Button onClick={signAction} type="primary" large>
diff --git a/ui/components/app/signature-request/signature-request.container.test.js b/ui/components/app/signature-request/signature-request.container.test.js
index c0bc03540..0eb4a6deb 100644
--- a/ui/components/app/signature-request/signature-request.container.test.js
+++ b/ui/components/app/signature-request/signature-request.container.test.js
@@ -64,7 +64,7 @@ describe('Signature Request', () => {
   });
 
   it('cancel', () => {
-    const cancelButton = wrapper.find('button.btn-default');
+    const cancelButton = wrapper.find('button.btn-secondary');
     cancelButton.simulate('click');
 
     expect(props.cancel.calledOnce).toStrictEqual(true);
diff --git a/ui/components/app/transaction-list-item-details/index.scss b/ui/components/app/transaction-list-item-details/index.scss
index 4ad7cd094..27a64f2e9 100644
--- a/ui/components/app/transaction-list-item-details/index.scss
+++ b/ui/components/app/transaction-list-item-details/index.scss
@@ -17,7 +17,12 @@
   }
 
   & &__header-button {
-    @include H8;
+    &-rounded-button {
+      @include H8;
+
+      padding: 0 16px;
+      margin-right: 8px;
+    }
 
     &-tooltip-container {
       display: flex !important;
diff --git a/ui/components/app/transaction-list-item-details/transaction-list-item-details.component.js b/ui/components/app/transaction-list-item-details/transaction-list-item-details.component.js
index 75f637e4d..a940cfcaa 100644
--- a/ui/components/app/transaction-list-item-details/transaction-list-item-details.component.js
+++ b/ui/components/app/transaction-list-item-details/transaction-list-item-details.component.js
@@ -147,9 +147,9 @@ export default class TransactionListItemDetails extends PureComponent {
             <div className="transaction-list-item-details__header-buttons">
               {showSpeedUp && (
                 <Button
-                  type="raised"
+                  type="primary"
                   onClick={this.handleRetry}
-                  className="transaction-list-item-details__header-button"
+                  className="transaction-list-item-details__header-button-rounded-button"
                 >
                   {t('speedUp')}
                 </Button>
diff --git a/ui/components/app/transaction-list-item/transaction-list-item.component.js b/ui/components/app/transaction-list-item/transaction-list-item.component.js
index 4f7210d16..88227e7d0 100644
--- a/ui/components/app/transaction-list-item/transaction-list-item.component.js
+++ b/ui/components/app/transaction-list-item/transaction-list-item.component.js
@@ -122,8 +122,7 @@ export default function TransactionListItem({
     }
     return (
       <Button
-        type="secondary"
-        rounded
+        type="primary"
         onClick={hasCancelled ? cancelTransaction : retryTransaction}
         style={hasCancelled ? { width: 'auto' } : null}
       >
diff --git a/ui/components/app/transaction-list/transaction-list.component.js b/ui/components/app/transaction-list/transaction-list.component.js
index 7a86e1513..2660be340 100644
--- a/ui/components/app/transaction-list/transaction-list.component.js
+++ b/ui/components/app/transaction-list/transaction-list.component.js
@@ -157,7 +157,6 @@ export default function TransactionList({
             <Button
               className="transaction-list__view-more"
               type="secondary"
-              rounded
               onClick={viewMore}
             >
               {t('viewMore')}
diff --git a/ui/components/app/whats-new-popup/whats-new-popup.js b/ui/components/app/whats-new-popup/whats-new-popup.js
index 9b944135a..06233371a 100644
--- a/ui/components/app/whats-new-popup/whats-new-popup.js
+++ b/ui/components/app/whats-new-popup/whats-new-popup.js
@@ -103,7 +103,6 @@ const renderFirstNotification = (notification, idRefMap, history, isLast) => {
         <Button
           type="secondary"
           className="whats-new-popup__button"
-          rounded
           onClick={actionFunction}
         >
           {actionText}
diff --git a/ui/components/ui/button/button.component.js b/ui/components/ui/button/button.component.js
index c994bb311..97c7bdd23 100644
--- a/ui/components/ui/button/button.component.js
+++ b/ui/components/ui/button/button.component.js
@@ -31,7 +31,6 @@ const Button = ({
   large,
   children,
   icon,
-  rounded,
   className,
   ...buttonProps
 }) => {
@@ -40,8 +39,10 @@ const Button = ({
   // we know to be erroneous attributes for a link. We will likely want to extract Link
   // to its own component in the future.
   let Tag = 'button';
+  let rounded = true;
   if (type === 'link') {
     Tag = 'a';
+    rounded = false;
   } else if (submit) {
     buttonProps.type = 'submit';
   }
@@ -58,9 +59,9 @@ const Button = ({
     <Tag
       className={classnames(
         'button',
+        rounded && CLASSNAME_ROUNDED,
         typeHash[type] || CLASSNAME_DEFAULT,
         large && CLASSNAME_LARGE,
-        rounded && CLASSNAME_ROUNDED,
         className,
       )}
       {...buttonProps}
@@ -75,7 +76,6 @@ Button.propTypes = {
   type: PropTypes.string,
   submit: PropTypes.bool,
   large: PropTypes.bool,
-  rounded: PropTypes.bool,
   className: PropTypes.string,
   children: PropTypes.node,
   icon: PropTypes.node,
diff --git a/ui/components/ui/page-container/page-container-footer/page-container-footer.component.js b/ui/components/ui/page-container/page-container-footer/page-container-footer.component.js
index cc5384817..4bc576f50 100644
--- a/ui/components/ui/page-container/page-container-footer/page-container-footer.component.js
+++ b/ui/components/ui/page-container/page-container-footer/page-container-footer.component.js
@@ -44,7 +44,7 @@ export default class PageContainerFooter extends Component {
         <footer>
           {!hideCancel && (
             <Button
-              type={cancelButtonType || 'default'}
+              type={cancelButtonType || 'secondary'}
               large={buttonSizeLarge}
               className={classnames(
                 'page-container__footer-button',
@@ -58,7 +58,7 @@ export default class PageContainerFooter extends Component {
           )}
 
           <Button
-            type={submitButtonType || 'secondary'}
+            type={submitButtonType || 'primary'}
             large={buttonSizeLarge}
             className={classnames(
               'page-container__footer-button',
diff --git a/ui/components/ui/page-container/page-container-footer/page-container-footer.component.test.js b/ui/components/ui/page-container/page-container-footer/page-container-footer.component.test.js
index c24fcd372..0cf5d6f93 100644
--- a/ui/components/ui/page-container/page-container-footer/page-container-footer.component.test.js
+++ b/ui/components/ui/page-container/page-container-footer/page-container-footer.component.test.js
@@ -45,7 +45,7 @@ describe('Page Footer', () => {
     it('has button type of default', () => {
       expect(
         wrapper.find('.page-container__footer-button').first().prop('type'),
-      ).toStrictEqual('default');
+      ).toStrictEqual('secondary');
     });
 
     it('has children text of Cancel', () => {
diff --git a/ui/components/ui/page-container/page-container.component.js b/ui/components/ui/page-container/page-container.component.js
index 28b5f75b0..a7bee1fe6 100644
--- a/ui/components/ui/page-container/page-container.component.js
+++ b/ui/components/ui/page-container/page-container.component.js
@@ -143,7 +143,6 @@ export default class PageContainer extends PureComponent {
             onSubmit={onSubmit}
             submitText={tabSubmitText || submitText}
             disabled={disabled}
-            submitButtonType="primary"
           />
         </div>
       </div>
diff --git a/ui/components/ui/truncated-definition-list/truncated-definition-list.js b/ui/components/ui/truncated-definition-list/truncated-definition-list.js
index 2382b045b..f7354cffd 100644
--- a/ui/components/ui/truncated-definition-list/truncated-definition-list.js
+++ b/ui/components/ui/truncated-definition-list/truncated-definition-list.js
@@ -49,7 +49,6 @@ export default function TruncatedDefinitionList({
               <Button
                 type="primary"
                 style={{ width: '50%' }}
-                rounded
                 onClick={() => setIsPopoverOpen(false)}
               >
                 Close
diff --git a/ui/css/itcss/components/send.scss b/ui/css/itcss/components/send.scss
index 9874b09ab..ef44990cd 100644
--- a/ui/css/itcss/components/send.scss
+++ b/ui/css/itcss/components/send.scss
@@ -785,13 +785,14 @@
     width: 56px;
     height: 20px;
     margin-top: 5px;
+    border-radius: 100px;
 
     &__button {
       width: 56px;
       height: 20px;
       position: absolute;
       border: 1px solid #b0d7f2;
-      border-radius: 6px;
+      border-radius: 100px;
       cursor: pointer;
       top: 0;
       left: 0;
diff --git a/ui/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js b/ui/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js
index ec8d163aa..5e222c002 100644
--- a/ui/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js
+++ b/ui/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js
@@ -125,7 +125,7 @@ export default class ConfirmAddSuggestedToken extends Component {
         <div className="page-container__footer">
           <footer>
             <Button
-              type="default"
+              type="secondary"
               large
               className="page-container__footer-button"
               onClick={async () => {
@@ -138,7 +138,7 @@ export default class ConfirmAddSuggestedToken extends Component {
               {this.context.t('cancel')}
             </Button>
             <Button
-              type="secondary"
+              type="primary"
               large
               className="page-container__footer-button"
               disabled={suggestedAssets.length === 0}
diff --git a/ui/pages/confirm-decrypt-message/confirm-decrypt-message.component.js b/ui/pages/confirm-decrypt-message/confirm-decrypt-message.component.js
index 7f4d5e0ff..29455db4e 100644
--- a/ui/pages/confirm-decrypt-message/confirm-decrypt-message.component.js
+++ b/ui/pages/confirm-decrypt-message/confirm-decrypt-message.component.js
@@ -296,7 +296,7 @@ export default class ConfirmDecryptMessage extends Component {
     return (
       <div className="request-decrypt-message__footer">
         <Button
-          type="default"
+          type="secondary"
           large
           className="request-decrypt-message__footer__cancel-button"
           onClick={async (event) => {
@@ -316,7 +316,7 @@ export default class ConfirmDecryptMessage extends Component {
           {t('cancel')}
         </Button>
         <Button
-          type="secondary"
+          type="primary"
           large
           className="request-decrypt-message__footer__sign-button"
           onClick={async (event) => {
diff --git a/ui/pages/confirm-encryption-public-key/confirm-encryption-public-key.component.js b/ui/pages/confirm-encryption-public-key/confirm-encryption-public-key.component.js
index 3be8d21ff..cf31ee051 100644
--- a/ui/pages/confirm-encryption-public-key/confirm-encryption-public-key.component.js
+++ b/ui/pages/confirm-encryption-public-key/confirm-encryption-public-key.component.js
@@ -199,7 +199,7 @@ export default class ConfirmEncryptionPublicKey extends Component {
     return (
       <div className="request-encryption-public-key__footer">
         <Button
-          type="default"
+          type="secondary"
           large
           className="request-encryption-public-key__footer__cancel-button"
           onClick={async (event) => {
@@ -219,7 +219,7 @@ export default class ConfirmEncryptionPublicKey extends Component {
           {this.context.t('cancel')}
         </Button>
         <Button
-          type="secondary"
+          type="primary"
           large
           className="request-encryption-public-key__footer__sign-button"
           onClick={async (event) => {
diff --git a/ui/pages/confirm-import-token/confirm-import-token.component.js b/ui/pages/confirm-import-token/confirm-import-token.component.js
index 5b6a9a95a..b64baacec 100644
--- a/ui/pages/confirm-import-token/confirm-import-token.component.js
+++ b/ui/pages/confirm-import-token/confirm-import-token.component.js
@@ -94,7 +94,7 @@ export default class ConfirmImportToken extends Component {
         <div className="page-container__footer">
           <footer>
             <Button
-              type="default"
+              type="secondary"
               large
               className="page-container__footer-button"
               onClick={() => history.push(IMPORT_TOKEN_ROUTE)}
@@ -102,7 +102,7 @@ export default class ConfirmImportToken extends Component {
               {this.context.t('back')}
             </Button>
             <Button
-              type="secondary"
+              type="primary"
               large
               className="page-container__footer-button"
               onClick={() => {
diff --git a/ui/pages/confirmation/components/confirmation-footer/confirmation-footer.js b/ui/pages/confirmation/components/confirmation-footer/confirmation-footer.js
index 9574a13a4..7a73d42b7 100644
--- a/ui/pages/confirmation/components/confirmation-footer/confirmation-footer.js
+++ b/ui/pages/confirmation/components/confirmation-footer/confirmation-footer.js
@@ -13,10 +13,10 @@ export default function ConfirmationFooter({
     <div className="confirmation-footer">
       {alerts}
       <div className="confirmation-footer__actions">
-        <Button rounded type="secondary" onClick={onCancel}>
+        <Button type="secondary" onClick={onCancel}>
           {cancelText}
         </Button>
-        <Button rounded type="primary" onClick={onApprove}>
+        <Button type="primary" onClick={onApprove}>
           {approveText}
         </Button>
       </div>
diff --git a/ui/pages/create-account/connect-hardware/account-list.js b/ui/pages/create-account/connect-hardware/account-list.js
index 96a31bd05..29922fcd2 100644
--- a/ui/pages/create-account/connect-hardware/account-list.js
+++ b/ui/pages/create-account/connect-hardware/account-list.js
@@ -194,7 +194,7 @@ class AccountList extends Component {
     return (
       <div className="new-external-account-form__buttons">
         <Button
-          type="default"
+          type="secondary"
           large
           className="new-external-account-form__button"
           onClick={this.props.onCancel.bind(this)}
diff --git a/ui/pages/create-account/import-account/json.js b/ui/pages/create-account/import-account/json.js
index 1aa35c493..bd14d6a2e 100644
--- a/ui/pages/create-account/import-account/json.js
+++ b/ui/pages/create-account/import-account/json.js
@@ -57,7 +57,7 @@ class JsonImportSubview extends Component {
         />
         <div className="new-account-create-form__buttons">
           <Button
-            type="default"
+            type="secondary"
             large
             className="new-account-create-form__button"
             onClick={() => history.push(mostRecentOverviewPage)}
@@ -65,7 +65,7 @@ class JsonImportSubview extends Component {
             {this.context.t('cancel')}
           </Button>
           <Button
-            type="secondary"
+            type="primary"
             large
             className="new-account-create-form__button"
             onClick={() => this.createNewKeychain()}
diff --git a/ui/pages/create-account/import-account/private-key.js b/ui/pages/create-account/import-account/private-key.js
index 507fb09f6..08495cf23 100644
--- a/ui/pages/create-account/import-account/private-key.js
+++ b/ui/pages/create-account/import-account/private-key.js
@@ -104,7 +104,7 @@ class PrivateKeyImportView extends Component {
         </div>
         <div className="new-account-import-form__buttons">
           <Button
-            type="default"
+            type="secondary"
             large
             className="new-account-create-form__button"
             onClick={() => {
@@ -116,7 +116,7 @@ class PrivateKeyImportView extends Component {
             {this.context.t('cancel')}
           </Button>
           <Button
-            type="secondary"
+            type="primary"
             large
             className="new-account-create-form__button"
             onClick={() => this.createNewKeychain()}
diff --git a/ui/pages/create-account/new-account.component.js b/ui/pages/create-account/new-account.component.js
index 252eb2821..112b6ba08 100644
--- a/ui/pages/create-account/new-account.component.js
+++ b/ui/pages/create-account/new-account.component.js
@@ -60,7 +60,7 @@ export default class NewAccountCreateForm extends Component {
           />
           <div className="new-account-create-form__buttons">
             <Button
-              type="default"
+              type="secondary"
               large
               className="new-account-create-form__button"
               onClick={() => history.push(mostRecentOverviewPage)}
@@ -68,7 +68,7 @@ export default class NewAccountCreateForm extends Component {
               {this.context.t('cancel')}
             </Button>
             <Button
-              type="secondary"
+              type="primary"
               large
               className="new-account-create-form__button"
               onClick={createClick}
diff --git a/ui/pages/first-time-flow/metametrics-opt-in/metametrics-opt-in.component.js b/ui/pages/first-time-flow/metametrics-opt-in/metametrics-opt-in.component.js
index 2ba9a4648..97ba3cd47 100644
--- a/ui/pages/first-time-flow/metametrics-opt-in/metametrics-opt-in.component.js
+++ b/ui/pages/first-time-flow/metametrics-opt-in/metametrics-opt-in.component.js
@@ -166,7 +166,6 @@ export default class MetaMetricsOptIn extends Component {
                 }
               }}
               submitText={t('affirmAgree')}
-              submitButtonType="primary"
               disabled={false}
             />
             <div className="metametrics-opt-in__bottom-text">
diff --git a/ui/pages/first-time-flow/metametrics-opt-in/metametrics-opt-in.test.js b/ui/pages/first-time-flow/metametrics-opt-in/metametrics-opt-in.test.js
index 4a83441b0..5f60c8d37 100644
--- a/ui/pages/first-time-flow/metametrics-opt-in/metametrics-opt-in.test.js
+++ b/ui/pages/first-time-flow/metametrics-opt-in/metametrics-opt-in.test.js
@@ -21,7 +21,7 @@ describe('MetaMetricsOptIn', () => {
       store,
     );
     const noThanksButton = wrapper.find(
-      '.btn-default.page-container__footer-button',
+      '.btn-secondary.page-container__footer-button',
     );
     noThanksButton.simulate('click');
 
diff --git a/ui/pages/keychains/index.scss b/ui/pages/keychains/index.scss
index a38d6a5ab..2ad54763a 100644
--- a/ui/pages/keychains/index.scss
+++ b/ui/pages/keychains/index.scss
@@ -185,3 +185,8 @@
     padding-top: 5px;
   }
 }
+
+.page-container__footer-single-button {
+  width: 40%;
+  margin: 8px auto;
+}
diff --git a/ui/pages/keychains/restore-vault.js b/ui/pages/keychains/restore-vault.js
index a8ba320ca..96d260911 100644
--- a/ui/pages/keychains/restore-vault.js
+++ b/ui/pages/keychains/restore-vault.js
@@ -239,7 +239,7 @@ class RestoreVaultPage extends Component {
               largeLabel
             />
             <Button
-              type="first-time"
+              type="primary"
               className="first-time-flow__button"
               onClick={() => !disabled && this.onClick()}
               disabled={disabled}
diff --git a/ui/pages/keychains/reveal-seed.js b/ui/pages/keychains/reveal-seed.js
index f9ac0c105..ea3005a9a 100644
--- a/ui/pages/keychains/reveal-seed.js
+++ b/ui/pages/keychains/reveal-seed.js
@@ -114,7 +114,7 @@ class RevealSeedPage extends Component {
       <div className="page-container__footer">
         <footer>
           <Button
-            type="default"
+            type="secondary"
             large
             className="page-container__footer-button"
             onClick={() =>
@@ -124,7 +124,7 @@ class RevealSeedPage extends Component {
             {this.context.t('cancel')}
           </Button>
           <Button
-            type="secondary"
+            type="primary"
             large
             className="page-container__footer-button"
             onClick={(event) => this.handleSubmit(event)}
@@ -141,9 +141,9 @@ class RevealSeedPage extends Component {
     return (
       <div className="page-container__footer">
         <Button
-          type="default"
+          type="secondary"
           large
-          className="page-container__footer-button"
+          className="page-container__footer-single-button"
           onClick={() =>
             this.props.history.push(this.props.mostRecentOverviewPage)
           }
diff --git a/ui/pages/mobile-sync/mobile-sync.component.js b/ui/pages/mobile-sync/mobile-sync.component.js
index bb61df1ae..45f37a2ff 100644
--- a/ui/pages/mobile-sync/mobile-sync.component.js
+++ b/ui/pages/mobile-sync/mobile-sync.component.js
@@ -408,10 +408,10 @@ export default class MobileSyncPage extends Component {
     return (
       <div
         className="new-account-import-form__buttons"
-        style={{ padding: 30, marginTop: 0 }}
+        style={{ padding: '30px 15px 30px 15px', marginTop: 0 }}
       >
         <Button
-          type="default"
+          type="secondary"
           large
           className="new-account-create-form__button"
           onClick={() => this.goBack()}
@@ -419,7 +419,7 @@ export default class MobileSyncPage extends Component {
           {t('cancel')}
         </Button>
         <Button
-          type="secondary"
+          type="primary"
           large
           className="new-account-create-form__button"
           onClick={(event) => this.handleSubmit(event)}
@@ -437,7 +437,7 @@ export default class MobileSyncPage extends Component {
     return (
       <div className="page-container__footer" style={{ padding: 30 }}>
         <Button
-          type="default"
+          type="secondary"
           large
           className="page-container__footer-button"
           onClick={() => this.goBack()}
diff --git a/ui/pages/onboarding-flow/new-account/new-account.js b/ui/pages/onboarding-flow/new-account/new-account.js
index 2eb351ff8..cc6ebed7e 100644
--- a/ui/pages/onboarding-flow/new-account/new-account.js
+++ b/ui/pages/onboarding-flow/new-account/new-account.js
@@ -169,7 +169,6 @@ export default function NewAccount({ onSubmit }) {
             className="new-account__form--submit-button"
             disabled={!isValid || !termsChecked}
             onClick={handleCreate}
-            rounded
           >
             {t('createNewWallet')}
           </Button>
diff --git a/ui/pages/onboarding-flow/recovery-phrase/confirm-recovery-phrase.js b/ui/pages/onboarding-flow/recovery-phrase/confirm-recovery-phrase.js
index 52d3926e1..2828b5fd7 100644
--- a/ui/pages/onboarding-flow/recovery-phrase/confirm-recovery-phrase.js
+++ b/ui/pages/onboarding-flow/recovery-phrase/confirm-recovery-phrase.js
@@ -80,7 +80,6 @@ export default function ConfirmRecoveryPhrase({ seedPhrase = '' }) {
       />
       <div className="recovery-phrase__footer">
         <Button
-          rounded
           type="primary"
           className="recovery-phrase__footer--button"
           onClick={() => {
diff --git a/ui/pages/onboarding-flow/recovery-phrase/review-recovery-phrase.js b/ui/pages/onboarding-flow/recovery-phrase/review-recovery-phrase.js
index 78f8437dc..fae50a3f1 100644
--- a/ui/pages/onboarding-flow/recovery-phrase/review-recovery-phrase.js
+++ b/ui/pages/onboarding-flow/recovery-phrase/review-recovery-phrase.js
@@ -92,7 +92,6 @@ export default function RecoveryPhrase({ seedPhrase }) {
               {copied ? t('copiedExclamation') : t('copyToClipboard')}
             </Button>
             <Button
-              rounded
               type="primary"
               className="recovery-phrase__footer--button"
               onClick={() => {
@@ -104,7 +103,6 @@ export default function RecoveryPhrase({ seedPhrase }) {
           </div>
         ) : (
           <Button
-            rounded
             type="primary"
             className="recovery-phrase__footer--button"
             onClick={() => {
diff --git a/ui/pages/permissions-connect/choose-account/choose-account.component.js b/ui/pages/permissions-connect/choose-account/choose-account.component.js
index 8e245e62e..d4fc98216 100644
--- a/ui/pages/permissions-connect/choose-account/choose-account.component.js
+++ b/ui/pages/permissions-connect/choose-account/choose-account.component.js
@@ -224,7 +224,7 @@ export default class ChooseAccount extends Component {
           <div className="permissions-connect-choose-account__bottom-buttons">
             <Button
               onClick={() => cancelPermissionsRequest(permissionsRequestId)}
-              type="default"
+              type="secondary"
             >
               {t('cancel')}
             </Button>
diff --git a/ui/pages/permissions-connect/choose-account/index.scss b/ui/pages/permissions-connect/choose-account/index.scss
index d02f5136b..4fcd11e70 100644
--- a/ui/pages/permissions-connect/choose-account/index.scss
+++ b/ui/pages/permissions-connect/choose-account/index.scss
@@ -179,7 +179,7 @@
       width: 124px;
     }
 
-    .btn-default {
+    .btn-secondary {
       background: white;
       margin-left: 16px;
     }
diff --git a/ui/pages/permissions-connect/permissions-connect.stories.js b/ui/pages/permissions-connect/permissions-connect.stories.js
index 6eff21540..87af63ee2 100644
--- a/ui/pages/permissions-connect/permissions-connect.stories.js
+++ b/ui/pages/permissions-connect/permissions-connect.stories.js
@@ -74,7 +74,6 @@ export const PermissionPageContainerComponent = () => {
           cancelButtonType="default"
           onSubmit={action('Account(s) Connected')}
           submitText="connect"
-          submitButtonType="confirm"
           buttonSizeLarge={false}
         />
       </div>
diff --git a/ui/pages/settings/contact-list-tab/add-contact/add-contact.component.js b/ui/pages/settings/contact-list-tab/add-contact/add-contact.component.js
index 6ce7f06f0..3ed6296d6 100644
--- a/ui/pages/settings/contact-list-tab/add-contact/add-contact.component.js
+++ b/ui/pages/settings/contact-list-tab/add-contact/add-contact.component.js
@@ -153,7 +153,6 @@ export default class AddContact extends PureComponent {
             history.push(CONTACT_LIST_ROUTE);
           }}
           submitText={this.context.t('save')}
-          submitButtonType="confirm"
         />
       </div>
     );
diff --git a/ui/pages/settings/contact-list-tab/contact-list-tab.component.js b/ui/pages/settings/contact-list-tab/contact-list-tab.component.js
index 2f4b135f0..0f315517f 100644
--- a/ui/pages/settings/contact-list-tab/contact-list-tab.component.js
+++ b/ui/pages/settings/contact-list-tab/contact-list-tab.component.js
@@ -80,7 +80,6 @@ export default class ContactListTab extends Component {
               viewingContact || editingContact,
           })}
           type="secondary"
-          rounded
           onClick={() => {
             history.push(CONTACT_ADD_ROUTE);
           }}
diff --git a/ui/pages/settings/contact-list-tab/edit-contact/edit-contact.component.js b/ui/pages/settings/contact-list-tab/edit-contact/edit-contact.component.js
index 51419b302..0f64dfa15 100644
--- a/ui/pages/settings/contact-list-tab/edit-contact/edit-contact.component.js
+++ b/ui/pages/settings/contact-list-tab/edit-contact/edit-contact.component.js
@@ -177,7 +177,6 @@ export default class EditContact extends PureComponent {
             history.push(`${viewRoute}/${address}`);
           }}
           submitText={this.context.t('save')}
-          submitButtonType="confirm"
         />
       </div>
     );
diff --git a/ui/pages/settings/contact-list-tab/index.scss b/ui/pages/settings/contact-list-tab/index.scss
index 4084afa0f..8f00641b6 100644
--- a/ui/pages/settings/contact-list-tab/index.scss
+++ b/ui/pages/settings/contact-list-tab/index.scss
@@ -257,7 +257,6 @@
     top: 85px;
     right: 16px;
     width: auto;
-    border-radius: 100px;
 
     @media screen and (max-width: $break-small) {
       top: 16px;
diff --git a/ui/pages/settings/networks-tab/index.scss b/ui/pages/settings/networks-tab/index.scss
index 40a182264..d8da8cf79 100644
--- a/ui/pages/settings/networks-tab/index.scss
+++ b/ui/pages/settings/networks-tab/index.scss
@@ -232,11 +232,11 @@
       width: 93%;
     }
 
-    .btn-default {
+    .btn-secondary {
       margin-right: 0.375rem;
     }
 
-    .btn-secondary {
+    .btn-primary {
       margin-left: 0.375rem;
     }
 
diff --git a/ui/pages/settings/networks-tab/network-form/network-form.component.js b/ui/pages/settings/networks-tab/network-form/network-form.component.js
index e2968001d..2e722031e 100644
--- a/ui/pages/settings/networks-tab/network-form/network-form.component.js
+++ b/ui/pages/settings/networks-tab/network-form/network-form.component.js
@@ -629,14 +629,14 @@ export default class NetworkForm extends PureComponent {
                 </Button>
               )}
               <Button
-                type="default"
+                type="secondary"
                 onClick={this.onCancel}
                 disabled={this.stateIsUnchanged()}
               >
                 {t('cancel')}
               </Button>
               <Button
-                type="secondary"
+                type="primary"
                 disabled={isSubmitDisabled}
                 onClick={this.onSubmit}
               >
diff --git a/ui/pages/settings/networks-tab/networks-tab.component.js b/ui/pages/settings/networks-tab/networks-tab.component.js
index f42591dd6..a7ab9e24a 100644
--- a/ui/pages/settings/networks-tab/networks-tab.component.js
+++ b/ui/pages/settings/networks-tab/networks-tab.component.js
@@ -56,7 +56,7 @@ export default class NetworksTab extends PureComponent {
         </span>
         <div className="networks-tab__add-network-header-button-wrapper">
           <Button
-            type="secondary"
+            type="primary"
             onClick={(event) => {
               event.preventDefault();
               setSelectedSettingsRpcUrl('');
diff --git a/ui/pages/swaps/import-token/import-token.js b/ui/pages/swaps/import-token/import-token.js
index 9be183878..88a0645a8 100644
--- a/ui/pages/swaps/import-token/import-token.js
+++ b/ui/pages/swaps/import-token/import-token.js
@@ -27,15 +27,13 @@ export default function ImportToken({
         type="secondary"
         className="page-container__footer-button"
         onClick={onImportTokenCloseClick}
-        rounded
       >
         {t('cancel')}
       </Button>
       <Button
-        type="confirm"
+        type="primary"
         className="page-container__footer-button"
         onClick={onImportTokenClick}
-        rounded
       >
         {t('import')}
       </Button>
diff --git a/ui/pages/swaps/searchable-item-list/item-list/item-list.component.js b/ui/pages/swaps/searchable-item-list/item-list/item-list.component.js
index 13ee5afa8..d74b50460 100644
--- a/ui/pages/swaps/searchable-item-list/item-list/item-list.component.js
+++ b/ui/pages/swaps/searchable-item-list/item-list/item-list.component.js
@@ -139,7 +139,7 @@ export default function ItemList({
                   )}
               </div>
               {result.notImported && (
-                <Button type="confirm" onClick={onClick} rounded>
+                <Button type="confirm" onClick={onClick}>
                   {t('import')}
                 </Button>
               )}
diff --git a/ui/pages/swaps/select-quote-popover/index.scss b/ui/pages/swaps/select-quote-popover/index.scss
index ad305a3f8..e2a3a2cc7 100644
--- a/ui/pages/swaps/select-quote-popover/index.scss
+++ b/ui/pages/swaps/select-quote-popover/index.scss
@@ -2,7 +2,6 @@
 
 .select-quote-popover {
   &__button {
-    border-radius: 100px;
     height: 39px;
     width: 140px;
   }
diff --git a/ui/pages/swaps/select-quote-popover/select-quote-popover.js b/ui/pages/swaps/select-quote-popover/select-quote-popover.js
index c3efcba48..c4f9e8977 100644
--- a/ui/pages/swaps/select-quote-popover/select-quote-popover.js
+++ b/ui/pages/swaps/select-quote-popover/select-quote-popover.js
@@ -57,7 +57,7 @@ const SelectQuotePopover = ({
   const footer = (
     <>
       <Button
-        type="default"
+        type="secondary"
         className="page-container__footer-button select-quote-popover__button"
         onClick={onClose}
       >
@@ -65,7 +65,7 @@ const SelectQuotePopover = ({
       </Button>
 
       <Button
-        type="confirm"
+        type="primary"
         className="page-container__footer-button select-quote-popover__button"
         onClick={onSubmitClick}
       >
diff --git a/ui/pages/swaps/swaps-footer/__snapshots__/swaps-footer.test.js.snap b/ui/pages/swaps/swaps-footer/__snapshots__/swaps-footer.test.js.snap
index 3edd2af66..410501e81 100644
--- a/ui/pages/swaps/swaps-footer/__snapshots__/swaps-footer.test.js.snap
+++ b/ui/pages/swaps/swaps-footer/__snapshots__/swaps-footer.test.js.snap
@@ -13,7 +13,7 @@ exports[`SwapsFooter renders the component with initial props 1`] = `
       >
         <footer>
           <button
-            class="button btn-default page-container__footer-button swaps-footer__custom-page-container-footer-button-class"
+            class="button btn--rounded btn-secondary page-container__footer-button swaps-footer__custom-page-container-footer-button-class"
             data-testid="page-container-footer-cancel"
             role="button"
             tabindex="0"
@@ -21,7 +21,7 @@ exports[`SwapsFooter renders the component with initial props 1`] = `
             Back
           </button>
           <button
-            class="button btn-primary page-container__footer-button swaps-footer__custom-page-container-footer-button-class"
+            class="button btn--rounded btn-primary page-container__footer-button swaps-footer__custom-page-container-footer-button-class"
             data-testid="page-container-footer-next"
             role="button"
             tabindex="0"
diff --git a/ui/pages/swaps/swaps-footer/swaps-footer.js b/ui/pages/swaps/swaps-footer/swaps-footer.js
index faefa85db..18192bfe1 100644
--- a/ui/pages/swaps/swaps-footer/swaps-footer.js
+++ b/ui/pages/swaps/swaps-footer/swaps-footer.js
@@ -30,7 +30,6 @@ export default function SwapsFooter({
           cancelText={t('back')}
           onSubmit={onSubmit}
           submitText={submitText}
-          submitButtonType="confirm"
           footerClassName={classnames(
             'swaps-footer__custom-page-container-footer-class',
             className,
diff --git a/ui/pages/unlock-page/index.scss b/ui/pages/unlock-page/index.scss
index 4ffd8d24a..aaad02b85 100644
--- a/ui/pages/unlock-page/index.scss
+++ b/ui/pages/unlock-page/index.scss
@@ -41,6 +41,7 @@
 
     button {
       font-weight: bold;
+      border-radius: 100px;
     }
   }
 
diff --git a/ui/pages/unlock-page/unlock-page.component.js b/ui/pages/unlock-page/unlock-page.component.js
index 32935696f..907cda27c 100644
--- a/ui/pages/unlock-page/unlock-page.component.js
+++ b/ui/pages/unlock-page/unlock-page.component.js
@@ -1,8 +1,8 @@
 import { EventEmitter } from 'events';
 import React, { Component } from 'react';
 import PropTypes from 'prop-types';
-import Button from '@material-ui/core/Button';
 import getCaretCoordinates from 'textarea-caret';
+import Button from '../../components/ui/button';
 import TextField from '../../components/ui/text-field';
 import Mascot from '../../components/ui/mascot';
 import { DEFAULT_ROUTE } from '../../helpers/constants/routes';
@@ -115,7 +115,7 @@ export default class UnlockPage extends Component {
       height: '60px',
       fontWeight: '400',
       boxShadow: 'none',
-      borderRadius: '4px',
+      borderRadius: '100px',
     };
 
     return (

From 3985a65e36aef74198c7d7dc401346149dc27365 Mon Sep 17 00:00:00 2001
From: Alex Donesky <adonesky@gmail.com>
Date: Tue, 5 Oct 2021 15:38:16 -0500
Subject: [PATCH 41/56] Onboarding V2 App Header (#12264)

* Add onboarding app header
---
 .../ui/metafox-logo/metafox-logo.component.js | 23 ++++---
 ui/pages/onboarding-flow/index.scss           |  1 +
 .../onboarding-app-header/index.scss          | 67 +++++++++++++++++++
 .../onboarding-app-header.js                  | 34 ++++++++++
 ui/pages/onboarding-flow/onboarding-flow.js   |  2 +-
 .../recovery-phrase/index.scss                |  4 +-
 .../recovery-phrase/review-recovery-phrase.js |  2 +-
 ui/pages/routes/routes.component.js           | 15 +++++
 8 files changed, 135 insertions(+), 13 deletions(-)
 create mode 100644 ui/pages/onboarding-flow/onboarding-app-header/index.scss
 create mode 100644 ui/pages/onboarding-flow/onboarding-app-header/onboarding-app-header.js

diff --git a/ui/components/ui/metafox-logo/metafox-logo.component.js b/ui/components/ui/metafox-logo/metafox-logo.component.js
index cdebd67c0..4a3812dd5 100644
--- a/ui/components/ui/metafox-logo/metafox-logo.component.js
+++ b/ui/components/ui/metafox-logo/metafox-logo.component.js
@@ -8,6 +8,7 @@ export default class MetaFoxLogo extends PureComponent {
     onClick: PropTypes.func,
     unsetIconHeight: PropTypes.bool,
     useDark: PropTypes.bool,
+    isOnboarding: PropTypes.bool,
   };
 
   static defaultProps = {
@@ -16,13 +17,15 @@ export default class MetaFoxLogo extends PureComponent {
   };
 
   render() {
-    const { onClick, unsetIconHeight, useDark } = this.props;
+    const { onClick, unsetIconHeight, useDark, isOnboarding } = this.props;
     const iconProps = unsetIconHeight ? {} : { height: 42, width: 42 };
 
     return (
       <div
         onClick={onClick}
-        className={classnames('app-header__logo-container', {
+        className={classnames({
+          'app-header__logo-container': !isOnboarding,
+          'onboarding-app-header__logo-container': isOnboarding,
           'app-header__logo-container--clickable': Boolean(onClick),
         })}
       >
@@ -33,19 +36,19 @@ export default class MetaFoxLogo extends PureComponent {
               ? getBuildSpecificAsset('metafoxLogoHorizontalDark')
               : './images/logo/metamask-logo-horizontal.svg'
           }
-          className={classnames(
-            'app-header__metafox-logo',
-            'app-header__metafox-logo--horizontal',
-          )}
+          className={classnames({
+            'app-header__metafox-logo--horizontal': !isOnboarding,
+            'onboarding-app-header__metafox-logo--horizontal': isOnboarding,
+          })}
           alt=""
         />
         <img
           {...iconProps}
           src="./images/logo/metamask-fox.svg"
-          className={classnames(
-            'app-header__metafox-logo',
-            'app-header__metafox-logo--icon',
-          )}
+          className={classnames({
+            'app-header__metafox-logo--icon': !isOnboarding,
+            'onboarding-app-header__metafox-logo--icon': isOnboarding,
+          })}
           alt=""
         />
       </div>
diff --git a/ui/pages/onboarding-flow/index.scss b/ui/pages/onboarding-flow/index.scss
index aebae2fbd..8e3431e05 100644
--- a/ui/pages/onboarding-flow/index.scss
+++ b/ui/pages/onboarding-flow/index.scss
@@ -1,5 +1,6 @@
 @import 'recovery-phrase/index';
 @import 'new-account/index';
+@import 'onboarding-app-header/index';
 
 .onboarding-flow {
   width: 100%;
diff --git a/ui/pages/onboarding-flow/onboarding-app-header/index.scss b/ui/pages/onboarding-flow/onboarding-app-header/index.scss
new file mode 100644
index 000000000..68202b7fb
--- /dev/null
+++ b/ui/pages/onboarding-flow/onboarding-app-header/index.scss
@@ -0,0 +1,67 @@
+.onboarding-app-header {
+  align-items: center;
+  background: $white;
+  position: relative;
+  z-index: $header-z-index;
+  display: flex;
+  flex-flow: column nowrap;
+  width: 100%;
+  flex: 0 0 auto;
+
+  @media screen and (max-width: $break-small) {
+    padding: 1rem;
+    z-index: $mobile-header-z-index;
+  }
+
+  @media screen and (min-width: $break-large) {
+    height: 75px;
+    justify-content: center;
+  }
+
+  &__metafox-logo {
+    &--icon {
+      height: 32px;
+
+      @media screen and (min-width: $break-large) {
+        display: none;
+      }
+    }
+
+    &--horizontal {
+      @media screen and (max-width: $break-small) {
+        display: none;
+      }
+    }
+  }
+
+  &__contents {
+    display: flex;
+    flex-flow: row nowrap;
+    width: 100%;
+    justify-content: space-between;
+
+    @media screen and (max-width: $break-small) {
+      height: 100%;
+    }
+
+    @media screen and (min-width: $break-large) {
+      width: 85vw;
+    }
+
+    @media screen and (min-width: 769px) {
+      width: 90vw;
+    }
+
+    @media screen and (min-width: 1281px) {
+      width: 62vw;
+    }
+  }
+
+  &__logo-container {
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    flex: 0 0 auto;
+    margin-right: 1rem;
+  }
+}
diff --git a/ui/pages/onboarding-flow/onboarding-app-header/onboarding-app-header.js b/ui/pages/onboarding-flow/onboarding-app-header/onboarding-app-header.js
new file mode 100644
index 000000000..ad5da2255
--- /dev/null
+++ b/ui/pages/onboarding-flow/onboarding-app-header/onboarding-app-header.js
@@ -0,0 +1,34 @@
+import React from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import MetaFoxLogo from '../../../components/ui/metafox-logo';
+import Dropdown from '../../../components/ui/dropdown';
+import { getCurrentLocale } from '../../../ducks/metamask/metamask';
+import { updateCurrentLocale } from '../../../store/actions';
+import locales from '../../../../app/_locales/index.json';
+
+export default function OnboardingAppHeader() {
+  const dispatch = useDispatch();
+  const currentLocale = useSelector(getCurrentLocale);
+  const localeOptions = locales.map((locale) => {
+    return {
+      name: locale.name,
+      value: locale.code,
+    };
+  });
+
+  return (
+    <div className="onboarding-app-header">
+      <div className="onboarding-app-header__contents">
+        <MetaFoxLogo unsetIconHeight isOnboarding />
+        <Dropdown
+          id="select-locale"
+          options={localeOptions}
+          selectedOption={currentLocale}
+          onChange={async (newLocale) =>
+            dispatch(updateCurrentLocale(newLocale))
+          }
+        />
+      </div>
+    </div>
+  );
+}
diff --git a/ui/pages/onboarding-flow/onboarding-flow.js b/ui/pages/onboarding-flow/onboarding-flow.js
index fed13cbad..7bb908383 100644
--- a/ui/pages/onboarding-flow/onboarding-flow.js
+++ b/ui/pages/onboarding-flow/onboarding-flow.js
@@ -39,7 +39,7 @@ export default function OnboardingFlow() {
     // For ONBOARDING_V2 dev purposes,
     // Remove when ONBOARDING_V2 dev complete
     if (process.env.ONBOARDING_V2) {
-      history.push(ONBOARDING_CREATE_PASSWORD_ROUTE);
+      history.push(ONBOARDING_REVIEW_SRP_ROUTE);
       return;
     }
 
diff --git a/ui/pages/onboarding-flow/recovery-phrase/index.scss b/ui/pages/onboarding-flow/recovery-phrase/index.scss
index 7c37d0059..c095b7119 100644
--- a/ui/pages/onboarding-flow/recovery-phrase/index.scss
+++ b/ui/pages/onboarding-flow/recovery-phrase/index.scss
@@ -1,4 +1,6 @@
 .recovery-phrase {
+  max-width: 600px;
+
   &__tips {
     flex-direction: column;
 
@@ -10,7 +12,7 @@
 
   &__chips {
     display: grid;
-    grid-template-columns: 160px 160px 160px;
+    grid-template-columns: 190px 190px 190px;
     justify-items: center;
     align-items: center;
     row-gap: 16px;
diff --git a/ui/pages/onboarding-flow/recovery-phrase/review-recovery-phrase.js b/ui/pages/onboarding-flow/recovery-phrase/review-recovery-phrase.js
index fae50a3f1..584296c67 100644
--- a/ui/pages/onboarding-flow/recovery-phrase/review-recovery-phrase.js
+++ b/ui/pages/onboarding-flow/recovery-phrase/review-recovery-phrase.js
@@ -23,7 +23,7 @@ export default function RecoveryPhrase({ seedPhrase }) {
   const [copied, handleCopy] = useCopyToClipboard();
   const [seedPhraseRevealed, setSeedPhraseRevealed] = useState(false);
   return (
-    <div>
+    <div className="recovery-phrase">
       <ProgressBar stage="SEED_PHRASE_REVIEW" />
       <Box
         justifyContent={JUSTIFY_CONTENT.CENTER}
diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js
index 4c37ca5bb..a6008025e 100644
--- a/ui/pages/routes/routes.component.js
+++ b/ui/pages/routes/routes.component.js
@@ -31,6 +31,7 @@ import AppHeader from '../../components/app/app-header';
 import UnlockPage from '../unlock-page';
 import Alerts from '../../components/app/alerts';
 import Asset from '../asset';
+import OnboardingAppHeader from '../onboarding-flow/onboarding-app-header/onboarding-app-header';
 
 import {
   IMPORT_TOKEN_ROUTE,
@@ -265,6 +266,17 @@ export default class Routes extends Component {
     return isHandlingPermissionsRequest || isHandlingAddEthereumChainRequest;
   }
 
+  showOnboardingHeader() {
+    const { location } = this.props;
+
+    return Boolean(
+      matchPath(location.pathname, {
+        path: ONBOARDING_ROUTE,
+        exact: false,
+      }),
+    );
+  }
+
   render() {
     const {
       isLoading,
@@ -319,6 +331,9 @@ export default class Routes extends Component {
             }
           />
         )}
+        {process.env.ONBOARDING_V2 && this.showOnboardingHeader() && (
+          <OnboardingAppHeader />
+        )}
         <NetworkDropdown
           provider={provider}
           frequentRpcListDetail={frequentRpcListDetail}

From cb174ff8e6e6df0f49831859d66ee372d9074b60 Mon Sep 17 00:00:00 2001
From: kumavis <kumavis@users.noreply.github.com>
Date: Tue, 5 Oct 2021 12:06:31 -1000
Subject: [PATCH 42/56] Lavamoat build system integration for WebApp (#12242)

* lavamoat - add lavamoat to webapp background

* test:e2e - add delay to resolve failure

* test:e2e - add delay to resolve failure

* build - add a switch for applying lavamoat, currently off for all

* test/e2e - remove delays added for lavamoat

* Revert "test/e2e - remove delays added for lavamoat"

This reverts commit 79c3479f15c072ed362ba1d4f1af41ea11a17d63.
---
 app/background.html                          |   16 +-
 app/home.html                                |   16 +-
 app/notification.html                        |   16 +-
 app/popup.html                               |   16 +-
 development/build/scripts.js                 |  112 +-
 development/build/static.js                  |    5 +
 lavamoat/browserify/policy-override.json     |   55 +
 lavamoat/browserify/policy.json              | 4656 ++++++++++++++++++
 lavamoat/node/policy.json                    |  101 +-
 package.json                                 |   10 +-
 test/e2e/tests/contract-interactions.spec.js |    4 +
 yarn.lock                                    |   61 +-
 12 files changed, 5006 insertions(+), 62 deletions(-)
 create mode 100644 lavamoat/browserify/policy-override.json
 create mode 100644 lavamoat/browserify/policy.json

diff --git a/app/background.html b/app/background.html
index 4f80b2d7f..447efa60e 100644
--- a/app/background.html
+++ b/app/background.html
@@ -6,12 +6,18 @@
   <body>
     <script src="./globalthis.js" type="text/javascript" charset="utf-8"></script>
     <script src="./sentry-install.js" type="text/javascript" charset="utf-8"></script>
-    <script src="./lockdown-install.js" type="text/javascript" charset="utf-8"></script>
-    <script src="./lockdown-run.js" type="text/javascript" charset="utf-8"></script>
-    <script src="./lockdown-more.js" type="text/javascript" charset="utf-8"></script>
-    <script src="./runtime-cjs.js" type="text/javascript" charset="utf-8"></script>
+    {{@if(it.useLavamoat)}}
+      <script src="./runtime-lavamoat.js" type="text/javascript" charset="utf-8"></script>
+      <script src="./lockdown-more.js" type="text/javascript" charset="utf-8"></script>
+      <script src="./policy-load.js" type="text/javascript" charset="utf-8"></script>
+    {{#else}}
+      <script src="./lockdown-install.js" type="text/javascript" charset="utf-8"></script>
+      <script src="./lockdown-run.js" type="text/javascript" charset="utf-8"></script>
+      <script src="./lockdown-more.js" type="text/javascript" charset="utf-8"></script>
+      <script src="./runtime-cjs.js" type="text/javascript" charset="utf-8"></script>
+    {{/if}}
     {{@each(it.jsBundles) => val}}
-    <script src="{{val}}" type="text/javascript" charset="utf-8"></script>
+      <script src="{{val}}" type="text/javascript" charset="utf-8"></script>
     {{/each}}
     <script src="./chromereload.js" type="text/javascript" charset="utf-8"></script>
   </body>
diff --git a/app/home.html b/app/home.html
index c55a6446f..d44eb6663 100644
--- a/app/home.html
+++ b/app/home.html
@@ -12,12 +12,18 @@
     <div id="popover-content"></div>
     <script src="./globalthis.js" type="text/javascript" charset="utf-8"></script>
     <script src="./sentry-install.js" type="text/javascript" charset="utf-8"></script>
-    <script src="./lockdown-install.js" type="text/javascript" charset="utf-8"></script>
-    <script src="./lockdown-run.js" type="text/javascript" charset="utf-8"></script>
-    <script src="./lockdown-more.js" type="text/javascript" charset="utf-8"></script>
-    <script src="./runtime-cjs.js" type="text/javascript" charset="utf-8"></script>
+    {{@if(it.useLavamoat)}}
+      <script src="./runtime-lavamoat.js" type="text/javascript" charset="utf-8"></script>
+      <script src="./lockdown-more.js" type="text/javascript" charset="utf-8"></script>
+      <script src="./policy-load.js" type="text/javascript" charset="utf-8"></script>
+    {{#else}}
+      <script src="./lockdown-install.js" type="text/javascript" charset="utf-8"></script>
+      <script src="./lockdown-run.js" type="text/javascript" charset="utf-8"></script>
+      <script src="./lockdown-more.js" type="text/javascript" charset="utf-8"></script>
+      <script src="./runtime-cjs.js" type="text/javascript" charset="utf-8"></script>
+    {{/if}}
     {{@each(it.jsBundles) => val}}
-    <script src="{{val}}" type="text/javascript" charset="utf-8"></script>
+      <script src="{{val}}" type="text/javascript" charset="utf-8"></script>
     {{/each}}
   </body>
 </html>
diff --git a/app/notification.html b/app/notification.html
index 7ff5d73cc..1002a37ef 100644
--- a/app/notification.html
+++ b/app/notification.html
@@ -35,12 +35,18 @@
     <div id="popover-content"></div>
     <script src="./globalthis.js" type="text/javascript" charset="utf-8"></script>
     <script src="./sentry-install.js" type="text/javascript" charset="utf-8"></script>
-    <script src="./lockdown-install.js" type="text/javascript" charset="utf-8"></script>
-    <script src="./lockdown-run.js" type="text/javascript" charset="utf-8"></script>
-    <script src="./lockdown-more.js" type="text/javascript" charset="utf-8"></script>
-    <script src="./runtime-cjs.js" type="text/javascript" charset="utf-8"></script>
+    {{@if(it.useLavamoat)}}
+      <script src="./runtime-lavamoat.js" type="text/javascript" charset="utf-8"></script>
+      <script src="./lockdown-more.js" type="text/javascript" charset="utf-8"></script>
+      <script src="./policy-load.js" type="text/javascript" charset="utf-8"></script>
+    {{#else}}
+      <script src="./lockdown-install.js" type="text/javascript" charset="utf-8"></script>
+      <script src="./lockdown-run.js" type="text/javascript" charset="utf-8"></script>
+      <script src="./lockdown-more.js" type="text/javascript" charset="utf-8"></script>
+      <script src="./runtime-cjs.js" type="text/javascript" charset="utf-8"></script>
+    {{/if}}
     {{@each(it.jsBundles) => val}}
-    <script src="{{val}}" type="text/javascript" charset="utf-8"></script>
+      <script src="{{val}}" type="text/javascript" charset="utf-8"></script>
     {{/each}}
   </body>
 </html>
diff --git a/app/popup.html b/app/popup.html
index 378efbfb4..e24978ad7 100644
--- a/app/popup.html
+++ b/app/popup.html
@@ -12,12 +12,18 @@
     <div id="popover-content"></div>
     <script src="./globalthis.js" type="text/javascript" charset="utf-8"></script>
     <script src="./sentry-install.js" type="text/javascript" charset="utf-8"></script>
-    <script src="./lockdown-install.js" type="text/javascript" charset="utf-8"></script>
-    <script src="./lockdown-run.js" type="text/javascript" charset="utf-8"></script>
-    <script src="./lockdown-more.js" type="text/javascript" charset="utf-8"></script>
-    <script src="./runtime-cjs.js" type="text/javascript" charset="utf-8"></script>
+    {{@if(it.useLavamoat)}}
+      <script src="./runtime-lavamoat.js" type="text/javascript" charset="utf-8"></script>
+      <script src="./lockdown-more.js" type="text/javascript" charset="utf-8"></script>
+      <script src="./policy-load.js" type="text/javascript" charset="utf-8"></script>
+    {{#else}}
+      <script src="./lockdown-install.js" type="text/javascript" charset="utf-8"></script>
+      <script src="./lockdown-run.js" type="text/javascript" charset="utf-8"></script>
+      <script src="./lockdown-more.js" type="text/javascript" charset="utf-8"></script>
+      <script src="./runtime-cjs.js" type="text/javascript" charset="utf-8"></script>
+    {{/if}}
     {{@each(it.jsBundles) => val}}
-    <script src="{{val}}" type="text/javascript" charset="utf-8"></script>
+      <script src="{{val}}" type="text/javascript" charset="utf-8"></script>
     {{/each}}
   </body>
 </html>
diff --git a/development/build/scripts.js b/development/build/scripts.js
index 618485ad7..e681be02f 100644
--- a/development/build/scripts.js
+++ b/development/build/scripts.js
@@ -4,6 +4,7 @@ const { writeFileSync, readFileSync } = require('fs');
 const EventEmitter = require('events');
 const gulp = require('gulp');
 const watch = require('gulp-watch');
+const Vinyl = require('vinyl');
 const source = require('vinyl-source-stream');
 const buffer = require('vinyl-buffer');
 const log = require('fancy-log');
@@ -20,7 +21,8 @@ const endOfStream = pify(require('end-of-stream'));
 const labeledStreamSplicer = require('labeled-stream-splicer').obj;
 const wrapInStream = require('pumpify').obj;
 const Sqrl = require('squirrelly');
-const lavaPack = require('@lavamoat/lavapack');
+const lavapack = require('@lavamoat/lavapack');
+const lavamoatBrowserify = require('lavamoat-browserify');
 const terser = require('terser');
 
 const bifyModuleGroups = require('bify-module-groups');
@@ -260,7 +262,22 @@ function createFactoredBuild({
     // set bundle entries
     bundlerOpts.entries = [...entryFiles];
 
+    // setup lavamoat
+    // lavamoat will add lavapack but it will be removed by bify-module-groups
+    // we will re-add it later by installing a lavapack runtime
+    const lavamoatOpts = {
+      policy: path.resolve(__dirname, '../../lavamoat/browserify/policy.json'),
+      policyOverride: path.resolve(
+        __dirname,
+        '../../lavamoat/browserify/policy-override.json',
+      ),
+      writeAutoPolicy: process.env.WRITE_AUTO_POLICY,
+    };
+    Object.assign(bundlerOpts, lavamoatBrowserify.args);
+    bundlerOpts.plugin.push([lavamoatBrowserify, lavamoatOpts]);
+
     // setup bundle factoring with bify-module-groups plugin
+    // note: this will remove lavapack, but its ok bc we manually readd it later
     Object.assign(bundlerOpts, bifyModuleGroups.plugin.args);
     bundlerOpts.plugin = [...bundlerOpts.plugin, [bifyModuleGroups.plugin]];
 
@@ -282,18 +299,24 @@ function createFactoredBuild({
           groupingMap: sizeGroupMap,
         }),
       );
-      pipeline.get('vinyl').unshift(
-        // convert each module group into a stream with a single vinyl file
-        streamFlatMap((moduleGroup) => {
-          const filename = `${moduleGroup.label}.js`;
-          const childStream = wrapInStream(
-            moduleGroup.stream,
-            lavaPack({ raw: true, hasExports: true, includePrelude: false }),
-            source(filename),
-          );
-          return childStream;
+      // converts each module group into a single vinyl file containing its bundle
+      const moduleGroupPackerStream = streamFlatMap((moduleGroup) => {
+        const filename = `${moduleGroup.label}.js`;
+        const childStream = wrapInStream(
+          moduleGroup.stream,
+          // we manually readd lavapack here bc bify-module-groups removes it
+          lavapack({ raw: true, hasExports: true, includePrelude: false }),
+          source(filename),
+        );
+        return childStream;
+      });
+      pipeline.get('vinyl').unshift(moduleGroupPackerStream, buffer());
+      // add lavamoat policy loader file to packer output
+      moduleGroupPackerStream.push(
+        new Vinyl({
+          path: 'policy-load.js',
+          contents: lavapack.makePolicyLoaderStream(lavamoatOpts),
         }),
-        buffer(),
       );
       // setup bundle destination
       browserPlatforms.forEach((platform) => {
@@ -307,36 +330,58 @@ function createFactoredBuild({
       const commonSet = sizeGroupMap.get('common');
       // create entry points for each file
       for (const [groupLabel, groupSet] of sizeGroupMap.entries()) {
-        // skip "common" group, they are added tp all other groups
+        // skip "common" group, they are added to all other groups
         if (groupSet === commonSet) continue;
 
         switch (groupLabel) {
           case 'ui': {
-            renderHtmlFile('popup', groupSet, commonSet, browserPlatforms);
-            renderHtmlFile(
-              'notification',
+            renderHtmlFile({
+              htmlName: 'popup',
               groupSet,
               commonSet,
               browserPlatforms,
-            );
-            renderHtmlFile('home', groupSet, commonSet, browserPlatforms);
+              useLavamoat: false,
+            });
+            renderHtmlFile({
+              htmlName: 'notification',
+              groupSet,
+              commonSet,
+              browserPlatforms,
+              useLavamoat: false,
+            });
+            renderHtmlFile({
+              htmlName: 'home',
+              groupSet,
+              commonSet,
+              browserPlatforms,
+              useLavamoat: false,
+            });
             break;
           }
           case 'background': {
-            renderHtmlFile('background', groupSet, commonSet, browserPlatforms);
-            break;
-          }
-          case 'content-script': {
-            renderHtmlFile(
-              'trezor-usb-permissions',
+            renderHtmlFile({
+              htmlName: 'background',
               groupSet,
               commonSet,
               browserPlatforms,
-            );
+              useLavamoat: false,
+            });
+            break;
+          }
+          case 'content-script': {
+            renderHtmlFile({
+              htmlName: 'trezor-usb-permissions',
+              groupSet,
+              commonSet,
+              browserPlatforms,
+              useLavamoat: false,
+            });
             break;
           }
           default: {
-            throw new Error(`buildsys - unknown groupLabel "${groupLabel}"`);
+            throw new Error(
+              `build/scripts - unknown groupLabel "${groupLabel}"`,
+            );
           }
         }
       }
@@ -637,13 +682,24 @@ function getEnvironment({ devMode, testing }) {
   return 'other';
 }
 
-function renderHtmlFile(htmlName, groupSet, commonSet, browserPlatforms) {
+function renderHtmlFile({
+  htmlName,
+  groupSet,
+  commonSet,
+  browserPlatforms,
+  useLavamoat,
+}) {
+  if (useLavamoat === undefined) {
+    throw new Error(
+      'build/scripts/renderHtmlFile - must specify "useLavamoat" option',
+    );
+  }
   const htmlFilePath = `./app/${htmlName}.html`;
   const htmlTemplate = readFileSync(htmlFilePath, 'utf8');
   const jsBundles = [...commonSet.values(), ...groupSet.values()].map(
     (label) => `./${label}.js`,
   );
-  const htmlOutput = Sqrl.render(htmlTemplate, { jsBundles });
+  const htmlOutput = Sqrl.render(htmlTemplate, { jsBundles, useLavamoat });
   browserPlatforms.forEach((platform) => {
     const dest = `./dist/${platform}/${htmlName}.html`;
     // we dont have a way of creating async events atm
diff --git a/development/build/static.js b/development/build/static.js
index 3926cefb0..5addd1b10 100644
--- a/development/build/static.js
+++ b/development/build/static.js
@@ -159,6 +159,11 @@ function getCopyTargets(shouldIncludeLockdown) {
       src: require.resolve('@lavamoat/lavapack/src/runtime-cjs.js'),
       dest: `runtime-cjs.js`,
     },
+    {
+      // eslint-disable-next-line node/no-extraneous-require
+      src: require.resolve('@lavamoat/lavapack/src/runtime.js'),
+      dest: `runtime-lavamoat.js`,
+    },
     {
       src: `./app/phishing.html`,
       dest: `phishing.html`,
diff --git a/lavamoat/browserify/policy-override.json b/lavamoat/browserify/policy-override.json
new file mode 100644
index 000000000..71fd98f61
--- /dev/null
+++ b/lavamoat/browserify/policy-override.json
@@ -0,0 +1,55 @@
+{
+  "resources": {
+    "browser-resolve": {
+      "packages": {
+        "core-js": true
+      }
+    },
+    "babel-runtime": {
+      "packages": {
+        "@babel/runtime": true
+      }
+    },
+    "node-fetch": {
+      "globals": {
+        "fetch": true
+      }
+    },
+    "lodash": {
+      "globals": {
+        "setTimeout": true,
+        "clearTimeout": true
+      }
+    },
+    "@ethersproject/random": {
+      "globals": {
+        "crypto.getRandomValues": true
+      }
+    },
+    "browser-passworder": {
+      "globals": {
+        "crypto": true
+      }
+    },
+    "randombytes": {
+      "globals": {
+        "crypto.getRandomValues": true
+      }
+    },
+    "extensionizer": {
+      "globals": {
+        "console": true
+      }
+    },
+    "web3": {
+      "globals": {
+        "XMLHttpRequest": true
+      }
+    },
+    "storage": {
+      "globals": {
+        "localStorage": true
+      }
+    }
+  }
+}
diff --git a/lavamoat/browserify/policy.json b/lavamoat/browserify/policy.json
new file mode 100644
index 000000000..766fae5a4
--- /dev/null
+++ b/lavamoat/browserify/policy.json
@@ -0,0 +1,4656 @@
+{
+  "resources": {
+    "3box": {
+      "globals": {
+        "console.error": true,
+        "console.log": true,
+        "console.warn": true,
+        "fetch": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "3box-orbitdb-plugins": true,
+        "3id-resolver": true,
+        "@babel/runtime": true,
+        "buffer": true,
+        "did-jwt": true,
+        "elliptic": true,
+        "ethers": true,
+        "graphql-request": true,
+        "https-did-resolver": true,
+        "ipfs": true,
+        "ipfs-did-document": true,
+        "ipfs-log": true,
+        "ipfs-mini": true,
+        "is-ipfs": true,
+        "js-sha256": true,
+        "multihashes": true,
+        "muport-did-resolver": true,
+        "node-fetch": true,
+        "orbit-db": true,
+        "orbit-db-access-controllers": true,
+        "orbit-db-identity-provider": true,
+        "orbit-db-pubsub": true,
+        "process": true,
+        "store": true,
+        "tweetnacl": true,
+        "tweetnacl-util": true
+      }
+    },
+    "3box-orbitdb-plugins": {
+      "globals": {
+        "console.log": true
+      },
+      "packages": {
+        "base64url": true,
+        "did-jwt": true,
+        "did-resolver": true,
+        "events": true,
+        "ipfs-log": true,
+        "is-ipfs": true,
+        "orbit-db-access-controllers": true,
+        "orbit-db-io": true,
+        "safe-buffer": true
+      }
+    },
+    "3id-resolver": {
+      "packages": {
+        "@babel/runtime": true,
+        "base64url": true,
+        "did-jwt": true,
+        "did-resolver": true,
+        "ipfs-did-document": true
+      }
+    },
+    "@babel/runtime": {
+      "packages": {
+        "regenerator-runtime": true
+      }
+    },
+    "@download/blockies": {
+      "globals": {
+        "document.createElement": true
+      }
+    },
+    "@ensdomains/content-hash": {
+      "globals": {
+        "console.warn": true
+      },
+      "packages": {
+        "buffer": true,
+        "cids": true,
+        "js-base64": true,
+        "multicodec": true,
+        "multihashes": true
+      }
+    },
+    "@ethereumjs/common": {
+      "packages": {
+        "buffer": true,
+        "crc-32": true,
+        "ethereumjs-util": true,
+        "events": true
+      }
+    },
+    "@ethereumjs/tx": {
+      "packages": {
+        "@ethereumjs/common": true,
+        "buffer": true,
+        "ethereumjs-util": true,
+        "is-buffer": true
+      }
+    },
+    "@ethersproject/abi": {
+      "globals": {
+        "console.log": true
+      },
+      "packages": {
+        "@ethersproject/address": true,
+        "@ethersproject/bignumber": true,
+        "@ethersproject/bytes": true,
+        "@ethersproject/constants": true,
+        "@ethersproject/hash": true,
+        "@ethersproject/keccak256": true,
+        "@ethersproject/logger": true,
+        "@ethersproject/properties": true,
+        "@ethersproject/strings": true
+      }
+    },
+    "@ethersproject/abstract-provider": {
+      "packages": {
+        "@ethersproject/bignumber": true,
+        "@ethersproject/bytes": true,
+        "@ethersproject/logger": true,
+        "@ethersproject/properties": true
+      }
+    },
+    "@ethersproject/abstract-signer": {
+      "packages": {
+        "@ethersproject/logger": true,
+        "@ethersproject/properties": true
+      }
+    },
+    "@ethersproject/address": {
+      "packages": {
+        "@ethersproject/bignumber": true,
+        "@ethersproject/bytes": true,
+        "@ethersproject/keccak256": true,
+        "@ethersproject/logger": true,
+        "@ethersproject/rlp": true
+      }
+    },
+    "@ethersproject/base64": {
+      "globals": {
+        "atob": true,
+        "btoa": true
+      },
+      "packages": {
+        "@ethersproject/bytes": true
+      }
+    },
+    "@ethersproject/basex": {
+      "packages": {
+        "@ethersproject/bytes": true,
+        "@ethersproject/properties": true
+      }
+    },
+    "@ethersproject/bignumber": {
+      "packages": {
+        "@ethersproject/bytes": true,
+        "@ethersproject/logger": true,
+        "bn.js": true
+      }
+    },
+    "@ethersproject/bytes": {
+      "packages": {
+        "@ethersproject/logger": true
+      }
+    },
+    "@ethersproject/constants": {
+      "packages": {
+        "@ethersproject/bignumber": true
+      }
+    },
+    "@ethersproject/contracts": {
+      "globals": {
+        "setTimeout": true
+      },
+      "packages": {
+        "@ethersproject/abi": true,
+        "@ethersproject/abstract-provider": true,
+        "@ethersproject/abstract-signer": true,
+        "@ethersproject/address": true,
+        "@ethersproject/bignumber": true,
+        "@ethersproject/bytes": true,
+        "@ethersproject/logger": true,
+        "@ethersproject/properties": true,
+        "@ethersproject/transactions": true
+      }
+    },
+    "@ethersproject/hash": {
+      "packages": {
+        "@ethersproject/address": true,
+        "@ethersproject/bignumber": true,
+        "@ethersproject/bytes": true,
+        "@ethersproject/keccak256": true,
+        "@ethersproject/logger": true,
+        "@ethersproject/properties": true,
+        "@ethersproject/strings": true
+      }
+    },
+    "@ethersproject/hdnode": {
+      "packages": {
+        "@ethersproject/basex": true,
+        "@ethersproject/bignumber": true,
+        "@ethersproject/bytes": true,
+        "@ethersproject/logger": true,
+        "@ethersproject/pbkdf2": true,
+        "@ethersproject/properties": true,
+        "@ethersproject/sha2": true,
+        "@ethersproject/signing-key": true,
+        "@ethersproject/strings": true,
+        "@ethersproject/transactions": true,
+        "@ethersproject/wordlists": true
+      }
+    },
+    "@ethersproject/json-wallets": {
+      "packages": {
+        "@ethersproject/address": true,
+        "@ethersproject/bytes": true,
+        "@ethersproject/hdnode": true,
+        "@ethersproject/keccak256": true,
+        "@ethersproject/logger": true,
+        "@ethersproject/pbkdf2": true,
+        "@ethersproject/properties": true,
+        "@ethersproject/random": true,
+        "@ethersproject/strings": true,
+        "@ethersproject/transactions": true,
+        "aes-js": true,
+        "scrypt-js": true
+      }
+    },
+    "@ethersproject/keccak256": {
+      "packages": {
+        "@ethersproject/bytes": true,
+        "js-sha3": true
+      }
+    },
+    "@ethersproject/logger": {
+      "globals": {
+        "console": true
+      }
+    },
+    "@ethersproject/networks": {
+      "packages": {
+        "@ethersproject/logger": true
+      }
+    },
+    "@ethersproject/pbkdf2": {
+      "packages": {
+        "@ethersproject/bytes": true,
+        "@ethersproject/sha2": true
+      }
+    },
+    "@ethersproject/properties": {
+      "packages": {
+        "@ethersproject/logger": true
+      }
+    },
+    "@ethersproject/providers": {
+      "globals": {
+        "WebSocket": true,
+        "clearInterval": true,
+        "clearTimeout": true,
+        "console.log": true,
+        "console.warn": true,
+        "name": true,
+        "setInterval": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "@ethersproject/abstract-provider": true,
+        "@ethersproject/abstract-signer": true,
+        "@ethersproject/address": true,
+        "@ethersproject/basex": true,
+        "@ethersproject/bignumber": true,
+        "@ethersproject/bytes": true,
+        "@ethersproject/constants": true,
+        "@ethersproject/hash": true,
+        "@ethersproject/logger": true,
+        "@ethersproject/networks": true,
+        "@ethersproject/properties": true,
+        "@ethersproject/random": true,
+        "@ethersproject/sha2": true,
+        "@ethersproject/strings": true,
+        "@ethersproject/transactions": true,
+        "@ethersproject/web": true,
+        "bech32": true
+      }
+    },
+    "@ethersproject/random": {
+      "packages": {
+        "@ethersproject/bytes": true,
+        "@ethersproject/logger": true
+      }
+    },
+    "@ethersproject/rlp": {
+      "packages": {
+        "@ethersproject/bytes": true,
+        "@ethersproject/logger": true
+      }
+    },
+    "@ethersproject/sha2": {
+      "packages": {
+        "@ethersproject/bytes": true,
+        "@ethersproject/logger": true,
+        "hash.js": true
+      }
+    },
+    "@ethersproject/signing-key": {
+      "packages": {
+        "@ethersproject/bytes": true,
+        "@ethersproject/logger": true,
+        "@ethersproject/properties": true,
+        "elliptic": true
+      }
+    },
+    "@ethersproject/solidity": {
+      "packages": {
+        "@ethersproject/bignumber": true,
+        "@ethersproject/bytes": true,
+        "@ethersproject/keccak256": true,
+        "@ethersproject/sha2": true,
+        "@ethersproject/strings": true
+      }
+    },
+    "@ethersproject/strings": {
+      "packages": {
+        "@ethersproject/bytes": true,
+        "@ethersproject/constants": true,
+        "@ethersproject/logger": true
+      }
+    },
+    "@ethersproject/transactions": {
+      "globals": {
+        "console.log": true
+      },
+      "packages": {
+        "@ethersproject/address": true,
+        "@ethersproject/bignumber": true,
+        "@ethersproject/bytes": true,
+        "@ethersproject/constants": true,
+        "@ethersproject/keccak256": true,
+        "@ethersproject/logger": true,
+        "@ethersproject/properties": true,
+        "@ethersproject/rlp": true,
+        "@ethersproject/signing-key": true
+      }
+    },
+    "@ethersproject/units": {
+      "packages": {
+        "@ethersproject/bignumber": true,
+        "@ethersproject/logger": true
+      }
+    },
+    "@ethersproject/wallet": {
+      "packages": {
+        "@ethersproject/abstract-provider": true,
+        "@ethersproject/abstract-signer": true,
+        "@ethersproject/address": true,
+        "@ethersproject/bytes": true,
+        "@ethersproject/hash": true,
+        "@ethersproject/hdnode": true,
+        "@ethersproject/json-wallets": true,
+        "@ethersproject/keccak256": true,
+        "@ethersproject/logger": true,
+        "@ethersproject/properties": true,
+        "@ethersproject/random": true,
+        "@ethersproject/signing-key": true,
+        "@ethersproject/transactions": true
+      }
+    },
+    "@ethersproject/web": {
+      "globals": {
+        "clearTimeout": true,
+        "fetch": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "@ethersproject/base64": true,
+        "@ethersproject/bytes": true,
+        "@ethersproject/logger": true,
+        "@ethersproject/properties": true,
+        "@ethersproject/strings": true
+      }
+    },
+    "@ethersproject/wordlists": {
+      "packages": {
+        "@ethersproject/bytes": true,
+        "@ethersproject/hash": true,
+        "@ethersproject/logger": true,
+        "@ethersproject/properties": true,
+        "@ethersproject/strings": true
+      }
+    },
+    "@formatjs/intl-relativetimeformat": {
+      "globals": {
+        "Intl": true
+      },
+      "packages": {
+        "@formatjs/intl-utils": true
+      }
+    },
+    "@formatjs/intl-utils": {
+      "globals": {
+        "Intl.getCanonicalLocales": true
+      }
+    },
+    "@material-ui/core": {
+      "globals": {
+        "Image": true,
+        "_formatMuiErrorMessage": true,
+        "addEventListener": true,
+        "clearInterval": true,
+        "clearTimeout": true,
+        "console.error": true,
+        "console.warn": true,
+        "document": true,
+        "getComputedStyle": true,
+        "getSelection": true,
+        "innerHeight": true,
+        "innerWidth": true,
+        "matchMedia": true,
+        "navigator": true,
+        "performance.now": true,
+        "removeEventListener": true,
+        "requestAnimationFrame": true,
+        "setInterval": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "@babel/runtime": true,
+        "@material-ui/styles": true,
+        "@material-ui/system": true,
+        "@material-ui/utils": true,
+        "clsx": true,
+        "hoist-non-react-statics": true,
+        "popper.js": true,
+        "prop-types": true,
+        "react": true,
+        "react-dom": true,
+        "react-is": true,
+        "react-transition-group": true
+      }
+    },
+    "@material-ui/styles": {
+      "globals": {
+        "console.error": true,
+        "console.warn": true,
+        "document.createComment": true,
+        "document.head": true
+      },
+      "packages": {
+        "@babel/runtime": true,
+        "@material-ui/utils": true,
+        "clsx": true,
+        "hoist-non-react-statics": true,
+        "jss": true,
+        "jss-plugin-camel-case": true,
+        "jss-plugin-default-unit": true,
+        "jss-plugin-global": true,
+        "jss-plugin-nested": true,
+        "jss-plugin-props-sort": true,
+        "jss-plugin-rule-value-function": true,
+        "jss-plugin-vendor-prefixer": true,
+        "prop-types": true,
+        "react": true
+      }
+    },
+    "@material-ui/system": {
+      "globals": {
+        "console.error": true
+      },
+      "packages": {
+        "@babel/runtime": true,
+        "@material-ui/utils": true,
+        "prop-types": true
+      }
+    },
+    "@material-ui/utils": {
+      "packages": {
+        "@babel/runtime": true,
+        "prop-types": true,
+        "react-is": true
+      }
+    },
+    "@metamask/controllers": {
+      "globals": {
+        "Headers": true,
+        "clearInterval": true,
+        "clearTimeout": true,
+        "console.error": true,
+        "console.log": true,
+        "fetch": true,
+        "setInterval": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "@ethereumjs/common": true,
+        "@ethereumjs/tx": true,
+        "@metamask/contract-metadata": true,
+        "abort-controller": true,
+        "async-mutex": true,
+        "await-semaphore": true,
+        "buffer": true,
+        "eth-ens-namehash": true,
+        "eth-json-rpc-infura": true,
+        "eth-keyring-controller": true,
+        "eth-method-registry": true,
+        "eth-phishing-detect": true,
+        "eth-query": true,
+        "eth-rpc-errors": true,
+        "eth-sig-util": true,
+        "ethereumjs-tx": true,
+        "ethereumjs-util": true,
+        "ethereumjs-wallet": true,
+        "ethers": true,
+        "ethjs-query": true,
+        "ethjs-unit": true,
+        "ethjs-util": true,
+        "events": true,
+        "human-standard-collectible-abi": true,
+        "human-standard-token-abi": true,
+        "immer": true,
+        "isomorphic-fetch": true,
+        "jsonschema": true,
+        "nanoid": true,
+        "punycode": true,
+        "single-call-balance-checker-abi": true,
+        "uuid": true,
+        "web3": true,
+        "web3-provider-engine": true
+      }
+    },
+    "@metamask/eth-ledger-bridge-keyring": {
+      "globals": {
+        "addEventListener": true,
+        "console.log": true,
+        "document.createElement": true,
+        "document.head.appendChild": true,
+        "fetch": true,
+        "removeEventListener": true
+      },
+      "packages": {
+        "@ethereumjs/tx": true,
+        "buffer": true,
+        "eth-sig-util": true,
+        "ethereumjs-util": true,
+        "events": true,
+        "hdkey": true
+      }
+    },
+    "@metamask/eth-token-tracker": {
+      "globals": {
+        "console.warn": true
+      },
+      "packages": {
+        "@babel/runtime": true,
+        "deep-equal": true,
+        "eth-block-tracker": true,
+        "ethjs": true,
+        "ethjs-contract": true,
+        "ethjs-query": true,
+        "human-standard-token-abi": true,
+        "safe-event-emitter": true
+      }
+    },
+    "@metamask/etherscan-link": {
+      "globals": {
+        "URL": true
+      }
+    },
+    "@metamask/jazzicon": {
+      "globals": {
+        "document.createElement": true,
+        "document.createElementNS": true
+      },
+      "packages": {
+        "color": true,
+        "mersenne-twister": true
+      }
+    },
+    "@metamask/logo": {
+      "globals": {
+        "addEventListener": true,
+        "document.body.appendChild": true,
+        "document.createElementNS": true,
+        "innerHeight": true,
+        "innerWidth": true,
+        "requestAnimationFrame": true
+      },
+      "packages": {
+        "gl-mat4": true,
+        "gl-vec3": true
+      }
+    },
+    "@metamask/obs-store": {
+      "globals": {
+        "localStorage": true
+      },
+      "packages": {
+        "@metamask/safe-event-emitter": true,
+        "stream-browserify": true,
+        "through2": true
+      }
+    },
+    "@metamask/safe-event-emitter": {
+      "globals": {
+        "setTimeout": true
+      },
+      "packages": {
+        "events": true
+      }
+    },
+    "@popperjs/core": {
+      "globals": {
+        "Element": true,
+        "HTMLElement": true,
+        "ShadowRoot": true,
+        "console.error": true,
+        "console.warn": true,
+        "document": true,
+        "navigator.userAgent": true
+      }
+    },
+    "@reduxjs/toolkit": {
+      "globals": {
+        "AbortController": true,
+        "__REDUX_DEVTOOLS_EXTENSION_COMPOSE__": true,
+        "console.error": true,
+        "console.info": true,
+        "console.warn": true
+      },
+      "packages": {
+        "immer": true,
+        "redux": true,
+        "redux-thunk": true,
+        "reselect": true
+      }
+    },
+    "@segment/loosely-validate-event": {
+      "packages": {
+        "assert": true,
+        "buffer": true,
+        "component-type": true,
+        "join-component": true
+      }
+    },
+    "@sentry/browser": {
+      "globals": {
+        "XMLHttpRequest": true,
+        "document.body": true,
+        "document.createElement": true,
+        "document.head": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "@sentry/core": true,
+        "@sentry/types": true,
+        "@sentry/utils": true,
+        "tslib": true
+      }
+    },
+    "@sentry/core": {
+      "globals": {
+        "clearInterval": true,
+        "setInterval": true
+      },
+      "packages": {
+        "@sentry/hub": true,
+        "@sentry/minimal": true,
+        "@sentry/types": true,
+        "@sentry/utils": true,
+        "tslib": true
+      }
+    },
+    "@sentry/hub": {
+      "globals": {
+        "setTimeout": true
+      },
+      "packages": {
+        "@sentry/utils": true,
+        "tslib": true
+      }
+    },
+    "@sentry/integrations": {
+      "globals": {
+        "clearTimeout": true,
+        "console.error": true,
+        "console.log": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "@sentry/types": true,
+        "@sentry/utils": true,
+        "localforage": true,
+        "tslib": true
+      }
+    },
+    "@sentry/minimal": {
+      "packages": {
+        "@sentry/hub": true,
+        "tslib": true
+      }
+    },
+    "@sentry/utils": {
+      "globals": {
+        "CustomEvent": true,
+        "DOMError": true,
+        "DOMException": true,
+        "Element": true,
+        "ErrorEvent": true,
+        "Event": true,
+        "Headers": true,
+        "Request": true,
+        "Response": true,
+        "XMLHttpRequest.prototype": true,
+        "clearTimeout": true,
+        "console.error": true,
+        "document": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "process": true,
+        "tslib": true
+      }
+    },
+    "@sindresorhus/is": {
+      "packages": {
+        "is-buffer": true,
+        "util": true
+      }
+    },
+    "@zxing/library": {
+      "globals": {
+        "TextDecoder": true,
+        "TextEncoder": true,
+        "btoa": true,
+        "clearTimeout": true,
+        "define": true,
+        "document.createElement": true,
+        "document.createElementNS": true,
+        "document.getElementById": true,
+        "navigator.mediaDevices.enumerateDevices": true,
+        "navigator.mediaDevices.getUserMedia": true,
+        "setTimeout": true
+      }
+    },
+    "abort-controller": {
+      "globals": {
+        "AbortController": true
+      }
+    },
+    "abstract-leveldown": {
+      "packages": {
+        "is-buffer": true,
+        "process": true,
+        "xtend": true
+      }
+    },
+    "accounting": {
+      "globals": {
+        "define": true
+      }
+    },
+    "aes-js": {
+      "globals": {
+        "define": true
+      }
+    },
+    "analytics-node": {
+      "globals": {
+        "clearTimeout": true,
+        "console.log": true,
+        "setImmediate": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "@segment/loosely-validate-event": true,
+        "assert": true,
+        "axios": true,
+        "axios-retry": true,
+        "lodash.isstring": true,
+        "md5": true,
+        "ms": true,
+        "process": true,
+        "remove-trailing-slash": true,
+        "uuid": true
+      }
+    },
+    "asap": {
+      "globals": {
+        "clearInterval": true,
+        "clearTimeout": true,
+        "document.createTextNode": true,
+        "setInterval": true,
+        "setTimeout": true
+      }
+    },
+    "asn1.js": {
+      "packages": {
+        "bn.js": true,
+        "buffer": true,
+        "inherits": true,
+        "minimalistic-assert": true,
+        "vm-browserify": true
+      }
+    },
+    "assemblyscript": {
+      "globals": {
+        "WebAssembly.Instance": true,
+        "WebAssembly.Module": true,
+        "WebAssembly.instantiateStreaming": true,
+        "console.log": true
+      }
+    },
+    "assert": {
+      "globals": {
+        "Buffer": true
+      },
+      "packages": {
+        "object-assign": true,
+        "util": true
+      }
+    },
+    "async": {
+      "globals": {
+        "clearTimeout": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "lodash": true,
+        "process": true,
+        "timers-browserify": true
+      }
+    },
+    "async-iterator-to-pull-stream": {
+      "packages": {
+        "get-iterator": true,
+        "pull-stream-to-async-iterator": true
+      }
+    },
+    "async-iterator-to-stream": {
+      "packages": {
+        "process": true,
+        "readable-stream": true
+      }
+    },
+    "async-mutex": {
+      "globals": {
+        "setTimeout": true
+      },
+      "packages": {
+        "tslib": true
+      }
+    },
+    "await-semaphore": {
+      "packages": {
+        "process": true,
+        "timers-browserify": true
+      }
+    },
+    "axios": {
+      "globals": {
+        "FormData": true,
+        "URLSearchParams": true,
+        "XMLHttpRequest": true,
+        "btoa": true,
+        "document": true,
+        "location.href": true,
+        "navigator": true
+      },
+      "packages": {
+        "process": true
+      }
+    },
+    "axios-retry": {
+      "globals": {
+        "setTimeout": true
+      },
+      "packages": {
+        "is-retry-allowed": true
+      }
+    },
+    "babel-runtime": {
+      "packages": {
+        "core-js": true,
+        "regenerator-runtime": true
+      }
+    },
+    "backoff": {
+      "globals": {
+        "clearTimeout": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "events": true,
+        "precond": true,
+        "util": true
+      }
+    },
+    "base-x": {
+      "packages": {
+        "safe-buffer": true
+      }
+    },
+    "base32-encode": {
+      "packages": {
+        "to-data-view": true
+      }
+    },
+    "base64url": {
+      "packages": {
+        "buffer": true
+      }
+    },
+    "bignumber.js": {
+      "globals": {
+        "crypto": true,
+        "define": true
+      },
+      "packages": {
+        "crypto-browserify": true
+      }
+    },
+    "bip39": {
+      "packages": {
+        "create-hash": true,
+        "pbkdf2": true,
+        "randombytes": true,
+        "safe-buffer": true,
+        "unorm": true
+      }
+    },
+    "bip66": {
+      "packages": {
+        "safe-buffer": true
+      }
+    },
+    "bl": {
+      "packages": {
+        "buffer": true,
+        "readable-stream": true,
+        "util": true
+      }
+    },
+    "blakejs": {
+      "globals": {
+        "console.log": true
+      },
+      "packages": {
+        "buffer": true
+      }
+    },
+    "blob": {
+      "globals": {
+        "Blob": true,
+        "MSBlobBuilder": true,
+        "MozBlobBuilder": true,
+        "WebKitBlobBuilder": true
+      }
+    },
+    "bn.js": {
+      "packages": {
+        "browser-resolve": true
+      }
+    },
+    "borc": {
+      "globals": {
+        "console": true
+      },
+      "packages": {
+        "bignumber.js": true,
+        "buffer": true,
+        "ieee754": true,
+        "iso-url": true
+      }
+    },
+    "brorand": {
+      "globals": {
+        "crypto": true,
+        "msCrypto": true
+      },
+      "packages": {
+        "browser-resolve": true
+      }
+    },
+    "browser-passworder": {
+      "globals": {
+        "btoa": true,
+        "crypto.getRandomValues": true,
+        "crypto.subtle.decrypt": true,
+        "crypto.subtle.deriveKey": true,
+        "crypto.subtle.encrypt": true,
+        "crypto.subtle.importKey": true
+      },
+      "packages": {
+        "browserify-unibabel": true
+      }
+    },
+    "browserify-aes": {
+      "packages": {
+        "buffer": true,
+        "buffer-xor": true,
+        "cipher-base": true,
+        "evp_bytestokey": true,
+        "inherits": true,
+        "safe-buffer": true
+      }
+    },
+    "browserify-cipher": {
+      "packages": {
+        "browserify-aes": true,
+        "browserify-des": true,
+        "evp_bytestokey": true
+      }
+    },
+    "browserify-des": {
+      "packages": {
+        "buffer": true,
+        "cipher-base": true,
+        "des.js": true,
+        "inherits": true
+      }
+    },
+    "browserify-rsa": {
+      "packages": {
+        "bn.js": true,
+        "buffer": true,
+        "randombytes": true
+      }
+    },
+    "browserify-sign": {
+      "packages": {
+        "bn.js": true,
+        "browserify-rsa": true,
+        "buffer": true,
+        "create-hash": true,
+        "create-hmac": true,
+        "elliptic": true,
+        "inherits": true,
+        "parse-asn1": true,
+        "stream-browserify": true
+      }
+    },
+    "browserify-unibabel": {
+      "globals": {
+        "atob": true,
+        "btoa": true
+      }
+    },
+    "bs58": {
+      "packages": {
+        "base-x": true
+      }
+    },
+    "bs58check": {
+      "packages": {
+        "bs58": true,
+        "create-hash": true,
+        "safe-buffer": true
+      }
+    },
+    "btoa": {
+      "packages": {
+        "buffer": true
+      }
+    },
+    "buffer": {
+      "globals": {
+        "console": true
+      },
+      "packages": {
+        "base64-js": true,
+        "ieee754": true
+      }
+    },
+    "buffer-split": {
+      "packages": {
+        "buffer-indexof": true
+      }
+    },
+    "buffer-xor": {
+      "packages": {
+        "buffer": true
+      }
+    },
+    "cids": {
+      "packages": {
+        "buffer": true,
+        "class-is": true,
+        "is-buffer": true,
+        "multibase": true,
+        "multicodec": true,
+        "multihashes": true,
+        "uint8arrays": true
+      }
+    },
+    "cipher-base": {
+      "packages": {
+        "inherits": true,
+        "safe-buffer": true,
+        "stream-browserify": true,
+        "string_decoder": true
+      }
+    },
+    "classnames": {
+      "globals": {
+        "classNames": "write",
+        "define": true
+      }
+    },
+    "clone": {
+      "packages": {
+        "buffer": true
+      }
+    },
+    "coinstring": {
+      "packages": {
+        "bs58": true,
+        "buffer": true,
+        "create-hash": true
+      }
+    },
+    "color": {
+      "packages": {
+        "clone": true,
+        "color-convert": true,
+        "color-string": true
+      }
+    },
+    "color-convert": {
+      "packages": {
+        "color-name": true
+      }
+    },
+    "color-string": {
+      "packages": {
+        "color-name": true
+      }
+    },
+    "cookiejar": {
+      "globals": {
+        "console.warn": true
+      }
+    },
+    "copy-to-clipboard": {
+      "globals": {
+        "clipboardData.setData": true,
+        "console.error": true,
+        "console.warn": true,
+        "document.body.appendChild": true,
+        "document.body.removeChild": true,
+        "document.createElement": true,
+        "document.createRange": true,
+        "document.execCommand": true,
+        "document.getSelection": true,
+        "navigator.userAgent": true,
+        "prompt": true
+      },
+      "packages": {
+        "toggle-selection": true
+      }
+    },
+    "core-js": {
+      "globals": {
+        "PromiseRejectionEvent": true,
+        "__e": "write",
+        "__g": "write",
+        "document.createTextNode": true,
+        "postMessage": true,
+        "setTimeout": true
+      }
+    },
+    "core-util-is": {
+      "packages": {
+        "is-buffer": true
+      }
+    },
+    "crc-32": {
+      "globals": {
+        "DO_NOT_EXPORT_CRC": true,
+        "define": true
+      }
+    },
+    "create-ecdh": {
+      "packages": {
+        "bn.js": true,
+        "buffer": true,
+        "elliptic": true
+      }
+    },
+    "create-hash": {
+      "packages": {
+        "cipher-base": true,
+        "inherits": true,
+        "md5.js": true,
+        "ripemd160": true,
+        "sha.js": true
+      }
+    },
+    "create-hmac": {
+      "packages": {
+        "cipher-base": true,
+        "create-hash": true,
+        "inherits": true,
+        "ripemd160": true,
+        "safe-buffer": true,
+        "sha.js": true
+      }
+    },
+    "cross-fetch": {
+      "globals": {
+        "Blob": true,
+        "FileReader": true,
+        "FormData": true,
+        "URLSearchParams.prototype.isPrototypeOf": true,
+        "XMLHttpRequest": true
+      }
+    },
+    "crypto-browserify": {
+      "packages": {
+        "browserify-cipher": true,
+        "browserify-sign": true,
+        "create-ecdh": true,
+        "create-hash": true,
+        "create-hmac": true,
+        "diffie-hellman": true,
+        "pbkdf2": true,
+        "public-encrypt": true,
+        "randombytes": true,
+        "randomfill": true
+      }
+    },
+    "crypto-js": {
+      "globals": {
+        "define": true
+      }
+    },
+    "css-vendor": {
+      "globals": {
+        "document.createElement": true,
+        "document.documentElement": true,
+        "getComputedStyle": true
+      },
+      "packages": {
+        "@babel/runtime": true,
+        "is-in-browser": true
+      }
+    },
+    "currency-formatter": {
+      "packages": {
+        "accounting": true,
+        "locale-currency": true,
+        "object-assign": true
+      }
+    },
+    "data-queue": {
+      "packages": {
+        "events": true
+      }
+    },
+    "datastore-core": {
+      "packages": {
+        "async": true,
+        "buffer": true,
+        "interface-datastore": true,
+        "pull-many": true,
+        "pull-stream": true
+      }
+    },
+    "datastore-level": {
+      "packages": {
+        "buffer": true,
+        "encoding-down": true,
+        "interface-datastore": true,
+        "level-js": true,
+        "levelup": true,
+        "pull-stream": true
+      }
+    },
+    "datastore-pubsub": {
+      "packages": {
+        "assert": true,
+        "buffer": true,
+        "debug": true,
+        "err-code": true,
+        "interface-datastore": true,
+        "multibase": true
+      }
+    },
+    "debounce": {
+      "globals": {
+        "clearTimeout": true,
+        "setTimeout": true
+      }
+    },
+    "debounce-stream": {
+      "packages": {
+        "debounce": true,
+        "duplexer": true,
+        "through": true
+      }
+    },
+    "debug": {
+      "globals": {
+        "chrome": true,
+        "console": true,
+        "document": true,
+        "localStorage": true,
+        "navigator": true,
+        "process": true
+      },
+      "packages": {
+        "ms": true,
+        "process": true
+      }
+    },
+    "deep-equal": {
+      "packages": {
+        "is-arguments": true,
+        "is-date-object": true,
+        "is-regex": true,
+        "object-is": true,
+        "object-keys": true,
+        "regexp.prototype.flags": true
+      }
+    },
+    "deep-extend": {
+      "packages": {
+        "buffer": true
+      }
+    },
+    "deferred-leveldown": {
+      "packages": {
+        "abstract-leveldown": true,
+        "inherits": true
+      }
+    },
+    "define-properties": {
+      "packages": {
+        "object-keys": true
+      }
+    },
+    "des.js": {
+      "packages": {
+        "inherits": true,
+        "minimalistic-assert": true
+      }
+    },
+    "did-jwt": {
+      "packages": {
+        "@babel/runtime": true,
+        "@stablelib/utf8": true,
+        "base64url": true,
+        "buffer": true,
+        "did-resolver": true,
+        "elliptic": true,
+        "js-sha256": true,
+        "js-sha3": true,
+        "tweetnacl": true,
+        "tweetnacl-util": true,
+        "uport-base64url": true
+      }
+    },
+    "diffie-hellman": {
+      "packages": {
+        "bn.js": true,
+        "buffer": true,
+        "miller-rabin": true,
+        "randombytes": true
+      }
+    },
+    "dlv": {
+      "globals": {
+        "define": true
+      }
+    },
+    "dnd-core": {
+      "packages": {
+        "asap": true,
+        "invariant": true,
+        "lodash": true,
+        "redux": true
+      }
+    },
+    "dom-helpers": {
+      "globals": {
+        "document": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "@babel/runtime": true
+      }
+    },
+    "drbg.js": {
+      "packages": {
+        "buffer": true,
+        "create-hmac": true
+      }
+    },
+    "duplexer": {
+      "packages": {
+        "stream-browserify": true
+      }
+    },
+    "elliptic": {
+      "packages": {
+        "bn.js": true,
+        "brorand": true,
+        "hash.js": true,
+        "hmac-drbg": true,
+        "inherits": true,
+        "minimalistic-assert": true,
+        "minimalistic-crypto-utils": true
+      }
+    },
+    "encoding-down": {
+      "packages": {
+        "abstract-leveldown": true,
+        "inherits": true,
+        "level-codec": true,
+        "level-errors": true
+      }
+    },
+    "end-of-stream": {
+      "packages": {
+        "once": true,
+        "process": true
+      }
+    },
+    "engine.io-client": {
+      "globals": {
+        "MozWebSocket": true,
+        "WebSocket": true,
+        "XDomainRequest": true,
+        "XMLHttpRequest": true,
+        "addEventListener": true,
+        "attachEvent": true,
+        "clearTimeout": true,
+        "document": true,
+        "location": true,
+        "navigator": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "browser-resolve": true,
+        "buffer": true,
+        "component-emitter": true,
+        "component-inherit": true,
+        "debug": true,
+        "engine.io-parser": true,
+        "has-cors": true,
+        "indexof": true,
+        "parseqs": true,
+        "parseuri": true,
+        "yeast": true
+      }
+    },
+    "engine.io-parser": {
+      "globals": {
+        "FileReader": true,
+        "btoa": true,
+        "navigator": true
+      },
+      "packages": {
+        "after": true,
+        "arraybuffer.slice": true,
+        "base64-arraybuffer": true,
+        "blob": true,
+        "has-binary2": true
+      }
+    },
+    "errno": {
+      "packages": {
+        "prr": true
+      }
+    },
+    "es-abstract": {
+      "packages": {
+        "function-bind": true,
+        "has-symbols": true
+      }
+    },
+    "eth-block-tracker": {
+      "globals": {
+        "clearTimeout": true,
+        "console.error": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "@metamask/safe-event-emitter": true,
+        "eth-query": true,
+        "json-rpc-random-id": true,
+        "pify": true,
+        "safe-event-emitter": true
+      }
+    },
+    "eth-ens-namehash": {
+      "globals": {
+        "name": "write"
+      },
+      "packages": {
+        "buffer": true,
+        "idna-uts46": true,
+        "idna-uts46-hx": true,
+        "js-sha3": true
+      }
+    },
+    "eth-hd-keyring": {
+      "packages": {
+        "bip39": true,
+        "eth-sig-util": true,
+        "eth-simple-keyring": true,
+        "ethereumjs-wallet": true
+      }
+    },
+    "eth-json-rpc-filters": {
+      "globals": {
+        "console.error": true,
+        "results": "write"
+      },
+      "packages": {
+        "await-semaphore": true,
+        "eth-json-rpc-middleware": true,
+        "eth-query": true,
+        "json-rpc-engine": true,
+        "lodash.flatmap": true,
+        "pify": true,
+        "safe-event-emitter": true
+      }
+    },
+    "eth-json-rpc-infura": {
+      "globals": {
+        "setTimeout": true
+      },
+      "packages": {
+        "eth-json-rpc-middleware": true,
+        "eth-rpc-errors": true,
+        "json-rpc-engine": true,
+        "node-fetch": true
+      }
+    },
+    "eth-json-rpc-middleware": {
+      "globals": {
+        "console.error": true,
+        "fetch": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "btoa": true,
+        "clone": true,
+        "eth-rpc-errors": true,
+        "eth-sig-util": true,
+        "json-rpc-engine": true,
+        "json-stable-stringify": true,
+        "node-fetch": true,
+        "pify": true,
+        "safe-event-emitter": true,
+        "url": true
+      }
+    },
+    "eth-keyring-controller": {
+      "packages": {
+        "bip39": true,
+        "browser-passworder": true,
+        "eth-hd-keyring": true,
+        "eth-sig-util": true,
+        "eth-simple-keyring": true,
+        "ethereumjs-util": true,
+        "events": true,
+        "loglevel": true,
+        "obs-store": true
+      }
+    },
+    "eth-method-registry": {
+      "packages": {
+        "ethjs": true
+      }
+    },
+    "eth-phishing-detect": {
+      "packages": {
+        "fast-levenshtein": true
+      }
+    },
+    "eth-query": {
+      "packages": {
+        "json-rpc-random-id": true,
+        "xtend": true
+      }
+    },
+    "eth-rpc-errors": {
+      "packages": {
+        "fast-safe-stringify": true
+      }
+    },
+    "eth-sig-util": {
+      "packages": {
+        "buffer": true,
+        "ethereumjs-abi": true,
+        "ethereumjs-util": true,
+        "tweetnacl": true,
+        "tweetnacl-util": true
+      }
+    },
+    "eth-simple-keyring": {
+      "packages": {
+        "buffer": true,
+        "eth-sig-util": true,
+        "ethereumjs-util": true,
+        "ethereumjs-wallet": true,
+        "events": true
+      }
+    },
+    "eth-trezor-keyring": {
+      "globals": {
+        "setTimeout": true
+      },
+      "packages": {
+        "@ethereumjs/tx": true,
+        "buffer": true,
+        "ethereumjs-util": true,
+        "events": true,
+        "hdkey": true,
+        "trezor-connect": true
+      }
+    },
+    "ethereum-cryptography": {
+      "packages": {
+        "assert": true,
+        "bs58check": true,
+        "buffer": true,
+        "create-hmac": true,
+        "hash.js": true,
+        "keccak": true,
+        "randombytes": true,
+        "safe-buffer": true,
+        "secp256k1": true
+      }
+    },
+    "ethereumjs-abi": {
+      "packages": {
+        "bn.js": true,
+        "buffer": true,
+        "ethereumjs-util": true
+      }
+    },
+    "ethereumjs-tx": {
+      "packages": {
+        "buffer": true,
+        "ethereum-common": true,
+        "ethereumjs-util": true
+      }
+    },
+    "ethereumjs-util": {
+      "packages": {
+        "assert": true,
+        "bn.js": true,
+        "buffer": true,
+        "create-hash": true,
+        "elliptic": true,
+        "ethereum-cryptography": true,
+        "ethjs-util": true,
+        "is-buffer": true,
+        "keccak": true,
+        "rlp": true,
+        "safe-buffer": true,
+        "secp256k1": true
+      }
+    },
+    "ethereumjs-wallet": {
+      "packages": {
+        "aes-js": true,
+        "bs58check": true,
+        "buffer": true,
+        "crypto-browserify": true,
+        "ethereum-cryptography": true,
+        "ethereumjs-util": true,
+        "randombytes": true,
+        "safe-buffer": true,
+        "scrypt-js": true,
+        "scryptsy": true,
+        "utf8": true,
+        "uuid": true
+      }
+    },
+    "ethers": {
+      "globals": {
+        "MessageChannel": true,
+        "XMLHttpRequest": true,
+        "atob": true,
+        "btoa": true,
+        "clearInterval": true,
+        "clearTimeout": true,
+        "console": true,
+        "crypto.getRandomValues": true,
+        "define": true,
+        "setInterval": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "@ethersproject/abi": true,
+        "@ethersproject/abstract-signer": true,
+        "@ethersproject/address": true,
+        "@ethersproject/base64": true,
+        "@ethersproject/basex": true,
+        "@ethersproject/bignumber": true,
+        "@ethersproject/bytes": true,
+        "@ethersproject/constants": true,
+        "@ethersproject/contracts": true,
+        "@ethersproject/hash": true,
+        "@ethersproject/hdnode": true,
+        "@ethersproject/json-wallets": true,
+        "@ethersproject/keccak256": true,
+        "@ethersproject/logger": true,
+        "@ethersproject/properties": true,
+        "@ethersproject/providers": true,
+        "@ethersproject/random": true,
+        "@ethersproject/rlp": true,
+        "@ethersproject/sha2": true,
+        "@ethersproject/signing-key": true,
+        "@ethersproject/solidity": true,
+        "@ethersproject/strings": true,
+        "@ethersproject/transactions": true,
+        "@ethersproject/units": true,
+        "@ethersproject/wallet": true,
+        "@ethersproject/web": true,
+        "@ethersproject/wordlists": true
+      }
+    },
+    "ethjs": {
+      "globals": {
+        "clearInterval": true,
+        "setInterval": true
+      },
+      "packages": {
+        "bn.js": true,
+        "buffer": true,
+        "ethjs-abi": true,
+        "ethjs-contract": true,
+        "ethjs-filter": true,
+        "ethjs-provider-http": true,
+        "ethjs-query": true,
+        "ethjs-unit": true,
+        "ethjs-util": true,
+        "js-sha3": true,
+        "number-to-bn": true
+      }
+    },
+    "ethjs-abi": {
+      "packages": {
+        "bn.js": true,
+        "buffer": true,
+        "js-sha3": true,
+        "number-to-bn": true
+      }
+    },
+    "ethjs-contract": {
+      "packages": {
+        "babel-runtime": true,
+        "ethjs-abi": true,
+        "ethjs-filter": true,
+        "ethjs-util": true,
+        "js-sha3": true,
+        "promise-to-callback": true
+      }
+    },
+    "ethjs-ens": {
+      "packages": {
+        "eth-ens-namehash": true,
+        "ethereum-ens-network-map": true,
+        "ethjs-contract": true,
+        "ethjs-query": true
+      }
+    },
+    "ethjs-filter": {
+      "globals": {
+        "clearInterval": true,
+        "setInterval": true
+      }
+    },
+    "ethjs-format": {
+      "packages": {
+        "ethjs-schema": true,
+        "ethjs-util": true,
+        "number-to-bn": true,
+        "strip-hex-prefix": true
+      }
+    },
+    "ethjs-provider-http": {
+      "packages": {
+        "xhr2": true
+      }
+    },
+    "ethjs-query": {
+      "globals": {
+        "console": true
+      },
+      "packages": {
+        "babel-runtime": true,
+        "ethjs-format": true,
+        "ethjs-rpc": true,
+        "promise-to-callback": true
+      }
+    },
+    "ethjs-rpc": {
+      "packages": {
+        "promise-to-callback": true
+      }
+    },
+    "ethjs-unit": {
+      "packages": {
+        "bn.js": true,
+        "number-to-bn": true
+      }
+    },
+    "ethjs-util": {
+      "packages": {
+        "buffer": true,
+        "is-hex-prefixed": true,
+        "strip-hex-prefix": true
+      }
+    },
+    "events": {
+      "globals": {
+        "console": true
+      }
+    },
+    "evp_bytestokey": {
+      "packages": {
+        "md5.js": true,
+        "safe-buffer": true
+      }
+    },
+    "extension-port-stream": {
+      "packages": {
+        "buffer": true,
+        "stream-browserify": true
+      }
+    },
+    "extensionizer": {
+      "globals": {
+        "browser": true,
+        "chrome": true
+      }
+    },
+    "fast-json-patch": {
+      "globals": {
+        "addEventListener": true,
+        "clearTimeout": true,
+        "removeEventListener": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "fast-deep-equal": true
+      }
+    },
+    "fast-levenshtein": {
+      "globals": {
+        "Intl": true,
+        "Levenshtein": "write",
+        "console.log": true,
+        "define": true,
+        "importScripts": true,
+        "postMessage": true
+      }
+    },
+    "fsm-event": {
+      "packages": {
+        "assert": true,
+        "events": true,
+        "fsm": true
+      }
+    },
+    "fuse.js": {
+      "globals": {
+        "console": true,
+        "define": true
+      }
+    },
+    "get-browser-rtc": {
+      "globals": {
+        "RTCIceCandidate": true,
+        "RTCPeerConnection": true,
+        "RTCSessionDescription": true,
+        "mozRTCIceCandidate": true,
+        "mozRTCPeerConnection": true,
+        "mozRTCSessionDescription": true,
+        "webkitRTCIceCandidate": true,
+        "webkitRTCPeerConnection": true,
+        "webkitRTCSessionDescription": true
+      }
+    },
+    "get-params": {
+      "globals": {
+        "GetParams": "write"
+      }
+    },
+    "graphql-request": {
+      "globals": {
+        "fetch": true
+      },
+      "packages": {
+        "cross-fetch": true
+      }
+    },
+    "hamt-sharding": {
+      "packages": {
+        "is-buffer": true,
+        "sparse-array": true
+      }
+    },
+    "has-binary2": {
+      "globals": {
+        "Blob": true,
+        "File": true
+      },
+      "packages": {
+        "buffer": true,
+        "isarray": true
+      }
+    },
+    "has-cors": {
+      "globals": {
+        "XMLHttpRequest": true
+      }
+    },
+    "hash-base": {
+      "packages": {
+        "inherits": true,
+        "safe-buffer": true,
+        "stream-browserify": true
+      }
+    },
+    "hash.js": {
+      "packages": {
+        "inherits": true,
+        "minimalistic-assert": true
+      }
+    },
+    "hdkey": {
+      "packages": {
+        "assert": true,
+        "coinstring": true,
+        "crypto-browserify": true,
+        "safe-buffer": true,
+        "secp256k1": true
+      }
+    },
+    "heap": {
+      "globals": {
+        "define": true
+      }
+    },
+    "hi-base32": {
+      "globals": {
+        "define": true
+      },
+      "packages": {
+        "process": true
+      }
+    },
+    "history": {
+      "globals": {
+        "addEventListener": true,
+        "confirm": true,
+        "document": true,
+        "history": true,
+        "location": true,
+        "navigator.userAgent": true,
+        "removeEventListener": true
+      },
+      "packages": {
+        "resolve-pathname": true,
+        "tiny-invariant": true,
+        "tiny-warning": true,
+        "value-equal": true
+      }
+    },
+    "hmac-drbg": {
+      "packages": {
+        "hash.js": true,
+        "minimalistic-assert": true,
+        "minimalistic-crypto-utils": true
+      }
+    },
+    "hoist-non-react-statics": {
+      "packages": {
+        "react-is": true
+      }
+    },
+    "https-browserify": {
+      "packages": {
+        "stream-http": true,
+        "url": true
+      }
+    },
+    "https-did-resolver": {
+      "globals": {
+        "XMLHttpRequest": true
+      },
+      "packages": {
+        "browser-resolve": true,
+        "did-resolver": true
+      }
+    },
+    "human-to-milliseconds": {
+      "packages": {
+        "promisify-es6": true
+      }
+    },
+    "idb-readable-stream": {
+      "globals": {
+        "IDBKeyRange.bound": true,
+        "IDBKeyRange.lowerBound": true,
+        "IDBKeyRange.upperBound": true
+      },
+      "packages": {
+        "stream-browserify": true,
+        "xtend": true
+      }
+    },
+    "idna-uts46": {
+      "globals": {
+        "define": true
+      },
+      "packages": {
+        "punycode": true
+      }
+    },
+    "idna-uts46-hx": {
+      "globals": {
+        "define": true
+      },
+      "packages": {
+        "punycode": true
+      }
+    },
+    "immediate": {
+      "globals": {
+        "MessageChannel": true,
+        "MutationObserver": true,
+        "WebKitMutationObserver": true,
+        "clearTimeout": true,
+        "document.createElement": true,
+        "document.createTextNode": true,
+        "document.documentElement.appendChild": true,
+        "setImmediate": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "process": true
+      }
+    },
+    "interface-connection": {
+      "packages": {
+        "pull-defer": true
+      }
+    },
+    "interface-datastore": {
+      "packages": {
+        "async": true,
+        "buffer": true,
+        "class-is": true,
+        "err-code": true,
+        "os-browserify": true,
+        "path-browserify": true,
+        "pull-defer": true,
+        "pull-stream": true,
+        "uuid": true
+      }
+    },
+    "ip": {
+      "packages": {
+        "buffer": true,
+        "os-browserify": true
+      }
+    },
+    "ipfs": {
+      "globals": {
+        "AbortController": true,
+        "clearInterval": true,
+        "clearTimeout": true,
+        "console.log": true,
+        "fetch": true,
+        "setInterval": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "async": true,
+        "async-iterator-all": true,
+        "async-iterator-to-pull-stream": true,
+        "async-iterator-to-stream": true,
+        "base32.js": true,
+        "bignumber.js": true,
+        "browser-resolve": true,
+        "buffer": true,
+        "callbackify": true,
+        "cids": true,
+        "class-is": true,
+        "datastore-core": true,
+        "datastore-pubsub": true,
+        "debug": true,
+        "dlv": true,
+        "err-code": true,
+        "events": true,
+        "fnv1a": true,
+        "fsm-event": true,
+        "human-to-milliseconds": true,
+        "interface-datastore": true,
+        "ipfs-bitswap": true,
+        "ipfs-block": true,
+        "ipfs-block-service": true,
+        "ipfs-mfs": true,
+        "ipfs-repo": true,
+        "ipfs-unixfs": true,
+        "ipfs-unixfs-exporter": true,
+        "ipfs-unixfs-importer": true,
+        "ipfs-utils": true,
+        "ipld": true,
+        "ipld-dag-cbor": true,
+        "ipld-dag-pb": true,
+        "ipld-raw": true,
+        "ipns": true,
+        "is-buffer": true,
+        "is-ipfs": true,
+        "is-pull-stream": true,
+        "is-stream": true,
+        "iso-url": true,
+        "just-flatten-it": true,
+        "kind-of": true,
+        "libp2p": true,
+        "libp2p-bootstrap": true,
+        "libp2p-crypto": true,
+        "libp2p-kad-dht": true,
+        "libp2p-keychain": true,
+        "libp2p-record": true,
+        "libp2p-secio": true,
+        "libp2p-webrtc-star": true,
+        "libp2p-websocket-star-multi": true,
+        "libp2p-websockets": true,
+        "mafmt": true,
+        "merge-options": true,
+        "multiaddr": true,
+        "multiaddr-to-uri": true,
+        "multibase": true,
+        "multicodec": true,
+        "multihashes": true,
+        "multihashing-async": true,
+        "peer-book": true,
+        "peer-id": true,
+        "peer-info": true,
+        "promisify-es6": true,
+        "protons": true,
+        "pull-cat": true,
+        "pull-defer": true,
+        "pull-mplex": true,
+        "pull-pushable": true,
+        "pull-sort": true,
+        "pull-stream": true,
+        "pull-stream-to-async-iterator": true,
+        "pull-stream-to-stream": true,
+        "pull-traverse": true,
+        "readable-stream": true,
+        "receptacle": true,
+        "stream-to-pull-stream": true,
+        "superstruct": true,
+        "timers-browserify": true,
+        "varint": true
+      }
+    },
+    "ipfs-bitswap": {
+      "globals": {
+        "clearInterval": true,
+        "clearTimeout": true,
+        "setInterval": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "assert": true,
+        "async": true,
+        "bignumber.js": true,
+        "cids": true,
+        "debug": true,
+        "events": true,
+        "ipfs-block": true,
+        "just-debounce-it": true,
+        "lodash.isequalwith": true,
+        "moving-average": true,
+        "multicodec": true,
+        "multihashing-async": true,
+        "protons": true,
+        "pull-length-prefixed": true,
+        "pull-stream": true,
+        "varint-decoder": true
+      }
+    },
+    "ipfs-block": {
+      "packages": {
+        "cids": true,
+        "class-is": true,
+        "is-buffer": true
+      }
+    },
+    "ipfs-block-service": {
+      "packages": {
+        "async": true
+      }
+    },
+    "ipfs-log": {
+      "globals": {
+        "clearTimeout": true,
+        "console.warn": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "buffer": true,
+        "json-stringify-deterministic": true,
+        "orbit-db-io": true,
+        "p-each-series": true,
+        "p-map": true,
+        "p-whilst": true
+      }
+    },
+    "ipfs-mfs": {
+      "globals": {
+        "Blob": true,
+        "FileReader": true
+      },
+      "packages": {
+        "assert": true,
+        "async-iterator-last": true,
+        "browser-resolve": true,
+        "buffer": true,
+        "cids": true,
+        "debug": true,
+        "err-code": true,
+        "hamt-sharding": true,
+        "interface-datastore": true,
+        "ipfs-unixfs": true,
+        "ipfs-unixfs-exporter": true,
+        "ipfs-unixfs-importer": true,
+        "ipld-dag-pb": true,
+        "mortice": true,
+        "multicodec": true,
+        "multihashes": true,
+        "promisify-es6": true
+      }
+    },
+    "ipfs-mini": {
+      "globals": {
+        "XMLHttpRequest": true
+      }
+    },
+    "ipfs-pubsub-1on1": {
+      "globals": {
+        "clearInterval": true,
+        "setInterval": true
+      },
+      "packages": {
+        "events": true,
+        "path-browserify": true,
+        "safe-buffer": true
+      }
+    },
+    "ipfs-pubsub-peer-monitor": {
+      "globals": {
+        "clearInterval": true,
+        "setInterval": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "events": true
+      }
+    },
+    "ipfs-repo": {
+      "packages": {
+        "assert": true,
+        "async": true,
+        "base32.js": true,
+        "bignumber.js": true,
+        "buffer": true,
+        "cids": true,
+        "datastore-core": true,
+        "datastore-level": true,
+        "debug": true,
+        "dlv": true,
+        "interface-datastore": true,
+        "ipfs-block": true,
+        "just-safe-set": true,
+        "path-browserify": true,
+        "pull-stream": true,
+        "sort-keys": true,
+        "timers-browserify": true
+      }
+    },
+    "ipfs-unixfs": {
+      "packages": {
+        "protons": true
+      }
+    },
+    "ipfs-unixfs-exporter": {
+      "packages": {
+        "async-iterator-last": true,
+        "buffer": true,
+        "cids": true,
+        "err-code": true,
+        "hamt-sharding": true,
+        "ipfs-unixfs": true,
+        "ipfs-unixfs-importer": true,
+        "is-buffer": true
+      }
+    },
+    "ipfs-unixfs-importer": {
+      "packages": {
+        "async-iterator-all": true,
+        "async-iterator-batch": true,
+        "async-iterator-first": true,
+        "bl": true,
+        "buffer": true,
+        "deep-extend": true,
+        "err-code": true,
+        "hamt-sharding": true,
+        "ipfs-unixfs": true,
+        "ipld-dag-pb": true,
+        "multicodec": true,
+        "multihashes": true,
+        "multihashing-async": true,
+        "rabin-wasm": true,
+        "superstruct": true
+      }
+    },
+    "ipfs-utils": {
+      "globals": {
+        "FileReader": true
+      },
+      "packages": {
+        "is-buffer": true,
+        "is-pull-stream": true,
+        "is-stream": true,
+        "kind-of": true,
+        "readable-stream": true
+      }
+    },
+    "ipld": {
+      "packages": {
+        "cids": true,
+        "ipfs-block": true,
+        "ipld-dag-cbor": true,
+        "ipld-dag-pb": true,
+        "ipld-raw": true,
+        "is-buffer": true,
+        "merge-options": true,
+        "multicodec": true,
+        "promisify-es6": true,
+        "typical": true
+      }
+    },
+    "ipld-dag-cbor": {
+      "packages": {
+        "borc": true,
+        "buffer": true,
+        "cids": true,
+        "is-buffer": true,
+        "is-circular": true,
+        "multicodec": true,
+        "multihashing-async": true
+      }
+    },
+    "ipld-dag-pb": {
+      "packages": {
+        "assert": true,
+        "buffer": true,
+        "cids": true,
+        "class-is": true,
+        "is-buffer": true,
+        "multicodec": true,
+        "multihashing-async": true,
+        "protons": true,
+        "stable": true
+      }
+    },
+    "ipld-raw": {
+      "packages": {
+        "cids": true,
+        "multicodec": true,
+        "multihashing-async": true
+      }
+    },
+    "ipns": {
+      "packages": {
+        "base32-encode": true,
+        "buffer": true,
+        "debug": true,
+        "interface-datastore": true,
+        "libp2p-crypto": true,
+        "multihashes": true,
+        "peer-id": true,
+        "protons": true,
+        "timestamp-nano": true
+      }
+    },
+    "is-dom": {
+      "globals": {
+        "Node": true
+      },
+      "packages": {
+        "is-object": true,
+        "is-window": true
+      }
+    },
+    "is-in-browser": {
+      "globals": {
+        "document": true
+      }
+    },
+    "is-ip": {
+      "packages": {
+        "ip-regex": true
+      }
+    },
+    "is-ipfs": {
+      "packages": {
+        "bs58": true,
+        "buffer": true,
+        "cids": true,
+        "mafmt": true,
+        "multiaddr": true,
+        "multibase": true,
+        "multihashes": true
+      }
+    },
+    "is-regex": {
+      "packages": {
+        "has-symbols": true
+      }
+    },
+    "iso-random-stream": {
+      "globals": {
+        "crypto": true,
+        "msCrypto": true
+      },
+      "packages": {
+        "buffer": true
+      }
+    },
+    "iso-url": {
+      "globals": {
+        "URL": true,
+        "URLSearchParams": true,
+        "location": true
+      }
+    },
+    "isomorphic-fetch": {
+      "globals": {
+        "fetch.bind": true
+      },
+      "packages": {
+        "whatwg-fetch": true
+      }
+    },
+    "js-base64": {
+      "globals": {
+        "Base64": "write",
+        "TextDecoder": true,
+        "TextEncoder": true,
+        "atob": true,
+        "btoa": true,
+        "define": true
+      },
+      "packages": {
+        "buffer": true
+      }
+    },
+    "js-sha256": {
+      "globals": {
+        "define": true
+      },
+      "packages": {
+        "process": true
+      }
+    },
+    "js-sha3": {
+      "globals": {
+        "define": true
+      },
+      "packages": {
+        "process": true
+      }
+    },
+    "jsan": {
+      "globals": {
+        "console.warn": true
+      }
+    },
+    "json-rpc-engine": {
+      "packages": {
+        "@metamask/safe-event-emitter": true,
+        "eth-rpc-errors": true,
+        "safe-event-emitter": true
+      }
+    },
+    "json-rpc-middleware-stream": {
+      "packages": {
+        "readable-stream": true
+      }
+    },
+    "json-stable-stringify": {
+      "packages": {
+        "jsonify": true
+      }
+    },
+    "jsonschema": {
+      "packages": {
+        "url": true
+      }
+    },
+    "jss": {
+      "globals": {
+        "CSS": true,
+        "document.createElement": true,
+        "document.querySelector": true
+      },
+      "packages": {
+        "@babel/runtime": true,
+        "is-in-browser": true,
+        "tiny-warning": true
+      }
+    },
+    "jss-plugin-camel-case": {
+      "packages": {
+        "hyphenate-style-name": true
+      }
+    },
+    "jss-plugin-default-unit": {
+      "globals": {
+        "CSS": true
+      },
+      "packages": {
+        "jss": true
+      }
+    },
+    "jss-plugin-global": {
+      "packages": {
+        "@babel/runtime": true,
+        "jss": true
+      }
+    },
+    "jss-plugin-nested": {
+      "packages": {
+        "@babel/runtime": true,
+        "tiny-warning": true
+      }
+    },
+    "jss-plugin-rule-value-function": {
+      "packages": {
+        "jss": true,
+        "tiny-warning": true
+      }
+    },
+    "jss-plugin-vendor-prefixer": {
+      "packages": {
+        "css-vendor": true,
+        "jss": true
+      }
+    },
+    "just-debounce-it": {
+      "globals": {
+        "clearTimeout": true,
+        "setTimeout": true
+      }
+    },
+    "k-bucket": {
+      "packages": {
+        "events": true,
+        "randombytes": true
+      }
+    },
+    "keccak": {
+      "packages": {
+        "buffer": true,
+        "inherits": true,
+        "safe-buffer": true,
+        "stream-browserify": true
+      }
+    },
+    "latency-monitor": {
+      "globals": {
+        "clearInterval": true,
+        "clearTimeout": true,
+        "document": true,
+        "performance": true,
+        "setInterval": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "debug": true,
+        "events": true,
+        "lodash": true,
+        "process": true
+      }
+    },
+    "level-codec": {
+      "packages": {
+        "buffer": true
+      }
+    },
+    "level-errors": {
+      "packages": {
+        "errno": true
+      }
+    },
+    "level-iterator-stream": {
+      "packages": {
+        "inherits": true,
+        "readable-stream": true,
+        "xtend": true
+      }
+    },
+    "level-js": {
+      "globals": {
+        "IDBKeyRange.bound": true,
+        "IDBKeyRange.lowerBound": true,
+        "IDBKeyRange.only": true,
+        "IDBKeyRange.upperBound": true,
+        "indexedDB": true
+      },
+      "packages": {
+        "abstract-leveldown": true,
+        "buffer": true,
+        "idb-readable-stream": true,
+        "immediate": true,
+        "inherits": true,
+        "is-buffer": true,
+        "ltgt": true,
+        "process": true,
+        "stream-browserify": true,
+        "typedarray-to-buffer": true,
+        "util": true,
+        "xtend": true
+      }
+    },
+    "levelup": {
+      "packages": {
+        "assert": true,
+        "deferred-leveldown": true,
+        "events": true,
+        "level-errors": true,
+        "level-iterator-stream": true,
+        "process": true,
+        "util": true,
+        "xtend": true
+      }
+    },
+    "libp2p": {
+      "packages": {
+        "async": true,
+        "debug": true,
+        "err-code": true,
+        "events": true,
+        "fsm-event": true,
+        "is-buffer": true,
+        "libp2p-connection-manager": true,
+        "libp2p-floodsub": true,
+        "libp2p-ping": true,
+        "libp2p-switch": true,
+        "libp2p-websockets": true,
+        "multiaddr": true,
+        "once": true,
+        "peer-book": true,
+        "peer-id": true,
+        "peer-info": true,
+        "process": true,
+        "superstruct": true
+      }
+    },
+    "libp2p-bootstrap": {
+      "globals": {
+        "clearInterval": true,
+        "setInterval": true
+      },
+      "packages": {
+        "async": true,
+        "debug": true,
+        "events": true,
+        "mafmt": true,
+        "multiaddr": true,
+        "peer-id": true,
+        "peer-info": true
+      }
+    },
+    "libp2p-circuit": {
+      "packages": {
+        "async": true,
+        "debug": true,
+        "events": true,
+        "interface-connection": true,
+        "mafmt": true,
+        "multiaddr": true,
+        "once": true,
+        "peer-id": true,
+        "peer-info": true,
+        "protons": true,
+        "pull-handshake": true,
+        "pull-length-prefixed": true,
+        "pull-stream": true
+      }
+    },
+    "libp2p-connection-manager": {
+      "packages": {
+        "debug": true,
+        "events": true,
+        "latency-monitor": true
+      }
+    },
+    "libp2p-crypto": {
+      "globals": {
+        "crypto": true,
+        "msCrypto": true
+      },
+      "packages": {
+        "asn1.js": true,
+        "async": true,
+        "browserify-aes": true,
+        "bs58": true,
+        "buffer": true,
+        "iso-random-stream": true,
+        "libp2p-crypto-secp256k1": true,
+        "multihashing-async": true,
+        "node-forge": true,
+        "protons": true,
+        "tweetnacl": true
+      }
+    },
+    "libp2p-crypto-secp256k1": {
+      "packages": {
+        "async": true,
+        "bs58": true,
+        "multihashing-async": true,
+        "secp256k1": true
+      }
+    },
+    "libp2p-floodsub": {
+      "packages": {
+        "assert": true,
+        "async": true,
+        "debug": true,
+        "libp2p-pubsub": true,
+        "pull-length-prefixed": true,
+        "pull-stream": true
+      }
+    },
+    "libp2p-identify": {
+      "globals": {
+        "console.warn": true
+      },
+      "packages": {
+        "buffer": true,
+        "multiaddr": true,
+        "peer-id": true,
+        "peer-info": true,
+        "protons": true,
+        "pull-length-prefixed": true,
+        "pull-stream": true
+      }
+    },
+    "libp2p-kad-dht": {
+      "globals": {
+        "clearInterval": true,
+        "clearTimeout": true,
+        "setInterval": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "abort-controller": true,
+        "assert": true,
+        "async": true,
+        "base32.js": true,
+        "buffer": true,
+        "cids": true,
+        "debug": true,
+        "err-code": true,
+        "events": true,
+        "hashlru": true,
+        "heap": true,
+        "interface-datastore": true,
+        "is-buffer": true,
+        "k-bucket": true,
+        "libp2p-crypto": true,
+        "libp2p-record": true,
+        "multihashes": true,
+        "multihashing-async": true,
+        "p-queue": true,
+        "p-times": true,
+        "peer-id": true,
+        "peer-info": true,
+        "promise-to-callback": true,
+        "promisify-es6": true,
+        "protons": true,
+        "pull-length-prefixed": true,
+        "pull-stream": true,
+        "pull-stream-to-async-iterator": true,
+        "varint": true,
+        "xor-distance": true
+      }
+    },
+    "libp2p-keychain": {
+      "globals": {
+        "setTimeout": true
+      },
+      "packages": {
+        "async": true,
+        "buffer": true,
+        "err-code": true,
+        "interface-datastore": true,
+        "libp2p-crypto": true,
+        "merge-options": true,
+        "node-forge": true,
+        "pull-stream": true,
+        "sanitize-filename": true
+      }
+    },
+    "libp2p-ping": {
+      "packages": {
+        "debug": true,
+        "events": true,
+        "libp2p-crypto": true,
+        "pull-handshake": true,
+        "pull-stream": true
+      }
+    },
+    "libp2p-pubsub": {
+      "packages": {
+        "async": true,
+        "bs58": true,
+        "buffer": true,
+        "debug": true,
+        "err-code": true,
+        "events": true,
+        "is-buffer": true,
+        "libp2p-crypto": true,
+        "protons": true,
+        "pull-length-prefixed": true,
+        "pull-pushable": true,
+        "pull-stream": true,
+        "time-cache": true
+      }
+    },
+    "libp2p-record": {
+      "packages": {
+        "assert": true,
+        "async": true,
+        "buffer": true,
+        "buffer-split": true,
+        "err-code": true,
+        "is-buffer": true,
+        "multihashing-async": true,
+        "protons": true
+      }
+    },
+    "libp2p-secio": {
+      "packages": {
+        "assert": true,
+        "async": true,
+        "buffer": true,
+        "debug": true,
+        "interface-connection": true,
+        "libp2p-crypto": true,
+        "multihashing-async": true,
+        "once": true,
+        "peer-id": true,
+        "peer-info": true,
+        "protons": true,
+        "pull-defer": true,
+        "pull-handshake": true,
+        "pull-length-prefixed": true,
+        "pull-stream": true
+      }
+    },
+    "libp2p-switch": {
+      "packages": {
+        "assert": true,
+        "async": true,
+        "bignumber.js": true,
+        "class-is": true,
+        "debug": true,
+        "err-code": true,
+        "events": true,
+        "fsm-event": true,
+        "hashlru": true,
+        "interface-connection": true,
+        "libp2p-circuit": true,
+        "libp2p-identify": true,
+        "moving-average": true,
+        "multiaddr": true,
+        "multistream-select": true,
+        "once": true,
+        "peer-id": true,
+        "peer-info": true,
+        "pull-stream": true,
+        "retimer": true
+      }
+    },
+    "libp2p-webrtc-star": {
+      "packages": {
+        "async": true,
+        "class-is": true,
+        "debug": true,
+        "events": true,
+        "interface-connection": true,
+        "mafmt": true,
+        "multiaddr": true,
+        "once": true,
+        "peer-id": true,
+        "peer-info": true,
+        "simple-peer": true,
+        "socket.io-client": true,
+        "stream-to-pull-stream": true,
+        "webrtcsupport": true
+      }
+    },
+    "libp2p-websocket-star": {
+      "globals": {
+        "console.error": true
+      },
+      "packages": {
+        "async": true,
+        "buffer": true,
+        "class-is": true,
+        "debug": true,
+        "events": true,
+        "interface-connection": true,
+        "libp2p-crypto": true,
+        "mafmt": true,
+        "multiaddr": true,
+        "once": true,
+        "peer-id": true,
+        "peer-info": true,
+        "pull-stream": true,
+        "socket.io-client": true,
+        "socket.io-pull-stream": true,
+        "uuid": true
+      }
+    },
+    "libp2p-websocket-star-multi": {
+      "globals": {
+        "setTimeout": true
+      },
+      "packages": {
+        "async": true,
+        "debug": true,
+        "events": true,
+        "libp2p-websocket-star": true,
+        "mafmt": true,
+        "multiaddr": true,
+        "once": true
+      }
+    },
+    "libp2p-websockets": {
+      "packages": {
+        "class-is": true,
+        "debug": true,
+        "interface-connection": true,
+        "mafmt": true,
+        "multiaddr": true,
+        "multiaddr-to-uri": true,
+        "os-browserify": true,
+        "pull-ws": true
+      }
+    },
+    "locale-currency": {
+      "globals": {
+        "countryCode": true
+      }
+    },
+    "localforage": {
+      "globals": {
+        "Blob": true,
+        "BlobBuilder": true,
+        "FileReader": true,
+        "IDBKeyRange": true,
+        "MSBlobBuilder": true,
+        "MozBlobBuilder": true,
+        "OIndexedDB": true,
+        "WebKitBlobBuilder": true,
+        "atob": true,
+        "btoa": true,
+        "console.error": true,
+        "console.info": true,
+        "console.warn": true,
+        "define": true,
+        "fetch": true,
+        "indexedDB": true,
+        "localStorage": true,
+        "mozIndexedDB": true,
+        "msIndexedDB": true,
+        "navigator.platform": true,
+        "navigator.userAgent": true,
+        "openDatabase": true,
+        "setTimeout": true,
+        "webkitIndexedDB": true
+      }
+    },
+    "lodash": {
+      "globals": {
+        "define": true
+      }
+    },
+    "lodash.throttle": {
+      "globals": {
+        "clearTimeout": true,
+        "setTimeout": true
+      }
+    },
+    "loglevel": {
+      "globals": {
+        "console": true,
+        "define": true,
+        "document.cookie": true,
+        "localStorage": true,
+        "log": "write"
+      }
+    },
+    "logplease": {
+      "globals": {
+        "LOG": true,
+        "console.error": true,
+        "console.log": true
+      },
+      "packages": {
+        "browser-resolve": true,
+        "events": true,
+        "process": true,
+        "util": true
+      }
+    },
+    "lru": {
+      "packages": {
+        "events": true,
+        "inherits": true
+      }
+    },
+    "ltgt": {
+      "packages": {
+        "is-buffer": true
+      }
+    },
+    "luxon": {
+      "globals": {
+        "Intl": true
+      }
+    },
+    "mafmt": {
+      "packages": {
+        "multiaddr": true
+      }
+    },
+    "md5": {
+      "packages": {
+        "charenc": true,
+        "crypt": true,
+        "is-buffer": true
+      }
+    },
+    "md5.js": {
+      "packages": {
+        "hash-base": true,
+        "inherits": true,
+        "safe-buffer": true
+      }
+    },
+    "merge-options": {
+      "packages": {
+        "is-plain-obj": true
+      }
+    },
+    "miller-rabin": {
+      "packages": {
+        "bn.js": true,
+        "brorand": true
+      }
+    },
+    "mini-create-react-context": {
+      "packages": {
+        "@babel/runtime": true,
+        "gud": true,
+        "prop-types": true,
+        "react": true,
+        "tiny-warning": true
+      }
+    },
+    "mortice": {
+      "globals": {
+        "Worker": true
+      },
+      "packages": {
+        "browser-resolve": true,
+        "events": true,
+        "observable-webworkers": true,
+        "p-queue": true,
+        "process": true,
+        "promise-timeout": true,
+        "shortid": true
+      }
+    },
+    "multiaddr": {
+      "packages": {
+        "bs58": true,
+        "buffer": true,
+        "class-is": true,
+        "hi-base32": true,
+        "ip": true,
+        "is-ip": true,
+        "varint": true
+      }
+    },
+    "multiaddr-to-uri": {
+      "packages": {
+        "multiaddr": true
+      }
+    },
+    "multibase": {
+      "globals": {
+        "TextDecoder": true,
+        "TextEncoder": true
+      },
+      "packages": {
+        "@multiformats/base-x": true,
+        "base-x": true,
+        "buffer": true,
+        "web-encoding": true
+      }
+    },
+    "multicodec": {
+      "packages": {
+        "buffer": true,
+        "uint8arrays": true,
+        "varint": true
+      }
+    },
+    "multihashes": {
+      "packages": {
+        "bs58": true,
+        "buffer": true,
+        "multibase": true,
+        "uint8arrays": true,
+        "varint": true,
+        "web-encoding": true
+      }
+    },
+    "multihashing-async": {
+      "globals": {
+        "crypto": true,
+        "msCrypto": true
+      },
+      "packages": {
+        "blakejs": true,
+        "buffer": true,
+        "err-code": true,
+        "js-sha3": true,
+        "multihashes": true,
+        "murmurhash3js": true,
+        "murmurhash3js-revisited": true,
+        "nodeify": true,
+        "process": true
+      }
+    },
+    "multistream-select": {
+      "packages": {
+        "assert": true,
+        "async": true,
+        "buffer": true,
+        "debug": true,
+        "err-code": true,
+        "interface-connection": true,
+        "once": true,
+        "pull-handshake": true,
+        "pull-length-prefixed": true,
+        "pull-stream": true,
+        "semver": true,
+        "varint": true
+      }
+    },
+    "muport-did-resolver": {
+      "packages": {
+        "@babel/runtime": true,
+        "did-resolver": true,
+        "node-fetch": true
+      }
+    },
+    "murmurhash3js": {
+      "globals": {
+        "define": true
+      }
+    },
+    "murmurhash3js-revisited": {
+      "globals": {
+        "define": true
+      }
+    },
+    "nanoid": {
+      "globals": {
+        "crypto": true,
+        "msCrypto": true,
+        "navigator": true
+      },
+      "packages": {
+        "buffer": true,
+        "crypto-browserify": true
+      }
+    },
+    "node-forge": {
+      "globals": {
+        "Blob": true,
+        "MutationObserver": true,
+        "QuotaExceededError": true,
+        "URL.createObjectURL": true,
+        "URL.revokeObjectURL": true,
+        "Worker": true,
+        "addEventListener": true,
+        "document": true,
+        "jQuery": true,
+        "localStorage": true,
+        "location": true,
+        "navigator": true,
+        "postMessage": true,
+        "removeEventListener": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "browser-resolve": true,
+        "process": true,
+        "timers-browserify": true
+      }
+    },
+    "nodeify": {
+      "globals": {
+        "setTimeout": true
+      },
+      "packages": {
+        "is-promise": true,
+        "process": true,
+        "promise": true,
+        "timers-browserify": true
+      }
+    },
+    "nonce-tracker": {
+      "packages": {
+        "assert": true,
+        "await-semaphore": true,
+        "ethjs-query": true
+      }
+    },
+    "number-to-bn": {
+      "packages": {
+        "bn.js": true,
+        "strip-hex-prefix": true
+      }
+    },
+    "obj-multiplex": {
+      "globals": {
+        "console.warn": true
+      },
+      "packages": {
+        "end-of-stream": true,
+        "once": true,
+        "readable-stream": true
+      }
+    },
+    "obs-store": {
+      "packages": {
+        "safe-event-emitter": true,
+        "xtend": true
+      }
+    },
+    "once": {
+      "packages": {
+        "wrappy": true
+      }
+    },
+    "orbit-db": {
+      "globals": {
+        "console.log": true
+      },
+      "packages": {
+        "cids": true,
+        "ipfs-pubsub-1on1": true,
+        "logplease": true,
+        "multihashes": true,
+        "orbit-db-access-controllers": true,
+        "orbit-db-cache": true,
+        "orbit-db-counterstore": true,
+        "orbit-db-docstore": true,
+        "orbit-db-eventstore": true,
+        "orbit-db-feedstore": true,
+        "orbit-db-identity-provider": true,
+        "orbit-db-io": true,
+        "orbit-db-keystore": true,
+        "orbit-db-kvstore": true,
+        "orbit-db-pubsub": true,
+        "path-browserify": true
+      }
+    },
+    "orbit-db-access-controllers": {
+      "globals": {
+        "console.log": true
+      },
+      "packages": {
+        "events": true,
+        "orbit-db-io": true,
+        "p-map-series": true,
+        "path-browserify": true,
+        "safe-buffer": true
+      }
+    },
+    "orbit-db-cache": {
+      "packages": {
+        "level-js": true,
+        "logplease": true,
+        "path-browserify": true
+      }
+    },
+    "orbit-db-counterstore": {
+      "packages": {
+        "crdts": true,
+        "orbit-db-store": true
+      }
+    },
+    "orbit-db-docstore": {
+      "packages": {
+        "orbit-db-store": true,
+        "p-map": true,
+        "readable-stream": true
+      }
+    },
+    "orbit-db-eventstore": {
+      "packages": {
+        "orbit-db-store": true
+      }
+    },
+    "orbit-db-feedstore": {
+      "packages": {
+        "orbit-db-eventstore": true
+      }
+    },
+    "orbit-db-identity-provider": {
+      "packages": {
+        "orbit-db-keystore": true
+      }
+    },
+    "orbit-db-io": {
+      "packages": {
+        "buffer": true,
+        "cids": true,
+        "ipld-dag-pb": true
+      }
+    },
+    "orbit-db-keystore": {
+      "globals": {
+        "console.error": true,
+        "console.log": true
+      },
+      "packages": {
+        "elliptic": true,
+        "level-js": true,
+        "levelup": true,
+        "libp2p-crypto": true,
+        "lru": true,
+        "safe-buffer": true,
+        "secp256k1": true
+      }
+    },
+    "orbit-db-kvstore": {
+      "packages": {
+        "orbit-db-store": true
+      }
+    },
+    "orbit-db-pubsub": {
+      "packages": {
+        "buffer": true,
+        "ipfs-pubsub-peer-monitor": true,
+        "logplease": true,
+        "p-series": true
+      }
+    },
+    "orbit-db-store": {
+      "globals": {
+        "clearInterval": true,
+        "console.error": true,
+        "console.warn": true,
+        "setInterval": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "buffer": true,
+        "events": true,
+        "ipfs-log": true,
+        "logplease": true,
+        "orbit-db-io": true,
+        "p-each-series": true,
+        "p-map": true,
+        "readable-stream": true
+      }
+    },
+    "os-browserify": {
+      "globals": {
+        "location": true,
+        "navigator": true
+      }
+    },
+    "p-each-series": {
+      "packages": {
+        "p-reduce": true
+      }
+    },
+    "p-map-series": {
+      "packages": {
+        "p-reduce": true
+      }
+    },
+    "p-queue": {
+      "globals": {
+        "clearInterval": true,
+        "setInterval": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "eventemitter3": true
+      }
+    },
+    "p-series": {
+      "packages": {
+        "@sindresorhus/is": true,
+        "p-reduce": true
+      }
+    },
+    "p-times": {
+      "packages": {
+        "p-map": true
+      }
+    },
+    "parse-asn1": {
+      "packages": {
+        "asn1.js": true,
+        "browserify-aes": true,
+        "buffer": true,
+        "evp_bytestokey": true,
+        "pbkdf2": true
+      }
+    },
+    "path-browserify": {
+      "packages": {
+        "process": true
+      }
+    },
+    "path-to-regexp": {
+      "packages": {
+        "isarray": true
+      }
+    },
+    "pbkdf2": {
+      "globals": {
+        "crypto": true,
+        "process": true
+      },
+      "packages": {
+        "create-hash": true,
+        "process": true,
+        "ripemd160": true,
+        "safe-buffer": true,
+        "sha.js": true
+      }
+    },
+    "peer-book": {
+      "packages": {
+        "bs58": true,
+        "is-buffer": true,
+        "peer-id": true,
+        "peer-info": true
+      }
+    },
+    "peer-id": {
+      "packages": {
+        "assert": true,
+        "async": true,
+        "buffer": true,
+        "class-is": true,
+        "libp2p-crypto": true,
+        "multihashes": true
+      }
+    },
+    "peer-info": {
+      "packages": {
+        "assert": true,
+        "multiaddr": true,
+        "peer-id": true,
+        "unique-by": true
+      }
+    },
+    "popper.js": {
+      "globals": {
+        "MSInputMethodContext": true,
+        "Node.DOCUMENT_POSITION_FOLLOWING": true,
+        "cancelAnimationFrame": true,
+        "console.warn": true,
+        "define": true,
+        "devicePixelRatio": true,
+        "document": true,
+        "getComputedStyle": true,
+        "innerHeight": true,
+        "innerWidth": true,
+        "navigator": true,
+        "requestAnimationFrame": true,
+        "setTimeout": true
+      }
+    },
+    "precond": {
+      "packages": {
+        "util": true
+      }
+    },
+    "process": {
+      "globals": {
+        "clearTimeout": true,
+        "setTimeout": true
+      }
+    },
+    "process-nextick-args": {
+      "packages": {
+        "process": true
+      }
+    },
+    "promise": {
+      "globals": {
+        "setImediate": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "is-promise": true,
+        "process": true
+      }
+    },
+    "promise-timeout": {
+      "globals": {
+        "clearTimeout": true,
+        "setTimeout": true
+      }
+    },
+    "promise-to-callback": {
+      "packages": {
+        "is-fn": true,
+        "set-immediate-shim": true
+      }
+    },
+    "prop-types": {
+      "globals": {
+        "console": true
+      },
+      "packages": {
+        "object-assign": true,
+        "react-is": true
+      }
+    },
+    "protons": {
+      "packages": {
+        "buffer": true,
+        "is-buffer": true,
+        "protocol-buffers-schema": true,
+        "safe-buffer": true,
+        "signed-varint": true,
+        "varint": true
+      }
+    },
+    "public-encrypt": {
+      "packages": {
+        "bn.js": true,
+        "browserify-rsa": true,
+        "buffer": true,
+        "create-hash": true,
+        "parse-asn1": true,
+        "randombytes": true
+      }
+    },
+    "pubnub": {
+      "globals": {
+        "ActiveXObject": true,
+        "XMLHttpRequest": true,
+        "addEventListener": true,
+        "btoa": true,
+        "clearInterval": true,
+        "clearTimeout": true,
+        "console": true,
+        "define": true,
+        "localStorage.getItem": true,
+        "localStorage.setItem": true,
+        "location": true,
+        "navigator": true,
+        "setInterval": true,
+        "setTimeout": true
+      }
+    },
+    "pull-handshake": {
+      "packages": {
+        "pull-cat": true,
+        "pull-pair": true,
+        "pull-pushable": true,
+        "pull-reader": true
+      }
+    },
+    "pull-length-prefixed": {
+      "packages": {
+        "pull-pushable": true,
+        "pull-reader": true,
+        "safe-buffer": true,
+        "varint": true
+      }
+    },
+    "pull-mplex": {
+      "packages": {
+        "async": true,
+        "buffer": true,
+        "debug": true,
+        "events": true,
+        "interface-connection": true,
+        "looper": true,
+        "pull-abortable": true,
+        "pull-pushable": true,
+        "pull-stream": true,
+        "pull-through": true,
+        "varint": true
+      }
+    },
+    "pull-reader": {
+      "globals": {
+        "cb": true,
+        "clearTimeout": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "buffer": true
+      }
+    },
+    "pull-sort": {
+      "packages": {
+        "pull-defer": true,
+        "pull-stream": true
+      }
+    },
+    "pull-stream": {
+      "globals": {
+        "console.log": true
+      }
+    },
+    "pull-stream-to-async-iterator": {
+      "packages": {
+        "pull-stream": true
+      }
+    },
+    "pull-stream-to-stream": {
+      "packages": {
+        "process": true,
+        "stream-browserify": true,
+        "timers-browserify": true
+      }
+    },
+    "pull-through": {
+      "packages": {
+        "looper": true
+      }
+    },
+    "pull-ws": {
+      "globals": {
+        "WebSocket": true,
+        "location": true
+      },
+      "packages": {
+        "browser-resolve": true,
+        "events": true,
+        "https-browserify": true,
+        "process": true,
+        "relative-url": true,
+        "safe-buffer": true,
+        "stream-http": true,
+        "timers-browserify": true,
+        "url": true
+      }
+    },
+    "pump": {
+      "packages": {
+        "browser-resolve": true,
+        "end-of-stream": true,
+        "once": true,
+        "process": true
+      }
+    },
+    "punycode": {
+      "globals": {
+        "define": true
+      }
+    },
+    "qrcode-generator": {
+      "globals": {
+        "define": true
+      }
+    },
+    "rabin-wasm": {
+      "globals": {
+        "Blob": true,
+        "Response": true,
+        "WebAssembly": true
+      },
+      "packages": {
+        "assemblyscript": true
+      }
+    },
+    "randombytes": {
+      "globals": {
+        "crypto": true,
+        "msCrypto": true
+      },
+      "packages": {
+        "process": true,
+        "safe-buffer": true
+      }
+    },
+    "randomfill": {
+      "globals": {
+        "crypto": true,
+        "msCrypto": true
+      },
+      "packages": {
+        "process": true,
+        "randombytes": true,
+        "safe-buffer": true
+      }
+    },
+    "react": {
+      "globals": {
+        "console": true
+      },
+      "packages": {
+        "object-assign": true,
+        "prop-types": true
+      }
+    },
+    "react-dnd": {
+      "globals": {
+        "console.error": true
+      },
+      "packages": {
+        "disposables": true,
+        "dnd-core": true,
+        "hoist-non-react-statics": true,
+        "invariant": true,
+        "lodash": true,
+        "prop-types": true,
+        "react": true,
+        "shallowequal": true
+      }
+    },
+    "react-dnd-html5-backend": {
+      "globals": {
+        "Image": true,
+        "console.warn": true,
+        "devicePixelRatio": true,
+        "document": true,
+        "navigator.userAgent": true,
+        "safari": true,
+        "setTimeout": true
+      }
+    },
+    "react-dom": {
+      "globals": {
+        "MSApp": true,
+        "__REACT_DEVTOOLS_GLOBAL_HOOK__": true,
+        "addEventListener": true,
+        "clearTimeout": true,
+        "clipboardData": true,
+        "console": true,
+        "dispatchEvent": true,
+        "document": true,
+        "event": "write",
+        "jest": true,
+        "location.protocol": true,
+        "navigator.userAgent.indexOf": true,
+        "performance": true,
+        "removeEventListener": true,
+        "self": true,
+        "setTimeout": true,
+        "top": true,
+        "trustedTypes": true
+      },
+      "packages": {
+        "object-assign": true,
+        "prop-types": true,
+        "react": true,
+        "scheduler": true
+      }
+    },
+    "react-fast-compare": {
+      "globals": {
+        "Element": true,
+        "console.warn": true
+      }
+    },
+    "react-idle-timer": {
+      "globals": {
+        "clearTimeout": true,
+        "document": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "prop-types": true,
+        "react": true
+      }
+    },
+    "react-inspector": {
+      "globals": {
+        "Node.CDATA_SECTION_NODE": true,
+        "Node.COMMENT_NODE": true,
+        "Node.DOCUMENT_FRAGMENT_NODE": true,
+        "Node.DOCUMENT_NODE": true,
+        "Node.DOCUMENT_TYPE_NODE": true,
+        "Node.ELEMENT_NODE": true,
+        "Node.PROCESSING_INSTRUCTION_NODE": true,
+        "Node.TEXT_NODE": true
+      },
+      "packages": {
+        "babel-runtime": true,
+        "is-dom": true,
+        "prop-types": true,
+        "react": true
+      }
+    },
+    "react-is": {
+      "globals": {
+        "console": true
+      }
+    },
+    "react-popper": {
+      "globals": {
+        "document": true
+      },
+      "packages": {
+        "@popperjs/core": true,
+        "react": true,
+        "react-fast-compare": true,
+        "warning": true
+      }
+    },
+    "react-redux": {
+      "globals": {
+        "console": true,
+        "document": true
+      },
+      "packages": {
+        "@babel/runtime": true,
+        "hoist-non-react-statics": true,
+        "prop-types": true,
+        "react": true,
+        "react-dom": true,
+        "react-is": true,
+        "redux": true
+      }
+    },
+    "react-router": {
+      "packages": {
+        "history": true,
+        "hoist-non-react-statics": true,
+        "mini-create-react-context": true,
+        "path-to-regexp": true,
+        "prop-types": true,
+        "react": true,
+        "react-is": true,
+        "tiny-invariant": true,
+        "tiny-warning": true
+      }
+    },
+    "react-router-dom": {
+      "packages": {
+        "history": true,
+        "prop-types": true,
+        "react": true,
+        "react-router": true,
+        "tiny-invariant": true,
+        "tiny-warning": true
+      }
+    },
+    "react-simple-file-input": {
+      "globals": {
+        "File": true,
+        "FileReader": true,
+        "console.warn": true
+      },
+      "packages": {
+        "prop-types": true,
+        "react": true
+      }
+    },
+    "react-tippy": {
+      "globals": {
+        "Element": true,
+        "MSStream": true,
+        "MutationObserver": true,
+        "addEventListener": true,
+        "clearTimeout": true,
+        "console.error": true,
+        "console.warn": true,
+        "define": true,
+        "document": true,
+        "getComputedStyle": true,
+        "innerHeight": true,
+        "innerWidth": true,
+        "navigator.maxTouchPoints": true,
+        "navigator.msMaxTouchPoints": true,
+        "navigator.userAgent": true,
+        "performance": true,
+        "requestAnimationFrame": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "popper.js": true,
+        "react": true,
+        "react-dom": true
+      }
+    },
+    "react-toggle-button": {
+      "globals": {
+        "clearTimeout": true,
+        "console.warn": true,
+        "define": true,
+        "performance": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "react": true
+      }
+    },
+    "react-transition-group": {
+      "globals": {
+        "Element": true,
+        "clearTimeout": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "chain-function": true,
+        "dom-helpers": true,
+        "prop-types": true,
+        "react": true,
+        "react-dom": true,
+        "warning": true
+      }
+    },
+    "readable-stream": {
+      "packages": {
+        "browser-resolve": true,
+        "buffer": true,
+        "core-util-is": true,
+        "events": true,
+        "inherits": true,
+        "isarray": true,
+        "process": true,
+        "process-nextick-args": true,
+        "safe-buffer": true,
+        "string_decoder": true,
+        "timers-browserify": true,
+        "util-deprecate": true
+      }
+    },
+    "receptacle": {
+      "globals": {
+        "clearTimeout": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "ms": true
+      }
+    },
+    "redux": {
+      "globals": {
+        "console": true
+      },
+      "packages": {
+        "symbol-observable": true
+      }
+    },
+    "redux-devtools-core": {
+      "globals": {
+        "ErrorUtils": true,
+        "console": true,
+        "devToolsOptions": true,
+        "onerror": "write",
+        "serializeState": true
+      },
+      "packages": {
+        "get-params": true,
+        "jsan": true,
+        "lodash": true,
+        "nanoid": true,
+        "remotedev-serialize": true
+      }
+    },
+    "redux-devtools-instrument": {
+      "globals": {
+        "chrome": true,
+        "console.error": true,
+        "process": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "lodash": true,
+        "process": true,
+        "symbol-observable": true
+      }
+    },
+    "regenerator-runtime": {
+      "globals": {
+        "regeneratorRuntime": "write"
+      }
+    },
+    "regexp.prototype.flags": {
+      "packages": {
+        "define-properties": true,
+        "es-abstract": true
+      }
+    },
+    "relative-url": {
+      "packages": {
+        "url": true
+      }
+    },
+    "remote-redux-devtools": {
+      "globals": {
+        "console.log": true,
+        "console.warn": true,
+        "fetch": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "jsan": true,
+        "redux-devtools-core": true,
+        "redux-devtools-instrument": true,
+        "rn-host-detect": true,
+        "socketcluster-client": true
+      }
+    },
+    "retimer": {
+      "globals": {
+        "clearTimeout": true,
+        "setTimeout": true
+      }
+    },
+    "ripemd160": {
+      "packages": {
+        "buffer": true,
+        "hash-base": true,
+        "inherits": true
+      }
+    },
+    "rlp": {
+      "packages": {
+        "bn.js": true,
+        "buffer": true
+      }
+    },
+    "rn-host-detect": {
+      "globals": {
+        "__DEV__": true,
+        "__fbBatchedBridgeConfig": true,
+        "console": true
+      }
+    },
+    "rpc-cap": {
+      "packages": {
+        "@metamask/controllers": true,
+        "eth-rpc-errors": true,
+        "is-subset": true,
+        "json-rpc-engine": true,
+        "uuid": true
+      }
+    },
+    "safe-buffer": {
+      "packages": {
+        "buffer": true
+      }
+    },
+    "safe-event-emitter": {
+      "globals": {
+        "setTimeout": true
+      },
+      "packages": {
+        "events": true,
+        "util": true
+      }
+    },
+    "sanitize-filename": {
+      "packages": {
+        "truncate-utf8-bytes": true
+      }
+    },
+    "sc-channel": {
+      "packages": {
+        "component-emitter": true
+      }
+    },
+    "sc-formatter": {
+      "globals": {
+        "Buffer": true
+      }
+    },
+    "scheduler": {
+      "globals": {
+        "MessageChannel": true,
+        "cancelAnimationFrame": true,
+        "clearTimeout": true,
+        "console": true,
+        "navigator": true,
+        "performance": true,
+        "requestAnimationFrame": true,
+        "setTimeout": true
+      }
+    },
+    "scrypt-js": {
+      "globals": {
+        "define": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "timers-browserify": true
+      }
+    },
+    "scryptsy": {
+      "packages": {
+        "buffer": true,
+        "pbkdf2": true
+      }
+    },
+    "secp256k1": {
+      "packages": {
+        "bip66": true,
+        "bn.js": true,
+        "create-hash": true,
+        "drbg.js": true,
+        "elliptic": true,
+        "is-buffer": true,
+        "safe-buffer": true
+      }
+    },
+    "semaphore": {
+      "globals": {
+        "define": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "process": true
+      }
+    },
+    "semver": {
+      "globals": {
+        "console": true
+      },
+      "packages": {
+        "process": true
+      }
+    },
+    "set-immediate-shim": {
+      "globals": {
+        "setTimeout.apply": true
+      },
+      "packages": {
+        "timers-browserify": true
+      }
+    },
+    "sha.js": {
+      "packages": {
+        "inherits": true,
+        "safe-buffer": true
+      }
+    },
+    "shortid": {
+      "globals": {
+        "crypto": true,
+        "msCrypto": true
+      },
+      "packages": {
+        "nanoid": true
+      }
+    },
+    "signed-varint": {
+      "packages": {
+        "varint": true
+      }
+    },
+    "simple-peer": {
+      "globals": {
+        "clearInterval": true,
+        "console.warn": true,
+        "setInterval": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "buffer": true,
+        "debug": true,
+        "get-browser-rtc": true,
+        "inherits": true,
+        "randombytes": true,
+        "readable-stream": true
+      }
+    },
+    "socket.io-client": {
+      "globals": {
+        "clearTimeout": true,
+        "location": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "backo2": true,
+        "component-bind": true,
+        "component-emitter": true,
+        "debug": true,
+        "engine.io-client": true,
+        "has-binary2": true,
+        "indexof": true,
+        "parseqs": true,
+        "parseuri": true,
+        "socket.io-parser": true,
+        "to-array": true
+      }
+    },
+    "socket.io-parser": {
+      "globals": {
+        "Blob": true,
+        "File": true,
+        "FileReader": true
+      },
+      "packages": {
+        "buffer": true,
+        "component-emitter": true,
+        "debug": true,
+        "isarray": true
+      }
+    },
+    "socket.io-pull-stream": {
+      "globals": {
+        "console.error": true
+      },
+      "packages": {
+        "buffer": true,
+        "data-queue": true,
+        "debug": true,
+        "pull-stream": true,
+        "uuid": true
+      }
+    },
+    "socketcluster-client": {
+      "globals": {
+        "WebSocket": true,
+        "WorkerGlobalScope": true,
+        "addEventListener": true,
+        "clearTimeout": true,
+        "localStorage": true,
+        "location": true,
+        "removeEventListener": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "buffer": true,
+        "clone": true,
+        "component-emitter": true,
+        "linked-list": true,
+        "querystring-es3": true,
+        "sc-channel": true,
+        "sc-errors": true,
+        "sc-formatter": true,
+        "uuid": true
+      }
+    },
+    "sort-keys": {
+      "packages": {
+        "is-plain-obj": true
+      }
+    },
+    "stable": {
+      "globals": {
+        "define": true
+      }
+    },
+    "store": {
+      "globals": {
+        "ActiveXObject": true,
+        "console": true
+      }
+    },
+    "stream-browserify": {
+      "packages": {
+        "events": true,
+        "inherits": true,
+        "readable-stream": true
+      }
+    },
+    "stream-http": {
+      "globals": {
+        "AbortController": true,
+        "Blob": true,
+        "MSStreamReader": true,
+        "ReadableStream": true,
+        "WritableStream": true,
+        "XDomainRequest": true,
+        "XMLHttpRequest": true,
+        "clearTimeout": true,
+        "fetch": true,
+        "location.protocol.search": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "buffer": true,
+        "builtin-status-codes": true,
+        "inherits": true,
+        "process": true,
+        "readable-stream": true,
+        "url": true,
+        "xtend": true
+      }
+    },
+    "stream-to-pull-stream": {
+      "globals": {
+        "console.error": true
+      },
+      "packages": {
+        "looper": true,
+        "process": true,
+        "pull-stream": true
+      }
+    },
+    "string_decoder": {
+      "packages": {
+        "safe-buffer": true
+      }
+    },
+    "strip-hex-prefix": {
+      "packages": {
+        "is-hex-prefixed": true
+      }
+    },
+    "textarea-caret": {
+      "globals": {
+        "document.body.appendChild": true,
+        "document.body.removeChild": true,
+        "document.createElement": true,
+        "document.querySelector": true,
+        "getCaretCoordinates": "write",
+        "getComputedStyle": true,
+        "mozInnerScreenX": true
+      }
+    },
+    "through": {
+      "packages": {
+        "process": true,
+        "stream-browserify": true
+      }
+    },
+    "through2": {
+      "packages": {
+        "process": true,
+        "readable-stream": true,
+        "util": true,
+        "xtend": true
+      }
+    },
+    "time-cache": {
+      "packages": {
+        "lodash.throttle": true
+      }
+    },
+    "timers-browserify": {
+      "globals": {
+        "clearInterval": true,
+        "clearTimeout": true,
+        "setInterval": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "process": true
+      }
+    },
+    "tiny-warning": {
+      "globals": {
+        "console": true
+      }
+    },
+    "toggle-selection": {
+      "globals": {
+        "document.activeElement": true,
+        "document.getSelection": true
+      }
+    },
+    "trezor-connect": {
+      "globals": {
+        "__TREZOR_CONNECT_SRC": true,
+        "addEventListener": true,
+        "btoa": true,
+        "chrome": true,
+        "clearInterval": true,
+        "clearTimeout": true,
+        "console": true,
+        "document.body": true,
+        "document.createElement": true,
+        "document.createTextNode": true,
+        "document.getElementById": true,
+        "document.querySelectorAll": true,
+        "fetch": true,
+        "location": true,
+        "navigator": true,
+        "open": true,
+        "removeEventListener": true,
+        "setInterval": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "@babel/runtime": true,
+        "events": true,
+        "whatwg-fetch": true
+      }
+    },
+    "truncate-utf8-bytes": {
+      "packages": {
+        "utf8-byte-length": true
+      }
+    },
+    "tslib": {
+      "globals": {
+        "define": true
+      }
+    },
+    "tweetnacl": {
+      "globals": {
+        "crypto": true,
+        "msCrypto": true,
+        "nacl": "write"
+      },
+      "packages": {
+        "browser-resolve": true
+      }
+    },
+    "tweetnacl-util": {
+      "globals": {
+        "atob": true,
+        "btoa": true
+      },
+      "packages": {
+        "browser-resolve": true
+      }
+    },
+    "typedarray-to-buffer": {
+      "packages": {
+        "buffer": true,
+        "is-typedarray": true
+      }
+    },
+    "typical": {
+      "globals": {
+        "define": true
+      }
+    },
+    "uint8arrays": {
+      "globals": {
+        "TextDecoder": true,
+        "TextEncoder": true
+      },
+      "packages": {
+        "multibase": true,
+        "web-encoding": true
+      }
+    },
+    "unorm": {
+      "globals": {
+        "define": true
+      }
+    },
+    "uport-base64url": {
+      "packages": {
+        "buffer": true
+      }
+    },
+    "url": {
+      "packages": {
+        "punycode": true,
+        "querystring-es3": true
+      }
+    },
+    "utf8": {
+      "globals": {
+        "define": true
+      }
+    },
+    "util": {
+      "globals": {
+        "console.error": true,
+        "console.log": true,
+        "console.trace": true,
+        "process": true
+      },
+      "packages": {
+        "inherits": true,
+        "process": true
+      }
+    },
+    "util-deprecate": {
+      "globals": {
+        "console.trace": true,
+        "console.warn": true,
+        "localStorage": true
+      }
+    },
+    "uuid": {
+      "globals": {
+        "crypto": true,
+        "msCrypto": true
+      }
+    },
+    "varint-decoder": {
+      "packages": {
+        "is-buffer": true,
+        "varint": true
+      }
+    },
+    "vm-browserify": {
+      "globals": {
+        "document.body.appendChild": true,
+        "document.body.removeChild": true,
+        "document.createElement": true
+      }
+    },
+    "warning": {
+      "globals": {
+        "console": true
+      }
+    },
+    "web-encoding": {
+      "globals": {
+        "TextDecoder": true,
+        "TextEncoder": true
+      },
+      "packages": {
+        "util": true
+      }
+    },
+    "web3": {
+      "globals": {
+        "Web3": "write",
+        "XMLHttpRequest": "write",
+        "clearTimeout": true,
+        "console.error": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "bignumber.js": true,
+        "buffer": true,
+        "crypto-js": true,
+        "utf8": true,
+        "xhr2-cookies": true
+      }
+    },
+    "web3-provider-engine": {
+      "globals": {
+        "WebSocket": true,
+        "console": true,
+        "setTimeout": true
+      },
+      "packages": {
+        "@ethereumjs/tx": true,
+        "async": true,
+        "backoff": true,
+        "browser-resolve": true,
+        "buffer": true,
+        "eth-block-tracker": true,
+        "eth-json-rpc-filters": true,
+        "eth-json-rpc-infura": true,
+        "eth-json-rpc-middleware": true,
+        "eth-sig-util": true,
+        "ethereumjs-util": true,
+        "events": true,
+        "json-stable-stringify": true,
+        "semaphore": true,
+        "util": true,
+        "xtend": true
+      }
+    },
+    "web3-stream-provider": {
+      "globals": {
+        "setTimeout": true
+      },
+      "packages": {
+        "readable-stream": true,
+        "util": true,
+        "uuid": true
+      }
+    },
+    "webrtcsupport": {
+      "globals": {
+        "AudioContext": true,
+        "MediaStream": true,
+        "RTCIceCandidate": true,
+        "RTCPeerConnection": true,
+        "RTCSessionDescription": true,
+        "document": true,
+        "location.protocol": true,
+        "mozRTCIceCandidate": true,
+        "mozRTCPeerConnection": true,
+        "mozRTCSessionDescription": true,
+        "navigator.getUserMedia": true,
+        "navigator.mozGetUserMedia": true,
+        "navigator.msGetUserMedia": true,
+        "navigator.userAgent.match": true,
+        "navigator.webkitGetUserMedia": true,
+        "webkitAudioContext": true,
+        "webkitMediaStream": true,
+        "webkitRTCPeerConnection": true
+      }
+    },
+    "whatwg-fetch": {
+      "globals": {
+        "Blob": true,
+        "FileReader": true,
+        "FormData": true,
+        "URLSearchParams.prototype.isPrototypeOf": true,
+        "XMLHttpRequest": true,
+        "define": true,
+        "setTimeout": true
+      }
+    },
+    "xhr2": {
+      "globals": {
+        "XMLHttpRequest": true
+      }
+    },
+    "xhr2-cookies": {
+      "globals": {
+        "console.warn": true
+      },
+      "packages": {
+        "buffer": true,
+        "cookiejar": true,
+        "https-browserify": true,
+        "os-browserify": true,
+        "process": true,
+        "stream-http": true,
+        "url": true
+      }
+    },
+    "xor-distance": {
+      "packages": {
+        "buffer": true
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/lavamoat/node/policy.json b/lavamoat/node/policy.json
index c3acbd59d..b9080a2e6 100644
--- a/lavamoat/node/policy.json
+++ b/lavamoat/node/policy.json
@@ -840,18 +840,22 @@
       "builtin": {
         "assert": true,
         "buffer.Buffer.from": true,
+        "fs.readFileSync": true,
         "path.join": true,
         "path.relative": true
       },
       "globals": {
         "__dirname": true,
-        "process.cwd": true
+        "process.cwd": true,
+        "setTimeout": true
       },
       "packages": {
         "JSONStream": true,
         "combine-source-map": true,
         "convert-source-map": true,
         "json-stable-stringify": true,
+        "lavamoat-core": true,
+        "readable-stream": true,
         "through2": true,
         "umd": true
       }
@@ -1398,6 +1402,15 @@
         "buffer.Buffer": true
       }
     },
+    "clone-deep": {
+      "packages": {
+        "for-own": true,
+        "is-plain-object": true,
+        "kind-of": true,
+        "lazy-cache": true,
+        "shallow-clone": true
+      }
+    },
     "clone-regexp": {
       "packages": {
         "is-regexp": true
@@ -1456,6 +1469,7 @@
     "concat-stream": {
       "globals": {
         "Buffer.concat": true,
+        "Buffer.from": true,
         "Buffer.isBuffer": true
       },
       "packages": {
@@ -2812,6 +2826,70 @@
         "es6-weak-map": true
       }
     },
+    "lavamoat-browserify": {
+      "builtin": {
+        "fs.existsSync": true,
+        "fs.mkdirSync": true,
+        "fs.readFileSync": true,
+        "fs.writeFileSync": true,
+        "path.dirname": true,
+        "path.extname": true,
+        "path.resolve": true,
+        "util.callbackify": true
+      },
+      "globals": {
+        "console.warn": true,
+        "process.cwd": true
+      },
+      "packages": {
+        "@lavamoat/lavapack": true,
+        "concat-stream": true,
+        "duplexify": true,
+        "json-stable-stringify": true,
+        "lavamoat-core": true,
+        "readable-stream": true,
+        "through2": true
+      }
+    },
+    "lavamoat-core": {
+      "builtin": {
+        "events": true,
+        "fs.existsSync": true,
+        "fs.readFileSync": true,
+        "module.createRequire": true,
+        "module.createRequireFromPath": true,
+        "path.extname": true,
+        "path.join": true,
+        "path.sep": true
+      },
+      "globals": {
+        "__dirname": true,
+        "console.warn": true,
+        "define": true
+      },
+      "packages": {
+        "fromentries": true,
+        "json-stable-stringify": true,
+        "lavamoat-tofu": true,
+        "merge-deep": true,
+        "resolve": true
+      }
+    },
+    "lavamoat-tofu": {
+      "globals": {
+        "console.log": true
+      },
+      "packages": {
+        "@babel/parser": true,
+        "@babel/traverse": true
+      }
+    },
+    "lazy-cache": {
+      "globals": {
+        "process.env.TRAVIS": true,
+        "process.env.UNLAZY": true
+      }
+    },
     "lazystream": {
       "builtin": {
         "util.inherits": true
@@ -2928,6 +3006,13 @@
         "timers-ext": true
       }
     },
+    "merge-deep": {
+      "packages": {
+        "arr-union": true,
+        "clone-deep": true,
+        "kind-of": true
+      }
+    },
     "merge-source-map": {
       "packages": {
         "source-map": true
@@ -2992,6 +3077,12 @@
         "is-extendable": true
       }
     },
+    "mixin-object": {
+      "packages": {
+        "for-in": true,
+        "is-extendable": true
+      }
+    },
     "mkdirp": {
       "builtin": {
         "fs": true,
@@ -3734,6 +3825,14 @@
         "split-string": true
       }
     },
+    "shallow-clone": {
+      "packages": {
+        "is-extendable": true,
+        "kind-of": true,
+        "lazy-cache": true,
+        "mixin-object": true
+      }
+    },
     "shasum": {
       "builtin": {
         "buffer.Buffer.isBuffer": true,
diff --git a/package.json b/package.json
index a26a3a81f..162649c83 100644
--- a/package.json
+++ b/package.json
@@ -64,8 +64,10 @@
     "storybook:deploy": "storybook-to-ghpages --existing-output-dir storybook-build --remote storybook --branch master",
     "update-changelog": "auto-changelog update",
     "generate:migration": "./development/generate-migration.sh",
-    "lavamoat:auto": "lavamoat ./development/build/index.js --writeAutoPolicy",
-    "lavamoat:debug:build": "lavamoat ./development/build/index.js --writeAutoPolicyDebug"
+    "lavamoat:build:auto": "lavamoat ./development/build/index.js --writeAutoPolicy",
+    "lavamoat:debug:build": "lavamoat ./development/build/index.js --writeAutoPolicyDebug",
+    "lavamoat:background:auto": "WRITE_AUTO_POLICY=1 yarn build prod",
+    "lavamoat:auto": "yarn lavamoat:build:auto && yarn lavamoat:background:auto"
   },
   "resolutions": {
     "**/regenerator-runtime": "^0.13.7",
@@ -217,7 +219,7 @@
     "@babel/preset-react": "^7.0.0",
     "@babel/register": "^7.5.5",
     "@lavamoat/allow-scripts": "^1.0.6",
-    "@lavamoat/lavapack": "^1.0.4",
+    "@lavamoat/lavapack": "^2.0.3",
     "@metamask/auto-changelog": "^2.1.0",
     "@metamask/eslint-config": "^6.0.0",
     "@metamask/eslint-config-jest": "^6.0.0",
@@ -288,6 +290,7 @@
     "jsdom": "^11.2.0",
     "koa": "^2.7.0",
     "lavamoat": "^5.3.4",
+    "lavamoat-browserify": "^14.0.3",
     "lavamoat-viz": "^6.0.9",
     "lockfile-lint": "^4.0.0",
     "loose-envify": "^1.4.0",
@@ -323,6 +326,7 @@
     "terser": "^5.7.0",
     "through2": "^4.0.2",
     "ttest": "^2.1.1",
+    "vinyl": "^2.2.1",
     "vinyl-buffer": "^1.0.1",
     "vinyl-source-stream": "^2.0.0",
     "vinyl-sourcemaps-apply": "^0.2.1",
diff --git a/test/e2e/tests/contract-interactions.spec.js b/test/e2e/tests/contract-interactions.spec.js
index c4e0537c5..6ef4cc4fb 100644
--- a/test/e2e/tests/contract-interactions.spec.js
+++ b/test/e2e/tests/contract-interactions.spec.js
@@ -32,6 +32,7 @@ describe('Deploy contract and call contract methods', function () {
         await driver.openNewPage('http://127.0.0.1:8080/');
         await driver.clickElement({ text: 'Connect', tag: 'button' });
         await driver.waitUntilXWindowHandles(3);
+        await driver.delay(5000);
         windowHandles = await driver.getAllWindowHandles();
         extension = windowHandles[0];
         dapp = await driver.switchToWindowWithTitle(
@@ -86,6 +87,7 @@ describe('Deploy contract and call contract methods', function () {
         await driver.switchToWindow(dapp);
         await driver.clickElement('#depositButton');
         await driver.waitUntilXWindowHandles(3);
+        await driver.delay(5000);
         windowHandles = await driver.getAllWindowHandles();
         await driver.switchToWindowWithTitle(
           'MetaMask Notification',
@@ -111,11 +113,13 @@ describe('Deploy contract and call contract methods', function () {
         await driver.switchToWindow(dapp);
         await driver.clickElement('#withdrawButton');
         await driver.waitUntilXWindowHandles(3);
+        await driver.delay(5000);
         windowHandles = await driver.getAllWindowHandles();
         await driver.switchToWindowWithTitle(
           'MetaMask Notification',
           windowHandles,
         );
+        await driver.delay(regularDelayMs);
         await driver.clickElement({ text: 'Confirm', tag: 'button' });
         await driver.waitUntilXWindowHandles(2);
         await driver.switchToWindow(extension);
diff --git a/yarn.lock b/yarn.lock
index 9133ce6ce..dd43cfdc2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2639,15 +2639,16 @@
     semver "^7.3.4"
     yargs "^16.2.0"
 
-"@lavamoat/lavapack@^1.0.4":
-  version "1.0.4"
-  resolved "https://registry.yarnpkg.com/@lavamoat/lavapack/-/lavapack-1.0.4.tgz#e72d6b29fa70da8236a127c1d95cb581cda6941e"
-  integrity sha512-Zhcn+eJyHIS4AAmN9IIjs8WCh12Q7NpFXXz0pI3Y54uknTdx5TPlwr3ARKf0jEXDOWNok/TuK2uPld54BSG/FQ==
+"@lavamoat/lavapack@^2.0.3":
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/@lavamoat/lavapack/-/lavapack-2.0.3.tgz#d535e23b4e98d21ac7aad57ef5019e64f1701117"
+  integrity sha512-7gpFnNaB4P15YsLf9/6Rn5EGAAg8s5CyVUZWy8bFHT+KaLzcRnjsbr1H5VehZkpU2xT0jWYHo1Rii7WmGdLQxg==
   dependencies:
     JSONStream "^1.3.5"
     combine-source-map "^0.8.0"
     convert-source-map "^1.7.0"
     json-stable-stringify "^1.0.1"
+    lavamoat-core "^11.0.0"
     through2 "^4.0.2"
     umd "^3.0.3"
 
@@ -8272,7 +8273,7 @@ clone@2.1.1:
   resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.1.tgz#d217d1e961118e3ac9a4b8bba3285553bf647cdb"
   integrity sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=
 
-clone@2.1.2, clone@^2.0.0, clone@^2.1.1:
+clone@2.1.2, clone@^2.0.0, clone@^2.1.1, clone@^2.1.2:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
   integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=
@@ -8523,7 +8524,7 @@ concat-stream@^1.5.0, concat-stream@^1.5.1, concat-stream@^1.5.2, concat-stream@
     readable-stream "^2.2.2"
     typedarray "^0.0.6"
 
-"concat-stream@github:hugomrdias/concat-stream#feat/smaller":
+concat-stream@^2.0.0, "concat-stream@github:hugomrdias/concat-stream#feat/smaller":
   version "2.0.0"
   resolved "https://codeload.github.com/hugomrdias/concat-stream/tar.gz/057bc7b5d6d8df26c8cf00a3f151b6721a0a8034"
   dependencies:
@@ -8654,7 +8655,7 @@ convert-source-map@^0.3.3:
   resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-0.3.5.tgz#f1d802950af7dd2631a1febe0596550c86ab3190"
   integrity sha1-8dgClQr33SYxof6+BZZVDIarMZA=
 
-convert-source-map@^1.0.0:
+convert-source-map@^1.0.0, convert-source-map@^1.8.0:
   version "1.8.0"
   resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369"
   integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==
@@ -17407,6 +17408,27 @@ latest-version@^5.0.0:
   dependencies:
     package-json "^6.3.0"
 
+lavamoat-browserify@^14.0.3:
+  version "14.0.3"
+  resolved "https://registry.yarnpkg.com/lavamoat-browserify/-/lavamoat-browserify-14.0.3.tgz#1f179ec34f18a433f6c8348d5e271b6a8e58bc09"
+  integrity sha512-TFRW6BV1n980g6rcKq90rjYyIbGumATc/7y6//qRXXBUSxRKAvTmyzVE19kNS5xdhWPlneDddf+YOZLwI8n1Gw==
+  dependencies:
+    "@lavamoat/lavapack" "^2.0.3"
+    JSONStream "^1.3.5"
+    clone "^2.1.2"
+    concat-stream "^2.0.0"
+    convert-source-map "^1.8.0"
+    duplexify "^4.1.1"
+    json-stable-stringify "^1.0.1"
+    lavamoat-core "^11.0.0"
+    merge-deep "^3.0.2"
+    offset-sourcemap-lines "^1.0.1"
+    pify "^4.0.1"
+    readable-stream "^3.6.0"
+    safe-buffer "^5.1.2"
+    through2 "^3.0.0"
+    umd "^3.0.3"
+
 lavamoat-core@^10.0.1:
   version "10.0.1"
   resolved "https://registry.yarnpkg.com/lavamoat-core/-/lavamoat-core-10.0.1.tgz#155bb4a50ace2e624941f14ca87705b171f6bba9"
@@ -20472,6 +20494,13 @@ observable-webworkers@^1.0.0:
   resolved "https://registry.yarnpkg.com/observable-webworkers/-/observable-webworkers-1.0.0.tgz#dcbd484a9644d512accc351962c6e710313fbb68"
   integrity sha512-+cECwCR8IEh8UY5nefQVLO9Cydqpk1izO+o7BABmKjXfJZyEOzBWY3ss5jbOPM6KmEa9aQExvAtTW6tVTOsNAQ==
 
+offset-sourcemap-lines@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/offset-sourcemap-lines/-/offset-sourcemap-lines-1.0.1.tgz#5854dff74b73fc06efcb61d7b721a8113d99be92"
+  integrity sha1-WFTf90tz/Abvy2HXtyGoET2ZvpI=
+  dependencies:
+    source-map "^0.5.0"
+
 on-finished@^2.3.0, on-finished@~2.3.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
@@ -27778,6 +27807,18 @@ vinyl@^2.0.0, vinyl@^2.1.0, vinyl@^2.2.0:
     remove-trailing-separator "^1.0.1"
     replace-ext "^1.0.0"
 
+vinyl@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.2.1.tgz#23cfb8bbab5ece3803aa2c0a1eb28af7cbba1974"
+  integrity sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==
+  dependencies:
+    clone "^2.1.1"
+    clone-buffer "^1.0.0"
+    clone-stats "^1.0.0"
+    cloneable-readable "^1.0.0"
+    remove-trailing-separator "^1.0.1"
+    replace-ext "^1.0.0"
+
 vm-browserify@^1.0.0, vm-browserify@^1.0.1:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
@@ -28899,9 +28940,9 @@ yauzl@2.10.0, yauzl@^2.10.0:
     fd-slicer "~1.1.0"
 
 yazl@^2.1.0:
-  version "2.5.1"
-  resolved "https://registry.yarnpkg.com/yazl/-/yazl-2.5.1.tgz#a3d65d3dd659a5b0937850e8609f22fffa2b5c35"
-  integrity sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==
+  version "2.4.3"
+  resolved "https://registry.yarnpkg.com/yazl/-/yazl-2.4.3.tgz#ec26e5cc87d5601b9df8432dbdd3cd2e5173a071"
+  integrity sha1-7CblzIfVYBud+EMtvdPNLlFzoHE=
   dependencies:
     buffer-crc32 "~0.2.3"
 

From cd09e5b0bb22e16c7915a3727d1a653570930740 Mon Sep 17 00:00:00 2001
From: ryanml <ryanlanese@gmail.com>
Date: Wed, 6 Oct 2021 05:02:50 -0700
Subject: [PATCH 43/56] Restoring mobile sync warning text (#12284)

---
 app/_locales/en/messages.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 9d2aa6aad..afe8ad1ef 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -1378,7 +1378,7 @@
     "message": "Don't see your token?"
   },
   "mobileSyncWarning": {
-    "message": "⚠️ Proceeding will display a secret QR code that allows access to your accounts. Do not share it with anyone. Support staff will never ask you for it."
+    "message": "The 'Sync with extension' feature is temporarily disabled. If you want to use your extension wallet on MetaMask mobile, then on your mobile app: go back to the wallet setup options and select the 'Import with Secret Recovery Phrase' option. Use your extension wallet's secret phrase to then import your wallet into mobile."
   },
   "mustSelectOne": {
     "message": "Must select at least 1 token."

From 822ce5b8b02a50b22a80d06a515a58972f0a60e4 Mon Sep 17 00:00:00 2001
From: Erik Marks <25517051+rekmarks@users.noreply.github.com>
Date: Wed, 6 Oct 2021 07:35:23 -0700
Subject: [PATCH 44/56] Fix unit test package scripts (#12278)

This PR fixes our local unit test package scripts. When the state migration unit tests were migrated to Jest in #12106, it left the `test:unit` script in a broken state, because it didn't tell `mocha` to ignore the state migration tests.

Arguably, that script was already broken, since the most reasonably expectation from its name is that it runs _all_ unit tests. The PR makes it so that it does just that, by means of `concurrently`.

Unfortunately, `concurrently` only outputs errors from child processes once (at the time when they exit, https://github.com/open-cli-tools/concurrently/issues/134). This means that we have to search/navigate the output for this combined script to identify the failure. That said, it's better than the status quo.
---
 package.json                            |  5 +++--
 test/test-unit-combined.sh              | 11 +++++++++++
 test/{run-jest.sh => test-unit-jest.sh} |  2 +-
 3 files changed, 15 insertions(+), 3 deletions(-)
 create mode 100755 test/test-unit-combined.sh
 rename test/{run-jest.sh => test-unit-jest.sh} (78%)

diff --git a/package.json b/package.json
index 162649c83..887b99329 100644
--- a/package.json
+++ b/package.json
@@ -24,9 +24,10 @@
     "dapp-chain": "GANACHE_ARGS='-b 2' concurrently -k -n ganache,dapp -p '[{time}][{name}]' 'yarn ganache:start' 'sleep 5 && yarn dapp'",
     "forwarder": "node ./development/static-server.js ./node_modules/@metamask/forwarder/dist/ --port 9010",
     "dapp-forwarder": "concurrently -k -n forwarder,dapp -p '[{time}][{name}]' 'yarn forwarder' 'yarn dapp'",
-    "test:unit": "mocha --exit --require test/env.js --require test/setup.js --recursive './app/**/*.test.js'",
+    "test:unit": "./test/test-unit-combined.sh",
+    "test:unit:jest": "./test/test-unit-jest.sh",
     "test:unit:global": "mocha --exit --require test/env.js --require test/setup.js --recursive test/unit-global/*.test.js",
-    "test:unit:jest": "./test/run-jest.sh",
+    "test:unit:mocha": "mocha --exit --require test/env.js --require test/setup.js --ignore './app/scripts/migrations/*.test.js' --recursive './app/**/*.test.js'",
     "test:unit:lax": "mocha --exit --require test/env.js --require test/setup.js --ignore './app/scripts/controllers/permissions/*.test.js' --ignore './app/scripts/migrations/*.test.js' --recursive './app/**/*.test.js'",
     "test:unit:strict": "mocha --exit --require test/env.js --require test/setup.js --recursive './app/scripts/controllers/permissions/*.test.js'",
     "test:unit:path": "mocha --exit --require test/env.js --require test/setup.js --recursive",
diff --git a/test/test-unit-combined.sh b/test/test-unit-combined.sh
new file mode 100755
index 000000000..d793ba752
--- /dev/null
+++ b/test/test-unit-combined.sh
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+
+set -x
+set -e
+set -u
+set -o pipefail
+
+concurrently --raw -n mocha,jest,global \
+  "yarn test:unit:mocha" \
+  "yarn test:unit:jest" \
+  "yarn test:unit:global"
diff --git a/test/run-jest.sh b/test/test-unit-jest.sh
similarity index 78%
rename from test/run-jest.sh
rename to test/test-unit-jest.sh
index 40b208732..384124920 100755
--- a/test/run-jest.sh
+++ b/test/test-unit-jest.sh
@@ -5,6 +5,6 @@ set -e
 set -u
 set -o pipefail
 
-concurrently \
+concurrently -n production,development \
   "jest --config=./jest.config.js $*" \
   "jest --config=./development/jest.config.js $*"

From 3a5538bd5030b15b682b2e6a1e774e43a754489f Mon Sep 17 00:00:00 2001
From: Mark Stacey <markjstacey@gmail.com>
Date: Wed, 6 Oct 2021 15:14:48 -0230
Subject: [PATCH 45/56] Migrate beta version to the main version field (#12246)

The main `version` field in `package.json` will now include the beta
version (if present) rather than it being passed in via the CLI when
building. The `version` field is now a fully SemVer-compatible version,
with the added restriction that any prerelease portion of the version
must match the format `<build type>.<build version>`.

This brings the build in-line with the future release process we will
be using for the beta version. The plan is for each future release to
enter a "beta phase" where the version would get updated to reflect
that it's a beta, and we would increment this beta version over time as
we update the beta. The manifest gives us a place to store this beta
version. It was also important to replace the automatic minor bump
logic that was being used previously, because the version in beta might
not be a minor bump.

Additionally, the filename logic used for beta builds was updated to
be generic across all build types rather than beta-specific. This will
be useful for Flask builds in the future.
---
 app/manifest/_beta_modifications.json |  3 +-
 development/build/README.md           |  2 -
 development/build/etc.js              | 24 +++------
 development/build/index.js            | 24 ++-------
 development/build/manifest.js         | 28 +++++-----
 development/build/utils.js            | 75 +++++++++++++++++++--------
 lavamoat/node/policy.json             | 10 +++-
 package.json                          |  1 +
 8 files changed, 86 insertions(+), 81 deletions(-)

diff --git a/app/manifest/_beta_modifications.json b/app/manifest/_beta_modifications.json
index a505eb7f4..33a2da321 100644
--- a/app/manifest/_beta_modifications.json
+++ b/app/manifest/_beta_modifications.json
@@ -22,6 +22,5 @@
     "512": "images/icon-512.png"
   },
   "name": "__MSG_appName__ Beta",
-  "short_name": "__MSG_appName__ Beta",
-  "version": ""
+  "short_name": "__MSG_appName__ Beta"
 }
diff --git a/development/build/README.md b/development/build/README.md
index 6c671be27..0e29f78ec 100644
--- a/development/build/README.md
+++ b/development/build/README.md
@@ -40,8 +40,6 @@ Commands:
                         e2e tests.
 
 Options:
-  --beta-version      If the build type is "beta", the beta version number.
-                                                           [number] [default: 0]
   --build-type        The "type" of build to create. One of: "beta", "main"
                                                       [string] [default: "main"]
   --lint-fence-files  Whether files with code fences should be linted after
diff --git a/development/build/etc.js b/development/build/etc.js
index 8d2b75956..f68da192a 100644
--- a/development/build/etc.js
+++ b/development/build/etc.js
@@ -5,18 +5,12 @@ const del = require('del');
 const pify = require('pify');
 const pump = pify(require('pump'));
 const { version } = require('../../package.json');
-
 const { createTask, composeParallel } = require('./task');
 const { BuildTypes } = require('./utils');
 
 module.exports = createEtcTasks;
 
-function createEtcTasks({
-  betaVersionsMap,
-  browserPlatforms,
-  buildType,
-  livereload,
-}) {
+function createEtcTasks({ browserPlatforms, buildType, livereload }) {
   const clean = createTask('clean', async function clean() {
     await del(['./dist/*']);
     await Promise.all(
@@ -34,23 +28,19 @@ function createEtcTasks({
   const zip = createTask(
     'zip',
     composeParallel(
-      ...browserPlatforms.map((platform) =>
-        createZipTask(
-          platform,
-          buildType === BuildTypes.beta ? betaVersionsMap[platform] : undefined,
-        ),
-      ),
+      ...browserPlatforms.map((platform) => createZipTask(platform, buildType)),
     ),
   );
 
   return { clean, reload, zip };
 }
 
-function createZipTask(platform, betaVersion) {
+function createZipTask(platform, buildType) {
   return async () => {
-    const path = betaVersion
-      ? `metamask-BETA-${platform}-${betaVersion}`
-      : `metamask-${platform}-${version}`;
+    const path =
+      buildType === BuildTypes.main
+        ? `metamask-${platform}-${version}`
+        : `metamask-${buildType}-${platform}-${version}`;
     await pump(
       gulp.src(`dist/${platform}/**`),
       gulpZip(`${path}.zip`),
diff --git a/development/build/index.js b/development/build/index.js
index d3e2ecc8d..cbea6e799 100755
--- a/development/build/index.js
+++ b/development/build/index.js
@@ -5,7 +5,6 @@
 //
 const livereload = require('gulp-livereload');
 const minimist = require('minimist');
-const { version } = require('../../package.json');
 const {
   createTask,
   composeSeries,
@@ -17,7 +16,7 @@ const createScriptTasks = require('./scripts');
 const createStyleTasks = require('./styles');
 const createStaticAssetTasks = require('./static');
 const createEtcTasks = require('./etc');
-const { BuildTypes, getNextBetaVersionMap } = require('./utils');
+const { BuildTypes, getBrowserVersionMap } = require('./utils');
 
 // packages required dynamically via browserify configuration in dependencies
 require('loose-envify');
@@ -34,7 +33,6 @@ defineAndRunBuildTasks();
 
 function defineAndRunBuildTasks() {
   const {
-    betaVersion,
     buildType,
     entryTask,
     isLavaMoat,
@@ -45,14 +43,7 @@ function defineAndRunBuildTasks() {
 
   const browserPlatforms = ['firefox', 'chrome', 'brave', 'opera'];
 
-  let betaVersionsMap;
-  if (buildType === BuildTypes.beta) {
-    betaVersionsMap = getNextBetaVersionMap(
-      version,
-      betaVersion,
-      browserPlatforms,
-    );
-  }
+  const browserVersionMap = getBrowserVersionMap(browserPlatforms);
 
   const staticTasks = createStaticAssetTasks({
     livereload,
@@ -63,7 +54,7 @@ function defineAndRunBuildTasks() {
 
   const manifestTasks = createManifestTasks({
     browserPlatforms,
-    betaVersionsMap,
+    browserVersionMap,
     buildType,
   });
 
@@ -80,7 +71,6 @@ function defineAndRunBuildTasks() {
   const { clean, reload, zip } = createEtcTasks({
     livereload,
     browserPlatforms,
-    betaVersionsMap,
     buildType,
   });
 
@@ -145,7 +135,6 @@ function defineAndRunBuildTasks() {
 
 function parseArgv() {
   const NamedArgs = {
-    BetaVersion: 'beta-version',
     BuildType: 'build-type',
     LintFenceFiles: 'lint-fence-files',
     OmitLockdown: 'omit-lockdown',
@@ -160,7 +149,6 @@ function parseArgv() {
     ],
     string: [NamedArgs.BuildType],
     default: {
-      [NamedArgs.BetaVersion]: 0,
       [NamedArgs.BuildType]: BuildTypes.main,
       [NamedArgs.LintFenceFiles]: true,
       [NamedArgs.OmitLockdown]: false,
@@ -179,11 +167,6 @@ function parseArgv() {
     throw new Error('MetaMask build: No entry task specified.');
   }
 
-  const betaVersion = argv[NamedArgs.BetaVersion];
-  if (!Number.isInteger(betaVersion) || betaVersion < 0) {
-    throw new Error(`MetaMask build: Invalid beta version: "${betaVersion}"`);
-  }
-
   const buildType = argv[NamedArgs.BuildType];
   if (!(buildType in BuildTypes)) {
     throw new Error(`MetaMask build: Invalid build type: "${buildType}"`);
@@ -197,7 +180,6 @@ function parseArgv() {
     : !/dev/iu.test(entryTask);
 
   return {
-    betaVersion: String(betaVersion),
     buildType,
     entryTask,
     isLavaMoat: process.argv[0].includes('lavamoat'),
diff --git a/development/build/manifest.js b/development/build/manifest.js
index 6f8b9e8fe..e14c3d786 100644
--- a/development/build/manifest.js
+++ b/development/build/manifest.js
@@ -3,7 +3,6 @@ const path = require('path');
 const { merge, cloneDeep } = require('lodash');
 
 const baseManifest = require('../../app/manifest/_base.json');
-const { version } = require('../../package.json');
 const betaManifestModifications = require('../../app/manifest/_beta_modifications.json');
 
 const { createTask, composeSeries } = require('./task');
@@ -11,7 +10,11 @@ const { BuildTypes } = require('./utils');
 
 module.exports = createManifestTasks;
 
-function createManifestTasks({ betaVersionsMap, browserPlatforms, buildType }) {
+function createManifestTasks({
+  browserPlatforms,
+  browserVersionMap,
+  buildType,
+}) {
   // merge base manifest with per-platform manifests
   const prepPlatforms = async () => {
     return Promise.all(
@@ -29,9 +32,8 @@ function createManifestTasks({ betaVersionsMap, browserPlatforms, buildType }) {
         const result = merge(
           cloneDeep(baseManifest),
           platformModifications,
-          buildType === BuildTypes.beta
-            ? getBetaModifications(platform, betaVersionsMap)
-            : { version },
+          browserVersionMap[platform],
+          getBuildModifications(buildType),
         );
         const dir = path.join('.', 'dist', platform);
         await fs.mkdir(dir, { recursive: true });
@@ -110,16 +112,10 @@ async function writeJson(obj, file) {
   return fs.writeFile(file, JSON.stringify(obj, null, 2));
 }
 
-function getBetaModifications(platform, betaVersionsMap) {
-  if (!betaVersionsMap || typeof betaVersionsMap !== 'object') {
-    throw new Error('MetaMask build: Expected object beta versions map.');
+function getBuildModifications(buildType) {
+  const buildModifications = {};
+  if (buildType === BuildTypes.beta) {
+    Object.assign(buildModifications, betaManifestModifications);
   }
-
-  const betaVersion = betaVersionsMap[platform];
-
-  return {
-    ...betaManifestModifications,
-    version: betaVersion,
-    ...(platform === 'firefox' ? {} : { version_name: 'beta' }),
-  };
+  return buildModifications;
 }
diff --git a/development/build/utils.js b/development/build/utils.js
index 0b8503d19..4413ab899 100644
--- a/development/build/utils.js
+++ b/development/build/utils.js
@@ -1,24 +1,5 @@
-/**
- * @returns {Object} An object with browser as key and next version of beta
- * as the value.  E.g. { firefox: '9.6.0.beta0', chrome: '9.6.0.1' }
- */
-function getNextBetaVersionMap(currentVersion, betaVersion, platforms) {
-  const [major, minor] = currentVersion.split('.');
-
-  return platforms.reduce((platformMap, platform) => {
-    platformMap[platform] = [
-      // Keeps the current major
-      major,
-      // Bump the minor version
-      Number(minor) + 1,
-      // This isn't typically used
-      0,
-      // The beta number
-      `${platform === 'firefox' ? 'beta' : ''}${betaVersion}`,
-    ].join('.');
-    return platformMap;
-  }, {});
-}
+const semver = require('semver');
+const { version } = require('../../package.json');
 
 const BuildTypes = {
   beta: 'beta',
@@ -26,7 +7,57 @@ const BuildTypes = {
   main: 'main',
 };
 
+/**
+ * Map the current version to a format that is compatible with each browser.
+ *
+ * The given version number is assumed to be a SemVer version number. Additionally, if the version
+ * has a prerelease component, it is assumed to have the format "<build type>.<build version",
+ * where the build version is a positive integer.
+ *
+ * @param {string} currentVersion - The current version.
+ * @param {string[]} platforms - A list of browsers to generate versions for.
+ * @returns {Object} An object with the browser as the key and the browser-specific version object
+ * as the value.  For example, the version `9.6.0-beta.1` would return the object
+ * `{ firefox: { version: '9.6.0.beta1' }, chrome: { version: '9.6.0.1', version_name: 'beta' } }`.
+ */
+function getBrowserVersionMap(platforms) {
+  const major = semver.major(version);
+  const minor = semver.minor(version);
+  const patch = semver.patch(version);
+  const prerelease = semver.prerelease(version);
+
+  let buildType;
+  let buildVersion;
+  if (prerelease) {
+    if (prerelease.length !== 2) {
+      throw new Error(`Invalid prerelease version: '${prerelease.join('.')}'`);
+    }
+    [buildType, buildVersion] = prerelease;
+    if (!String(buildVersion).match(/^\d+$/u)) {
+      throw new Error(`Invalid prerelease build version: '${buildVersion}'`);
+    } else if (buildType !== BuildTypes.beta) {
+      throw new Error(`Invalid prerelease build type: ${buildType}`);
+    }
+  }
+
+  return platforms.reduce((platformMap, platform) => {
+    const versionParts = [major, minor, patch];
+    const browserSpecificVersion = {};
+    if (prerelease) {
+      if (platform === 'firefox') {
+        versionParts.push(`${buildType}${buildVersion}`);
+      } else {
+        versionParts.push(buildVersion);
+        browserSpecificVersion.version_name = buildType;
+      }
+    }
+    browserSpecificVersion.version = versionParts.join('.');
+    platformMap[platform] = browserSpecificVersion;
+    return platformMap;
+  }, {});
+}
+
 module.exports = {
   BuildTypes,
-  getNextBetaVersionMap,
+  getBrowserVersionMap,
 };
diff --git a/lavamoat/node/policy.json b/lavamoat/node/policy.json
index b9080a2e6..8399643d6 100644
--- a/lavamoat/node/policy.json
+++ b/lavamoat/node/policy.json
@@ -2940,6 +2940,11 @@
         "js-tokens": true
       }
     },
+    "lru-cache": {
+      "packages": {
+        "yallist": true
+      }
+    },
     "lru-queue": {
       "packages": {
         "es5-ext": true
@@ -3815,6 +3820,9 @@
       "globals": {
         "console": true,
         "process": true
+      },
+      "packages": {
+        "lru-cache": true
       }
     },
     "set-value": {
@@ -4684,4 +4692,4 @@
       }
     }
   }
-}
\ No newline at end of file
+}
diff --git a/package.json b/package.json
index 887b99329..6b0e1e337 100644
--- a/package.json
+++ b/package.json
@@ -316,6 +316,7 @@
     "sass": "^1.32.4",
     "sass-loader": "^10.1.1",
     "selenium-webdriver": "4.0.0-alpha.7",
+    "semver": "^7.3.5",
     "serve-handler": "^6.1.2",
     "sinon": "^9.0.0",
     "source-map": "^0.7.2",

From fc413214708e305dd614c3f64f7b95b88cffc03f Mon Sep 17 00:00:00 2001
From: Jyoti Puri <jyotipuri@gmail.com>
Date: Wed, 6 Oct 2021 23:59:57 +0530
Subject: [PATCH 46/56] Support for type 0 transaction (#12252)

Support for type 0 transaction
---
 app/scripts/controllers/transactions/index.js |   5 +-
 .../controllers/transactions/index.test.js    |  52 +++++++
 .../advanced-gas-controls.component.js        |   8 +-
 .../advanced-gas-controls.test.js             |  39 ++++++
 .../edit-gas-display.component.js             |  37 +++--
 .../edit-gas-popover.component.js             |  17 +--
 ui/helpers/utils/transactions.util.js         |   5 +
 ui/helpers/utils/transactions.util.test.js    |  16 +++
 ui/hooks/useGasFeeInputs.js                   | 127 ++++++++----------
 ui/hooks/useGasFeeInputs.test.js              |  30 ++++-
 .../confirm-transaction-base.component.js     |  22 +--
 .../confirm-transaction-base.container.js     |   8 +-
 12 files changed, 250 insertions(+), 116 deletions(-)
 create mode 100644 ui/components/app/advanced-gas-controls/advanced-gas-controls.test.js

diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js
index 0aa43d339..82131dee0 100644
--- a/app/scripts/controllers/transactions/index.js
+++ b/app/scripts/controllers/transactions/index.js
@@ -405,8 +405,9 @@ export default class TransactionController extends EventEmitter {
    * @returns {Promise<object>} resolves with txMeta
    */
   async addTxGasDefaults(txMeta, getCodeResponse) {
-    const eip1559Compatibility = await this.getEIP1559Compatibility();
-
+    const eip1559Compatibility =
+      txMeta.txParams.type !== TRANSACTION_ENVELOPE_TYPES.LEGACY &&
+      (await this.getEIP1559Compatibility());
     const {
       gasPrice: defaultGasPrice,
       maxFeePerGas: defaultMaxFeePerGas,
diff --git a/app/scripts/controllers/transactions/index.test.js b/app/scripts/controllers/transactions/index.test.js
index 56669d267..e4bc6ca20 100644
--- a/app/scripts/controllers/transactions/index.test.js
+++ b/app/scripts/controllers/transactions/index.test.js
@@ -525,6 +525,58 @@ describe('Transaction Controller', function () {
       stub2.restore();
     });
 
+    it('should not add maxFeePerGas and maxPriorityFeePerGas to type-0 transactions', async function () {
+      const TEST_GASPRICE = '0x12a05f200';
+
+      const stub1 = sinon
+        .stub(txController, 'getEIP1559Compatibility')
+        .returns(true);
+
+      const stub2 = sinon
+        .stub(txController, '_getDefaultGasFees')
+        .callsFake(() => ({ gasPrice: TEST_GASPRICE }));
+
+      txController.txStateManager._addTransactionsToState([
+        {
+          id: 1,
+          status: TRANSACTION_STATUSES.UNAPPROVED,
+          metamaskNetworkId: currentNetworkId,
+          txParams: {
+            to: VALID_ADDRESS,
+            from: VALID_ADDRESS_TWO,
+            type: '0x0',
+          },
+          history: [{}],
+        },
+      ]);
+      const txMeta = {
+        id: 1,
+        txParams: {
+          from: '0xc684832530fcbddae4b4230a47e991ddcec2831d',
+          to: '0xc684832530fcbddae4b4230a47e991ddcec2831d',
+          type: '0x0',
+        },
+        history: [{}],
+      };
+      providerResultStub.eth_getBlockByNumber = { gasLimit: '47b784' };
+      providerResultStub.eth_estimateGas = '5209';
+
+      const txMetaWithDefaults = await txController.addTxGasDefaults(txMeta);
+
+      assert.equal(
+        txMetaWithDefaults.txParams.maxFeePerGas,
+        undefined,
+        'should not have maxFeePerGas',
+      );
+      assert.equal(
+        txMetaWithDefaults.txParams.maxPriorityFeePerGas,
+        undefined,
+        'should not have max priority fee per gas',
+      );
+      stub1.restore();
+      stub2.restore();
+    });
+
     it('should not add gasPrice if the fee data is available from the dapp', async function () {
       const TEST_GASPRICE = '0x12a05f200';
       const TEST_MAX_FEE_PER_GAS = '0x12a05f200';
diff --git a/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js b/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js
index d6676f93e..e9e3117ac 100644
--- a/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js
+++ b/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js
@@ -6,7 +6,6 @@ import { I18nContext } from '../../../contexts/i18n';
 import FormField from '../../ui/form-field';
 import { GAS_ESTIMATE_TYPES } from '../../../../shared/constants/gas';
 import { getGasFormErrorText } from '../../../helpers/constants/gas';
-import { checkNetworkAndAccountSupports1559 } from '../../../selectors';
 import { getIsGasEstimatesLoading } from '../../../ducks/metamask/metamask';
 
 export default function AdvancedGasControls({
@@ -24,15 +23,13 @@ export default function AdvancedGasControls({
   maxFeeFiat,
   gasErrors,
   minimumGasLimit,
+  supportsEIP1559,
 }) {
   const t = useContext(I18nContext);
-  const networkAndAccountSupport1559 = useSelector(
-    checkNetworkAndAccountSupports1559,
-  );
   const isGasEstimatesLoading = useSelector(getIsGasEstimatesLoading);
 
   const showFeeMarketFields =
-    networkAndAccountSupport1559 &&
+    supportsEIP1559 &&
     (gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET ||
       gasEstimateType === GAS_ESTIMATE_TYPES.ETH_GASPRICE ||
       isGasEstimatesLoading);
@@ -131,4 +128,5 @@ AdvancedGasControls.propTypes = {
   maxFeeFiat: PropTypes.string,
   gasErrors: PropTypes.object,
   minimumGasLimit: PropTypes.string,
+  supportsEIP1559: PropTypes.bool,
 };
diff --git a/ui/components/app/advanced-gas-controls/advanced-gas-controls.test.js b/ui/components/app/advanced-gas-controls/advanced-gas-controls.test.js
new file mode 100644
index 000000000..f99cd1618
--- /dev/null
+++ b/ui/components/app/advanced-gas-controls/advanced-gas-controls.test.js
@@ -0,0 +1,39 @@
+import React from 'react';
+import configureMockStore from 'redux-mock-store';
+
+import { GAS_ESTIMATE_TYPES } from '../../../../shared/constants/gas';
+import { renderWithProvider } from '../../../../test/jest/rendering';
+
+import AdvancedGasControls from './advanced-gas-controls.component';
+
+const renderComponent = (props) => {
+  const store = configureMockStore([])({ metamask: { identities: [] } });
+  return renderWithProvider(<AdvancedGasControls {...props} />, store);
+};
+
+describe('AdvancedGasControls Component', () => {
+  it('should render correctly', () => {
+    expect(() => {
+      renderComponent();
+    }).not.toThrow();
+  });
+
+  it('should not render maxFee and maxPriorityFee inputs if supportsEIP1559 is false', () => {
+    const { queryByText } = renderComponent({ supportsEIP1559: false });
+    expect(queryByText('Gas Limit')).toBeInTheDocument();
+    expect(queryByText('Gas price')).toBeInTheDocument();
+    expect(queryByText('Max fee')).not.toBeInTheDocument();
+    expect(queryByText('Max priority fee')).not.toBeInTheDocument();
+  });
+
+  it('should render maxFee and maxPriorityFee inputs if supportsEIP1559 is true', () => {
+    const { queryByText } = renderComponent({
+      gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET,
+      supportsEIP1559: true,
+    });
+    expect(queryByText('Gas price')).not.toBeInTheDocument();
+    expect(queryByText('Gas Limit')).toBeInTheDocument();
+    expect(queryByText('Max fee')).toBeInTheDocument();
+    expect(queryByText('Max priority fee')).toBeInTheDocument();
+  });
+});
diff --git a/ui/components/app/edit-gas-display/edit-gas-display.component.js b/ui/components/app/edit-gas-display/edit-gas-display.component.js
index ff1fa5e68..f95f6ebc7 100644
--- a/ui/components/app/edit-gas-display/edit-gas-display.component.js
+++ b/ui/components/app/edit-gas-display/edit-gas-display.component.js
@@ -22,6 +22,7 @@ import {
   FONT_WEIGHT,
 } from '../../../helpers/constants/design-system';
 import { areDappSuggestedAndTxParamGasFeesTheSame } from '../../../helpers/utils/confirm-tx.util';
+import { isLegacyTransaction } from '../../../helpers/utils/transactions.util';
 
 import InfoTooltip from '../../ui/info-tooltip';
 import ErrorMessage from '../../ui/error-message';
@@ -73,17 +74,15 @@ export default function EditGasDisplay({
   const scrollRef = useRef(null);
 
   const isMainnet = useSelector(getIsMainnet);
-  const networkAndAccountSupport1559 = useSelector(
-    checkNetworkAndAccountSupports1559,
-  );
+  const supportsEIP1559 =
+    useSelector(checkNetworkAndAccountSupports1559) &&
+    !isLegacyTransaction(transaction.txParams);
   const showAdvancedInlineGasIfPossible = useSelector(
     getAdvancedInlineGasShown,
   );
 
   const [showAdvancedForm, setShowAdvancedForm] = useState(
-    !estimateToUse ||
-      estimateToUse === 'custom' ||
-      !networkAndAccountSupport1559,
+    !estimateToUse || estimateToUse === 'custom' || !supportsEIP1559,
   );
   const [hideRadioButtons, setHideRadioButtons] = useState(
     showAdvancedInlineGasIfPossible,
@@ -109,7 +108,7 @@ export default function EditGasDisplay({
     (balanceError || estimatesUnavailableWarning) &&
     (!isGasEstimatesLoading || txParamsHaveBeenCustomized);
   const radioButtonsEnabled =
-    networkAndAccountSupport1559 &&
+    supportsEIP1559 &&
     gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET &&
     !requireDappAcknowledgement;
 
@@ -163,12 +162,12 @@ export default function EditGasDisplay({
         )}
         <TransactionTotalBanner
           total={
-            (networkAndAccountSupport1559 || isMainnet) && estimatedMinimumFiat
+            (supportsEIP1559 || isMainnet) && estimatedMinimumFiat
               ? `~ ${estimatedMinimumFiat}`
               : estimatedMinimumNative
           }
           detail={
-            networkAndAccountSupport1559 &&
+            supportsEIP1559 &&
             estimatedMaximumFiat !== undefined && (
               <>
                 <Typography
@@ -188,7 +187,8 @@ export default function EditGasDisplay({
             )
           }
           timing={
-            hasGasErrors === false && (
+            hasGasErrors === false &&
+            supportsEIP1559 && (
               <GasTiming
                 maxFeePerGas={maxFeePerGas}
                 maxPriorityFeePerGas={maxPriorityFeePerGas}
@@ -281,18 +281,17 @@ export default function EditGasDisplay({
               gasErrors={gasErrors}
               onManualChange={onManualChange}
               minimumGasLimit={minimumGasLimit}
+              supportsEIP1559={supportsEIP1559}
             />
           )}
       </div>
-      {networkAndAccountSupport1559 &&
-        !requireDappAcknowledgement &&
-        showEducationButton && (
-          <div className="edit-gas-display__education">
-            <button onClick={onEducationClick}>
-              {t('editGasEducationButtonText')}
-            </button>
-          </div>
-        )}
+      {supportsEIP1559 && !requireDappAcknowledgement && showEducationButton && (
+        <div className="edit-gas-display__education">
+          <button onClick={onEducationClick}>
+            {t('editGasEducationButtonText')}
+          </button>
+        </div>
+      )}
       <div ref={scrollRef} className="edit-gas-display__scroll-bottom" />
     </div>
   );
diff --git a/ui/components/app/edit-gas-popover/edit-gas-popover.component.js b/ui/components/app/edit-gas-popover/edit-gas-popover.component.js
index 64280984c..5825143bc 100644
--- a/ui/components/app/edit-gas-popover/edit-gas-popover.component.js
+++ b/ui/components/app/edit-gas-popover/edit-gas-popover.component.js
@@ -29,6 +29,7 @@ import {
 import LoadingHeartBeat from '../../ui/loading-heartbeat';
 import { checkNetworkAndAccountSupports1559 } from '../../../selectors';
 import { useIncrementedGasFees } from '../../../hooks/useIncrementedGasFees';
+import { isLegacyTransaction } from '../../../helpers/utils/transactions.util';
 
 export default function EditGasPopover({
   popoverTitle = '',
@@ -42,9 +43,9 @@ export default function EditGasPopover({
 }) {
   const t = useContext(I18nContext);
   const dispatch = useDispatch();
-  const networkAndAccountSupport1559 = useSelector(
-    checkNetworkAndAccountSupports1559,
-  );
+  const supportsEIP1559 =
+    useSelector(checkNetworkAndAccountSupports1559) &&
+    !isLegacyTransaction(transaction?.txParams);
   const gasLoadingAnimationIsShowing = useSelector(
     getGasLoadingAnimationIsShowing,
   );
@@ -52,7 +53,7 @@ export default function EditGasPopover({
   const showEducationButton =
     (mode === EDIT_GAS_MODES.MODIFY_IN_PLACE ||
       mode === EDIT_GAS_MODES.SWAPS) &&
-    networkAndAccountSupport1559;
+    supportsEIP1559;
   const [showEducationContent, setShowEducationContent] = useState(false);
 
   const [warning] = useState(null);
@@ -132,7 +133,7 @@ export default function EditGasPopover({
       closePopover();
     }
 
-    const newGasSettings = networkAndAccountSupport1559
+    const newGasSettings = supportsEIP1559
       ? {
           gas: decimalToHex(gasLimit),
           gasLimit: decimalToHex(gasLimit),
@@ -149,7 +150,7 @@ export default function EditGasPopover({
 
     const cleanTransactionParams = { ...updatedTransaction.txParams };
 
-    if (networkAndAccountSupport1559) {
+    if (supportsEIP1559) {
       delete cleanTransactionParams.gasPrice;
     }
 
@@ -182,7 +183,7 @@ export default function EditGasPopover({
         break;
       case EDIT_GAS_MODES.SWAPS:
         // This popover component should only be used for the "FEE_MARKET" type in Swaps.
-        if (networkAndAccountSupport1559) {
+        if (supportsEIP1559) {
           dispatch(updateSwapsUserFeeLevel(estimateToUse || 'custom'));
           dispatch(updateCustomSwapsEIP1559GasParams(newGasSettings));
         }
@@ -201,7 +202,7 @@ export default function EditGasPopover({
     gasPrice,
     maxFeePerGas,
     maxPriorityFeePerGas,
-    networkAndAccountSupport1559,
+    supportsEIP1559,
     estimateToUse,
     estimatedBaseFee,
   ]);
diff --git a/ui/helpers/utils/transactions.util.js b/ui/helpers/utils/transactions.util.js
index b91e79ee9..26ba26273 100644
--- a/ui/helpers/utils/transactions.util.js
+++ b/ui/helpers/utils/transactions.util.js
@@ -8,6 +8,7 @@ import {
   TRANSACTION_TYPES,
   TRANSACTION_GROUP_STATUSES,
   TRANSACTION_STATUSES,
+  TRANSACTION_ENVELOPE_TYPES,
 } from '../../../shared/constants/transaction';
 import { addCurrencies } from '../../../shared/modules/conversion.utils';
 import fetchWithCache from './fetch-with-cache';
@@ -161,6 +162,10 @@ export function sumHexes(...args) {
   return addHexPrefix(total);
 }
 
+export function isLegacyTransaction(txParams) {
+  return txParams.type === TRANSACTION_ENVELOPE_TYPES.LEGACY;
+}
+
 /**
  * Returns a status key for a transaction. Requires parsing the txMeta.txReceipt on top of
  * txMeta.status because txMeta.status does not reflect on-chain errors.
diff --git a/ui/helpers/utils/transactions.util.test.js b/ui/helpers/utils/transactions.util.test.js
index a8663c3a6..db2d65b13 100644
--- a/ui/helpers/utils/transactions.util.test.js
+++ b/ui/helpers/utils/transactions.util.test.js
@@ -2,6 +2,7 @@ import {
   TRANSACTION_TYPES,
   TRANSACTION_GROUP_STATUSES,
   TRANSACTION_STATUSES,
+  TRANSACTION_ENVELOPE_TYPES,
 } from '../../../shared/constants/transaction';
 import * as utils from './transactions.util';
 
@@ -59,4 +60,19 @@ describe('Transactions utils', () => {
       });
     });
   });
+
+  describe('isLegacyTransaction', () => {
+    it('should return true if transaction is type-0', () => {
+      expect(
+        utils.isLegacyTransaction({ type: TRANSACTION_ENVELOPE_TYPES.LEGACY }),
+      ).toStrictEqual(true);
+    });
+    it('should return false if transaction is not type-0', () => {
+      expect(
+        utils.isLegacyTransaction({
+          type: TRANSACTION_ENVELOPE_TYPES.FEE_MARKET,
+        }),
+      ).toStrictEqual(false);
+    });
+  });
 });
diff --git a/ui/hooks/useGasFeeInputs.js b/ui/hooks/useGasFeeInputs.js
index 7441bb07f..f58106123 100644
--- a/ui/hooks/useGasFeeInputs.js
+++ b/ui/hooks/useGasFeeInputs.js
@@ -37,6 +37,7 @@ import {
   bnLessThanEqualTo,
 } from '../helpers/utils/util';
 import { GAS_FORM_ERRORS } from '../helpers/constants/gas';
+import { isLegacyTransaction } from '../helpers/utils/transactions.util';
 
 import { useCurrencyDisplay } from './useCurrencyDisplay';
 import { useGasFeeEstimates } from './useGasFeeEstimates';
@@ -155,9 +156,9 @@ export function useGasFeeInputs(
   editGasMode,
 ) {
   const { balance: ethBalance } = useSelector(getSelectedAccount);
-  const networkAndAccountSupports1559 = useSelector(
-    checkNetworkAndAccountSupports1559,
-  );
+  const supportsEIP1559 =
+    useSelector(checkNetworkAndAccountSupports1559) &&
+    !isLegacyTransaction(transaction?.txParams);
   // We need to know whether to show fiat conversions or not, so that we can
   // default our fiat values to empty strings if showing fiat is not wanted or
   // possible.
@@ -188,13 +189,13 @@ export function useGasFeeInputs(
   } = useGasFeeEstimates();
 
   const [initialMaxFeePerGas] = useState(
-    networkAndAccountSupports1559 && !transaction?.txParams?.maxFeePerGas
+    supportsEIP1559 && !transaction?.txParams?.maxFeePerGas
       ? Number(hexWEIToDecGWEI(transaction?.txParams?.gasPrice))
       : Number(hexWEIToDecGWEI(transaction?.txParams?.maxFeePerGas)),
   );
+
   const [initialMaxPriorityFeePerGas] = useState(
-    networkAndAccountSupports1559 &&
-      !transaction?.txParams?.maxPriorityFeePerGas
+    supportsEIP1559 && !transaction?.txParams?.maxPriorityFeePerGas
       ? initialMaxFeePerGas
       : Number(hexWEIToDecGWEI(transaction?.txParams?.maxPriorityFeePerGas)),
   );
@@ -277,7 +278,9 @@ export function useGasFeeInputs(
   );
   const gasPriceToUse =
     gasPrice !== null &&
-    (gasPriceHasBeenManuallySet || gasPriceEstimatesHaveNotChanged)
+    (gasPriceHasBeenManuallySet ||
+      gasPriceEstimatesHaveNotChanged ||
+      isLegacyTransaction(transaction?.txParams))
       ? gasPrice
       : getGasPriceEstimate(
           gasFeeEstimates,
@@ -294,7 +297,7 @@ export function useGasFeeInputs(
   const gasSettings = {
     gasLimit: decimalToHex(gasLimit),
   };
-  if (networkAndAccountSupports1559) {
+  if (supportsEIP1559) {
     gasSettings.maxFeePerGas = maxFeePerGasToUse
       ? decGWEIToHexWEI(maxFeePerGasToUse)
       : decGWEIToHexWEI(gasPriceToUse || '0');
@@ -396,7 +399,7 @@ export function useGasFeeInputs(
   // This ensures these are applied when the api fails to return a fee market type
   // It is okay if these errors get overwritten below, as those overwrites can only
   // happen when the estimate api is live.
-  if (networkAndAccountSupports1559) {
+  if (supportsEIP1559) {
     if (bnLessThanEqualTo(maxPriorityFeePerGasToUse, 0)) {
       gasErrors.maxPriorityFee = GAS_FORM_ERRORS.MAX_PRIORITY_FEE_BELOW_MINIMUM;
     } else if (bnGreaterThan(maxPriorityFeePerGasToUse, maxFeePerGasToUse)) {
@@ -404,67 +407,53 @@ export function useGasFeeInputs(
     }
   }
 
-  switch (gasEstimateType) {
-    case GAS_ESTIMATE_TYPES.FEE_MARKET:
-      if (bnLessThanEqualTo(maxPriorityFeePerGasToUse, 0)) {
-        gasErrors.maxPriorityFee =
-          GAS_FORM_ERRORS.MAX_PRIORITY_FEE_BELOW_MINIMUM;
-      } else if (
-        !isGasEstimatesLoading &&
-        bnLessThan(
-          maxPriorityFeePerGasToUse,
-          gasFeeEstimates?.low?.suggestedMaxPriorityFeePerGas,
-        )
-      ) {
-        gasWarnings.maxPriorityFee = GAS_FORM_ERRORS.MAX_PRIORITY_FEE_TOO_LOW;
-      } else if (bnGreaterThan(maxPriorityFeePerGasToUse, maxFeePerGasToUse)) {
-        gasErrors.maxFee = GAS_FORM_ERRORS.MAX_FEE_IMBALANCE;
-      } else if (
-        gasFeeEstimates?.high &&
-        bnGreaterThan(
-          maxPriorityFeePerGasToUse,
-          gasFeeEstimates.high.suggestedMaxPriorityFeePerGas *
-            HIGH_FEE_WARNING_MULTIPLIER,
-        )
-      ) {
-        gasWarnings.maxPriorityFee =
-          GAS_FORM_ERRORS.MAX_PRIORITY_FEE_HIGH_WARNING;
-      }
+  if (supportsEIP1559 && gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET) {
+    if (bnLessThanEqualTo(maxPriorityFeePerGasToUse, 0)) {
+      gasErrors.maxPriorityFee = GAS_FORM_ERRORS.MAX_PRIORITY_FEE_BELOW_MINIMUM;
+    } else if (
+      !isGasEstimatesLoading &&
+      bnLessThan(
+        maxPriorityFeePerGasToUse,
+        gasFeeEstimates?.low?.suggestedMaxPriorityFeePerGas,
+      )
+    ) {
+      gasWarnings.maxPriorityFee = GAS_FORM_ERRORS.MAX_PRIORITY_FEE_TOO_LOW;
+    } else if (bnGreaterThan(maxPriorityFeePerGasToUse, maxFeePerGasToUse)) {
+      gasErrors.maxFee = GAS_FORM_ERRORS.MAX_FEE_IMBALANCE;
+    } else if (
+      gasFeeEstimates?.high &&
+      bnGreaterThan(
+        maxPriorityFeePerGasToUse,
+        gasFeeEstimates.high.suggestedMaxPriorityFeePerGas *
+          HIGH_FEE_WARNING_MULTIPLIER,
+      )
+    ) {
+      gasWarnings.maxPriorityFee =
+        GAS_FORM_ERRORS.MAX_PRIORITY_FEE_HIGH_WARNING;
+    }
 
-      if (
-        !isGasEstimatesLoading &&
-        bnLessThan(
-          maxFeePerGasToUse,
-          gasFeeEstimates?.low?.suggestedMaxFeePerGas,
-        )
-      ) {
-        gasWarnings.maxFee = GAS_FORM_ERRORS.MAX_FEE_TOO_LOW;
-      } else if (
-        gasFeeEstimates?.high &&
-        bnGreaterThan(
-          maxFeePerGasToUse,
-          gasFeeEstimates.high.suggestedMaxFeePerGas *
-            HIGH_FEE_WARNING_MULTIPLIER,
-        )
-      ) {
-        gasWarnings.maxFee = GAS_FORM_ERRORS.MAX_FEE_HIGH_WARNING;
-      }
-      break;
-    case GAS_ESTIMATE_TYPES.LEGACY:
-    case GAS_ESTIMATE_TYPES.ETH_GASPRICE:
-    case GAS_ESTIMATE_TYPES.NONE:
-      if (networkAndAccountSupports1559) {
-        estimatesUnavailableWarning = true;
-      }
-      if (
-        (!networkAndAccountSupports1559 || transaction?.txParams?.gasPrice) &&
-        bnLessThanEqualTo(gasPriceToUse, 0)
-      ) {
-        gasErrors.gasPrice = GAS_FORM_ERRORS.GAS_PRICE_TOO_LOW;
-      }
-      break;
-    default:
-      break;
+    if (
+      !isGasEstimatesLoading &&
+      bnLessThan(maxFeePerGasToUse, gasFeeEstimates?.low?.suggestedMaxFeePerGas)
+    ) {
+      gasWarnings.maxFee = GAS_FORM_ERRORS.MAX_FEE_TOO_LOW;
+    } else if (
+      gasFeeEstimates?.high &&
+      bnGreaterThan(
+        maxFeePerGasToUse,
+        gasFeeEstimates.high.suggestedMaxFeePerGas *
+          HIGH_FEE_WARNING_MULTIPLIER,
+      )
+    ) {
+      gasWarnings.maxFee = GAS_FORM_ERRORS.MAX_FEE_HIGH_WARNING;
+    }
+  } else if (supportsEIP1559) {
+    estimatesUnavailableWarning = true;
+  } else if (
+    (!supportsEIP1559 || transaction?.txParams?.gasPrice) &&
+    bnLessThanEqualTo(gasPriceToUse, 0)
+  ) {
+    gasErrors.gasPrice = GAS_FORM_ERRORS.GAS_PRICE_TOO_LOW;
   }
 
   // Determine if we have any errors which should block submission
diff --git a/ui/hooks/useGasFeeInputs.test.js b/ui/hooks/useGasFeeInputs.test.js
index 60972dbe4..f8f6cf6fc 100644
--- a/ui/hooks/useGasFeeInputs.test.js
+++ b/ui/hooks/useGasFeeInputs.test.js
@@ -2,6 +2,7 @@ import { act, renderHook } from '@testing-library/react-hooks';
 import { useSelector } from 'react-redux';
 import { GAS_ESTIMATE_TYPES } from '../../shared/constants/gas';
 import { multiplyCurrencies } from '../../shared/modules/conversion.utils';
+import { TRANSACTION_ENVELOPE_TYPES } from '../../shared/constants/transaction';
 import {
   getConversionRate,
   getNativeCurrency,
@@ -223,6 +224,31 @@ describe('useGasFeeInputs', () => {
     });
   });
 
+  describe('when transaction is type-0', () => {
+    beforeEach(() => {
+      useGasFeeEstimates.mockImplementation(
+        () => FEE_MARKET_ESTIMATE_RETURN_VALUE,
+      );
+      useSelector.mockImplementation(generateUseSelectorRouter());
+    });
+    it('returns gasPrice appropriately, and "0" for EIP1559 fields', () => {
+      const { result } = renderHook(() =>
+        useGasFeeInputs('medium', {
+          txParams: {
+            value: '3782DACE9D90000',
+            gasLimit: '0x5028',
+            gasPrice: '0x5028',
+            type: TRANSACTION_ENVELOPE_TYPES.LEGACY,
+          },
+        }),
+      );
+      expect(result.current.gasPrice).toBe(0.00002052);
+      expect(result.current.maxFeePerGas).toBe('0');
+      expect(result.current.maxPriorityFeePerGas).toBe('0');
+      expect(result.current.hasBlockingGasErrors).toBeUndefined();
+    });
+  });
+
   describe('when using EIP 1559 API for estimation', () => {
     beforeEach(() => {
       useGasFeeEstimates.mockImplementation(
@@ -260,7 +286,9 @@ describe('useGasFeeInputs', () => {
           checkNetworkAndAccountSupports1559Response: true,
         }),
       );
-      const { result } = renderHook(() => useGasFeeInputs());
+      const { result } = renderHook(() =>
+        useGasFeeInputs(null, { txParams: {}, userFeeLevel: 'medium' }),
+      );
       expect(result.current.maxFeePerGas).toBe(
         FEE_MARKET_ESTIMATE_RETURN_VALUE.gasFeeEstimates.medium
           .suggestedMaxFeePerGas,
diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js
index 65d366970..363b0fa6a 100644
--- a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js
+++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js
@@ -128,6 +128,7 @@ export default class ConfirmTransactionBase extends Component {
     showLedgerSteps: PropTypes.bool.isRequired,
     isFirefox: PropTypes.bool.isRequired,
     nativeCurrency: PropTypes.string,
+    supportsEIP1559: PropTypes.bool,
   };
 
   state = {
@@ -309,6 +310,7 @@ export default class ConfirmTransactionBase extends Component {
       isMainnet,
       showLedgerSteps,
       isFirefox,
+      supportsEIP1559,
     } = this.props;
     const { t } = this.context;
 
@@ -541,15 +543,17 @@ export default class ConfirmTransactionBase extends Component {
                   ) : (
                     ''
                   )}
-                  <GasTiming
-                    maxPriorityFeePerGas={hexWEIToDecGWEI(
-                      maxPriorityFeePerGas ||
-                        txData.txParams.maxPriorityFeePerGas,
-                    )}
-                    maxFeePerGas={hexWEIToDecGWEI(
-                      maxFeePerGas || txData.txParams.maxFeePerGas,
-                    )}
-                  />
+                  {supportsEIP1559 && (
+                    <GasTiming
+                      maxPriorityFeePerGas={hexWEIToDecGWEI(
+                        maxPriorityFeePerGas ||
+                          txData.txParams.maxPriorityFeePerGas,
+                      )}
+                      maxFeePerGas={hexWEIToDecGWEI(
+                        maxFeePerGas || txData.txParams.maxFeePerGas,
+                      )}
+                    />
+                  )}
                 </>
               }
             />,
diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js
index b7d8b8592..6309ffd57 100644
--- a/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js
+++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js
@@ -47,6 +47,7 @@ import {
   getNativeCurrency,
 } from '../../ducks/metamask/metamask';
 import { getGasLoadingAnimationIsShowing } from '../../ducks/app/app';
+import { isLegacyTransaction } from '../../helpers/utils/transactions.util';
 import ConfirmTransactionBase from './confirm-transaction-base.component';
 
 let customNonceValue = '';
@@ -66,7 +67,6 @@ const mapStateToProps = (state, ownProps) => {
   } = ownProps;
   const { id: paramsTransactionId } = params;
   const isMainnet = getIsMainnet(state);
-  const supportsEIP1599 = checkNetworkAndAccountSupports1559(state);
 
   const isGasEstimatesLoading = getIsGasEstimatesLoading(state);
   const gasLoadingAnimationIsShowing = getGasLoadingAnimationIsShowing(state);
@@ -122,6 +122,8 @@ const mapStateToProps = (state, ownProps) => {
   const toEns = ensResolutionsByAddress[checksummedAddress] || '';
   const toNickname = addressBookObject ? addressBookObject.name : '';
   const transactionStatus = transaction ? transaction.status : '';
+  const supportsEIP1559 =
+    checkNetworkAndAccountSupports1559(state) && !isLegacyTransaction(txParams);
 
   const {
     hexTransactionAmount,
@@ -163,7 +165,7 @@ const mapStateToProps = (state, ownProps) => {
   }
   customNonceValue = getCustomNonceValue(state);
   const isEthGasPrice = getIsEthGasPriceFetched(state);
-  const noGasPrice = !supportsEIP1599 && getNoGasPriceFetched(state);
+  const noGasPrice = !supportsEIP1559 && getNoGasPriceFetched(state);
   const { useNativeCurrencyAsPrimaryCurrency } = getPreferences(state);
   const gasFeeIsCustom =
     fullTxData.userFeeLevel === 'custom' ||
@@ -210,7 +212,7 @@ const mapStateToProps = (state, ownProps) => {
     isMainnet,
     isEthGasPrice,
     noGasPrice,
-    supportsEIP1599,
+    supportsEIP1559,
     gasIsLoading: isGasEstimatesLoading || gasLoadingAnimationIsShowing,
     useNativeCurrencyAsPrimaryCurrency,
     maxFeePerGas: gasEstimationObject.maxFeePerGas,

From 614228cba7fee3b4b83f5d56f8ac38e4486a53a5 Mon Sep 17 00:00:00 2001
From: Alex Donesky <adonesky@gmail.com>
Date: Wed, 6 Oct 2021 13:52:25 -0500
Subject: [PATCH 47/56] Onboarding V2 Secure Your Wallet view (#12208)

* secure-your-wallet onboarding view
---
 app/_locales/en/messages.json                 |  24 ++-
 app/images/warning-icon.png                   | Bin 0 -> 1757 bytes
 .../step-progress-bar/step-progress-bar.js    |   2 +-
 ui/components/ui/box/box.js                   |   4 +
 ui/components/ui/box/box.scss                 |   6 +
 .../ui/check-box/check-box.component.js       |  12 +-
 ui/components/ui/popover/popover.component.js |  57 +++---
 ui/css/design-system/attributes.scss          |   6 +
 ui/helpers/constants/design-system.js         |   7 +
 ui/pages/onboarding-flow/index.scss           |   1 +
 ui/pages/onboarding-flow/onboarding-flow.js   |   9 +-
 .../secure-your-wallet/index.scss             |  47 +++++
 .../secure-your-wallet/secure-your-wallet.js  | 167 ++++++++++++++++++
 .../secure-your-wallet.stories.js             |  15 ++
 .../secure-your-wallet.test.js                |  59 +++++++
 .../skip-srp-backup-popover.js                |  79 +++++++++
 ui/pages/routes/routes.component.js           |   2 +-
 17 files changed, 463 insertions(+), 34 deletions(-)
 create mode 100644 app/images/warning-icon.png
 create mode 100644 ui/pages/onboarding-flow/secure-your-wallet/index.scss
 create mode 100644 ui/pages/onboarding-flow/secure-your-wallet/secure-your-wallet.js
 create mode 100644 ui/pages/onboarding-flow/secure-your-wallet/secure-your-wallet.stories.js
 create mode 100644 ui/pages/onboarding-flow/secure-your-wallet/secure-your-wallet.test.js
 create mode 100644 ui/pages/onboarding-flow/secure-your-wallet/skip-srp-backup-popover.js

diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index afe8ad1ef..887ab9e42 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -1042,6 +1042,9 @@
   "getStarted": {
     "message": "Get Started"
   },
+  "goBack": {
+    "message": "Go Back"
+  },
   "goerli": {
     "message": "Goerli Test Network"
   },
@@ -1900,6 +1903,12 @@
   "seedPhraseEnterMissingWords": {
     "message": "Confirm Secret Recovery Phrase"
   },
+  "seedPhraseIntroNotRecommendedButtonCopy": {
+    "message": "Remind me later (not recommended)"
+  },
+  "seedPhraseIntroRecommendedButtonCopy": {
+    "message": "Secure my wallet (recommended)"
+  },
   "seedPhraseIntroSidebarBulletFour": {
     "message": "Write down and store in multiple secret places."
   },
@@ -1913,13 +1922,13 @@
     "message": "Store in a bank vault."
   },
   "seedPhraseIntroSidebarCopyOne": {
-    "message": "Your Secret Recovery Phrase is the “master key” to your wallet and funds."
+    "message": "Your Secret Recovery Phrase is a 12-word phrase that is the “master key” to your wallet and your funds"
   },
   "seedPhraseIntroSidebarCopyThree": {
-    "message": "If someone asks for your Secret Recovery Phrase, they are most likely trying to scam you."
+    "message": "If someone asks for your recovery phrase they are likely trying to scam you and steal your wallet funds"
   },
   "seedPhraseIntroSidebarCopyTwo": {
-    "message": "Never, ever share your Secret Recovery Phrase, even with MetaMask!"
+    "message": "Never, ever share your Secret Recovery Phrase, not even with MetaMask!"
   },
   "seedPhraseIntroSidebarTitleOne": {
     "message": "What is a Secret Recovery Phrase?"
@@ -2071,6 +2080,15 @@
   "signed": {
     "message": "Signed"
   },
+  "skip": {
+    "message": "Skip"
+  },
+  "skipAccountSecurity": {
+    "message": "Skip Account Security?"
+  },
+  "skipAccountSecurityDetails": {
+    "message": "I understand that until I back up my Secret Recovery Phrase, I may lose my accounts and all of their assets."
+  },
   "slow": {
     "message": "Slow"
   },
diff --git a/app/images/warning-icon.png b/app/images/warning-icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..06b4be3327b4deeccec396a254491a4a17bd154d
GIT binary patch
literal 1757
zcmV<31|s>1P)<h;3K|Lk000e1NJLTq001}u001}$1^@s6sD?Wp00009a7bBm000XU
z000XU0RWnu7ytkO0drDELIAGL9O(c600d`2O+f$vv5yP<VFdsH269P6K~#7F)mv+9
zQ)L)_Io+i_ZQX^=354l3hG=BSf`MfdVNpy7Wc!2S<(EtJkC&)1LY(o3B+Q>&{$Mf_
z6N$+Fj7%{B#5q_tqS<5+!HhX&nK&5Ry7hE7I{lun{c10#TYGxG(LPDnY;Di^o<8sQ
z-oAIs{$(H&F+Zu%^o@R+?)!sg_+x;49uLjk8V-A3=88hfe5jBVhn>`l^<*t8A$4Op
znT=&;5Ckdjue~(-%_&YU>*3XuRa`{b#r|i-u2RN>uCUMCyFJ1m^O~2YuKrH512<{*
zuR&6uEGOsIO3K;!ESZ6%2!hz?$rc)IZsDYyHC3c;uHb|WU5}CVUh1aF^Bv^7JH`}U
zPp;Qq;c6R&A%cRK`@6H`*j`Bw?A~FRR4hQ<u@k4rbN(VH2}O+%O_-pky^DqqA0u^B
zIpyxHGo4r@1kssmeL<p$AZ~(C?amj+X|nAi6}(+f)+g4{QUXN(Y50sB+p4Ib{#EXr
z;z3*l;Q@#CAE4}YB}<}OEMSj9&8c55Q_+#exJ-6Yf*^PEt$pNpW(y~m3K&1pLY|+x
z;v%^qK|wBe&n|M*R8uMe&S$*&+eJw#v0g}JasbwZwPB5sNkk}g3BsWWvrI*D0$3Z?
zIJEy$n)hhB@6k2U;X|L({FFxd^)FL8;BM2`WU<+(@SQiJUW-!Defk^^1*zgt;z8ct
zTJp4Ykmo{I)N5e`Vap>d)~z`3;e87QMu4@tSZp3XbS%nL!w9;+KS$Y*l|?1S=>Q}i
zwoMyY!rv11_aH&8Uej?dce(;=cb`5JrecsFOcP!y9n}+nsoGg87ph{Aptt9WboG#C
z$wtoZ=V**oIzmNG5T4VrJWr~6Jo8{FmTZzxG4qktq;>cDf9E%E?_}m#MKUoSAU4r@
zh9}K3fx;Uq0-T`M(@S<%iOYh!^(@V?at9C<flQ#QfPN6nRPi!$Sxiz{aSwIeBvTC#
zX&+RazIvTxL6|8TUkZMpF(eC63+iEihZf4RRRpK$zJ5}sy05UCK1wnG_266pkQ2}N
zhM^!DOtOHE9?VqnX&PB1ndk7qBm5brDU^efXi@=4l4Us@zk-Y)G8h@*0m`A*2_U{g
ztviRCcuNqmDwEw43c~@(0IY;N8Ce8HSO7U(e`a8ilZI*H=DC5S0uJY1z{8pe+!Nds
zl7LXhBFr%o!faupFjZMFogmae#sWa4g~E?W4Pm~r!iMPth5Ltk&X9HoyqaIN|2;Mo
z6tPiO*ig`sGHVaOsfe(EqB#`EE)XJ|5j~#e^)Cg(hM5iroWM=7IPIpn>f0e<LGLsP
zB!p)b06DQwVcKS`g%iZ_T=tq0?%|RJloO6cuk1p2xW!uUdMe@l2pb@0mWn=YinaGu
zMvxsmMt-Y_!tYr8dkC4lUf5CWHvV@Dd)9+1km*20Gqwz@MXe~O3~N~_P5jVK$v{*K
zFgKW|-U3mrh>At;b&yB)fiD{gLF67z6u3_A^EDvI6#R3Ln}?B#0*jppR13Jq;Oiey
zvcR{GhtU%(tYeOG!jZUwV<_v|64Ea9kP*mo?@-Yw5La#NbQs{BZO?^H8u_X@X|qTb
zz?tRM?&OE1JkUc*VW#O0Y#HCkSnwp66NH)OzgQ<NvtLSJ%U~=;AegG<zfeb>A3M@o
zMNwemEMFi<5Gv;G+OZ_POIg6$Bgam{l5L@)%!ZIGK)spY`l6;V!U#gel@0H47oAR~
zinU?_!&M(WG!#`XIi9X0=l1HfGF7bAxvg4vhDNs@EMRs#d;LaQkmO?}KVJ@WETPaP
z2vc2k@MF^2I(Ze4s=(T?#-gK*q&W2lgJM?nuw{JEeX=zrNn%Y{8`ikc7)gACKyU72
z=WdlLb2tjK6v`zv2qYj#+Me3LN#5^KuGwq-w5qWou2HVInohV-@s~%*qN+6X?k7_E
zn2Q0i`EPdb44WF4asmmDQ{u1`XZhKWmj#=;G9gq$0<!X>_sOw!OG2MFG!P4u#Y6^(
zqJoz}RP<*wMl6S=kokM+VtUGjkkm-Cn5*%!6Do@<kCU};-e}55Gs-tqI7W#Hm@<?O
z@ChkaID5PO2de0=*jSTS45aI|5kUyO6Lj_XP&y-JU0cd>;#xkOo(wSo(+GwFr^YzC
z!sG@{c%Zc2zZIY-i?$^?d*}^W*OZz<Dir?#vRIDTpQc^;00000NkvXXu0mjfc6>LN

literal 0
HcmV?d00001

diff --git a/ui/components/app/step-progress-bar/step-progress-bar.js b/ui/components/app/step-progress-bar/step-progress-bar.js
index 72e80319c..a23ecc3c4 100644
--- a/ui/components/app/step-progress-bar/step-progress-bar.js
+++ b/ui/components/app/step-progress-bar/step-progress-bar.js
@@ -14,7 +14,7 @@ const stages = {
 export default function StepProgressBar({ stage = 'PASSWORD_CREATE' }) {
   const t = useI18nContext();
   return (
-    <Box margin={4}>
+    <Box>
       <ul className="progressbar">
         <li
           className={classnames({
diff --git a/ui/components/ui/box/box.js b/ui/components/ui/box/box.js
index 47d9617e6..276e7ad3b 100644
--- a/ui/components/ui/box/box.js
+++ b/ui/components/ui/box/box.js
@@ -10,6 +10,7 @@ import {
   JUSTIFY_CONTENT,
   SIZES,
   TEXT_ALIGN,
+  FLEX_DIRECTION,
 } from '../../../helpers/constants/design-system';
 
 const ValidSize = PropTypes.oneOf([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
@@ -72,6 +73,7 @@ export default function Box({
   alignItems,
   justifyContent,
   textAlign,
+  flexDirection = FLEX_DIRECTION.ROW,
   display,
   width,
   height,
@@ -115,6 +117,7 @@ export default function Box({
       !display && (Boolean(justifyContent) || Boolean(alignItems)),
     [`box--justify-content-${justifyContent}`]: Boolean(justifyContent),
     [`box--align-items-${alignItems}`]: Boolean(alignItems),
+    [`box--flex-direction-${flexDirection}`]: Boolean(flexDirection),
     // text align
     [`box--text-align-${textAlign}`]: Boolean(textAlign),
     // display
@@ -132,6 +135,7 @@ export default function Box({
 
 Box.propTypes = {
   children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
+  flexDirection: PropTypes.oneOf(Object.values(FLEX_DIRECTION)),
   margin: MultipleSizes,
   marginTop: ValidSize,
   marginBottom: ValidSize,
diff --git a/ui/components/ui/box/box.scss b/ui/components/ui/box/box.scss
index 9e7c0b776..59865bbf7 100644
--- a/ui/components/ui/box/box.scss
+++ b/ui/components/ui/box/box.scss
@@ -84,6 +84,12 @@ $attributes: padding, margin;
     }
   }
 
+  @each $direction in design-system.$flex-direction {
+    &--flex-direction-#{$direction} {
+      flex-direction: $direction;
+    }
+  }
+
   // Width and Height
   &--width-full {
     width: 100%;
diff --git a/ui/components/ui/check-box/check-box.component.js b/ui/components/ui/check-box/check-box.component.js
index 825d2cc16..8d8d4a617 100644
--- a/ui/components/ui/check-box/check-box.component.js
+++ b/ui/components/ui/check-box/check-box.component.js
@@ -10,7 +10,15 @@ const CHECKBOX_STATE = {
 
 export const { CHECKED, INDETERMINATE, UNCHECKED } = CHECKBOX_STATE;
 
-const CheckBox = ({ className, disabled, id, onClick, checked, title }) => {
+const CheckBox = ({
+  className,
+  disabled,
+  id,
+  onClick,
+  checked,
+  title,
+  dataTestId,
+}) => {
   if (typeof checked === 'boolean') {
     // eslint-disable-next-line no-param-reassign
     checked = checked ? CHECKBOX_STATE.CHECKED : CHECKBOX_STATE.UNCHECKED;
@@ -43,6 +51,7 @@ const CheckBox = ({ className, disabled, id, onClick, checked, title }) => {
       readOnly
       ref={ref}
       title={title}
+      data-testid={dataTestId}
       type="checkbox"
     />
   );
@@ -56,6 +65,7 @@ CheckBox.propTypes = {
   checked: PropTypes.oneOf([...Object.keys(CHECKBOX_STATE), true, false])
     .isRequired,
   title: PropTypes.string,
+  dataTestId: PropTypes.string,
 };
 
 CheckBox.defaultProps = {
diff --git a/ui/components/ui/popover/popover.component.js b/ui/components/ui/popover/popover.component.js
index c4bae4ffc..637f07528 100644
--- a/ui/components/ui/popover/popover.component.js
+++ b/ui/components/ui/popover/popover.component.js
@@ -20,6 +20,7 @@ const Popover = ({
   centerTitle,
 }) => {
   const t = useI18nContext();
+  const showHeader = title || onBack || subtitle || onClose;
   return (
     <div className="popover-container">
       {CustomBackground ? (
@@ -32,36 +33,38 @@ const Popover = ({
         ref={popoverRef}
       >
         {showArrow ? <div className="popover-arrow" /> : null}
-        <header className="popover-header">
-          <div
-            className={classnames(
-              'popover-header__title',
-              centerTitle ? 'center' : '',
-            )}
-          >
-            <h2 title={title}>
-              {onBack ? (
+        {showHeader && (
+          <header className="popover-header">
+            <div
+              className={classnames(
+                'popover-header__title',
+                centerTitle ? 'center' : '',
+              )}
+            >
+              <h2 title={title}>
+                {onBack ? (
+                  <button
+                    className="fas fa-chevron-left popover-header__button"
+                    title={t('back')}
+                    onClick={onBack}
+                  />
+                ) : null}
+                {title}
+              </h2>
+              {onClose ? (
                 <button
-                  className="fas fa-chevron-left popover-header__button"
-                  title={t('back')}
-                  onClick={onBack}
+                  className="fas fa-times popover-header__button"
+                  title={t('close')}
+                  data-testid="popover-close"
+                  onClick={onClose}
                 />
               ) : null}
-              {title}
-            </h2>
-            {onClose ? (
-              <button
-                className="fas fa-times popover-header__button"
-                title={t('close')}
-                data-testid="popover-close"
-                onClick={onClose}
-              />
+            </div>
+            {subtitle ? (
+              <p className="popover-header__subtitle">{subtitle}</p>
             ) : null}
-          </div>
-          {subtitle ? (
-            <p className="popover-header__subtitle">{subtitle}</p>
-          ) : null}
-        </header>
+          </header>
+        )}
         {children ? (
           <div className={classnames('popover-content', contentClassName)}>
             {children}
@@ -78,7 +81,7 @@ const Popover = ({
 };
 
 Popover.propTypes = {
-  title: PropTypes.string.isRequired,
+  title: PropTypes.string,
   subtitle: PropTypes.string,
   children: PropTypes.node,
   footer: PropTypes.node,
diff --git a/ui/css/design-system/attributes.scss b/ui/css/design-system/attributes.scss
index d2836769b..7373c7c3b 100644
--- a/ui/css/design-system/attributes.scss
+++ b/ui/css/design-system/attributes.scss
@@ -13,6 +13,12 @@ $justify-content:
   space-between,
   space-evenly;
 
+$flex-direction:
+  row,
+  row-reverse,
+  column,
+  column-reverse;
+
 $fractions: (
   1\/2: 50%,
   1\/3: 33.333333%,
diff --git a/ui/helpers/constants/design-system.js b/ui/helpers/constants/design-system.js
index 7edcbc0b2..2f3b4bdd9 100644
--- a/ui/helpers/constants/design-system.js
+++ b/ui/helpers/constants/design-system.js
@@ -88,6 +88,13 @@ export const JUSTIFY_CONTENT = {
   SPACE_EVENLY: 'space-evenly',
 };
 
+export const FLEX_DIRECTION = {
+  ROW: 'row',
+  ROW_REVERSE: 'row-reverse',
+  COLUMN: 'column',
+  COLUMN_REVERSE: 'column-reverse',
+};
+
 export const DISPLAY = {
   BLOCK: 'block',
   FLEX: 'flex',
diff --git a/ui/pages/onboarding-flow/index.scss b/ui/pages/onboarding-flow/index.scss
index 8e3431e05..8d5a5cd0c 100644
--- a/ui/pages/onboarding-flow/index.scss
+++ b/ui/pages/onboarding-flow/index.scss
@@ -1,6 +1,7 @@
 @import 'recovery-phrase/index';
 @import 'new-account/index';
 @import 'onboarding-app-header/index';
+@import 'secure-your-wallet/index';
 
 .onboarding-flow {
   width: 100%;
diff --git a/ui/pages/onboarding-flow/onboarding-flow.js b/ui/pages/onboarding-flow/onboarding-flow.js
index 7bb908383..82c1b00e6 100644
--- a/ui/pages/onboarding-flow/onboarding-flow.js
+++ b/ui/pages/onboarding-flow/onboarding-flow.js
@@ -8,6 +8,7 @@ import {
   ONBOARDING_CONFIRM_SRP_ROUTE,
   ONBOARDING_UNLOCK_ROUTE,
   DEFAULT_ROUTE,
+  ONBOARDING_SECURE_YOUR_WALLET_ROUTE,
 } from '../../helpers/constants/routes';
 import {
   getCompletedOnboarding,
@@ -23,6 +24,7 @@ import { getFirstTimeFlowTypeRoute } from '../../selectors';
 import OnboardingFlowSwitch from './onboarding-flow-switch/onboarding-flow-switch';
 import NewAccount from './new-account/new-account';
 import ReviewRecoveryPhrase from './recovery-phrase/review-recovery-phrase';
+import SecureYourWallet from './secure-your-wallet/secure-your-wallet';
 import ConfirmRecoveryPhrase from './recovery-phrase/confirm-recovery-phrase';
 
 export default function OnboardingFlow() {
@@ -39,7 +41,7 @@ export default function OnboardingFlow() {
     // For ONBOARDING_V2 dev purposes,
     // Remove when ONBOARDING_V2 dev complete
     if (process.env.ONBOARDING_V2) {
-      history.push(ONBOARDING_REVIEW_SRP_ROUTE);
+      history.push(ONBOARDING_SECURE_YOUR_WALLET_ROUTE);
       return;
     }
 
@@ -87,6 +89,11 @@ export default function OnboardingFlow() {
               />
             )}
           />
+          <Route
+            exact
+            path={ONBOARDING_SECURE_YOUR_WALLET_ROUTE}
+            component={SecureYourWallet}
+          />
           <Route
             path={ONBOARDING_REVIEW_SRP_ROUTE}
             render={() => <ReviewRecoveryPhrase seedPhrase={seedPhrase} />}
diff --git a/ui/pages/onboarding-flow/secure-your-wallet/index.scss b/ui/pages/onboarding-flow/secure-your-wallet/index.scss
new file mode 100644
index 000000000..ef2d0b314
--- /dev/null
+++ b/ui/pages/onboarding-flow/secure-your-wallet/index.scss
@@ -0,0 +1,47 @@
+.secure-your-wallet {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  flex-direction: column;
+  max-width: 1000px;
+  max-height: 1300px;
+
+  &__details {
+    max-width: 550px;
+  }
+
+  &__actions {
+    button {
+      margin: 16px;
+    }
+  }
+
+  &__list {
+    list-style: disc inside;
+
+    li {
+      font-size: 18px;
+    }
+  }
+
+  &__highlighted {
+    background-color: $primary-2;
+    padding: 12px;
+    border-radius: 10px;
+  }
+}
+
+.skip-srp-backup-popover {
+  width: 365px;
+
+  &__checkbox {
+    margin: 8px 12px 0 0;
+  }
+
+  &__footer {
+    button {
+      width: 140px;
+      margin: 0 10px;
+    }
+  }
+}
diff --git a/ui/pages/onboarding-flow/secure-your-wallet/secure-your-wallet.js b/ui/pages/onboarding-flow/secure-your-wallet/secure-your-wallet.js
new file mode 100644
index 000000000..f7c941260
--- /dev/null
+++ b/ui/pages/onboarding-flow/secure-your-wallet/secure-your-wallet.js
@@ -0,0 +1,167 @@
+import React, { useState } from 'react';
+import { useHistory } from 'react-router-dom';
+import { useSelector } from 'react-redux';
+import Box from '../../../components/ui/box';
+import Button from '../../../components/ui/button';
+import Typography from '../../../components/ui/typography';
+import {
+  TEXT_ALIGN,
+  TYPOGRAPHY,
+  JUSTIFY_CONTENT,
+  FONT_WEIGHT,
+} from '../../../helpers/constants/design-system';
+import ProgressBar from '../../../components/app/step-progress-bar';
+import { useI18nContext } from '../../../hooks/useI18nContext';
+import { ONBOARDING_REVIEW_SRP_ROUTE } from '../../../helpers/constants/routes';
+import { getCurrentLocale } from '../../../ducks/metamask/metamask';
+import SkipSRPBackup from './skip-srp-backup-popover';
+
+export default function SecureYourWallet() {
+  const history = useHistory();
+  const t = useI18nContext();
+  const currentLocale = useSelector(getCurrentLocale);
+  const [showSkipSRPBackupPopover, setShowSkipSRPBackupPopover] = useState(
+    false,
+  );
+
+  const handleClickRecommended = () => {
+    history.push(ONBOARDING_REVIEW_SRP_ROUTE);
+  };
+
+  const handleClickNotRecommended = () => {
+    setShowSkipSRPBackupPopover(true);
+  };
+
+  const subtitles = {
+    en: 'English',
+    es: 'Spanish',
+    hi: 'Hindi',
+    id: 'Indonesian',
+    ja: 'Japanese',
+    ko: 'Korean',
+    pt: 'Portuguese',
+    ru: 'Russian',
+    tl: 'Tagalog',
+    vi: 'Vietnamese',
+  };
+
+  const defaultLang = subtitles[currentLocale] ? currentLocale : 'en';
+  return (
+    <div className="secure-your-wallet">
+      {showSkipSRPBackupPopover && (
+        <SkipSRPBackup handleClose={() => setShowSkipSRPBackupPopover(false)} />
+      )}
+      <ProgressBar stage="SEED_PHRASE_VIDEO" />
+      <Box
+        justifyContent={JUSTIFY_CONTENT.CENTER}
+        textAlign={TEXT_ALIGN.CENTER}
+        marginBottom={4}
+      >
+        <Typography variant={TYPOGRAPHY.H2} fontWeight={FONT_WEIGHT.BOLD}>
+          {t('seedPhraseIntroTitle')}
+        </Typography>
+      </Box>
+      <Box
+        justifyContent={JUSTIFY_CONTENT.CENTER}
+        textAlign={TEXT_ALIGN.CENTER}
+        marginBottom={6}
+      >
+        <Typography
+          variant={TYPOGRAPHY.H4}
+          className="secure-your-wallet__details"
+        >
+          {t('seedPhraseIntroTitleCopy')}
+        </Typography>
+      </Box>
+      <Box>
+        <video controls style={{ borderRadius: '10px' }}>
+          <source
+            type="video/webm"
+            src="./images/videos/recovery-onboarding/video.webm"
+          />
+          {Object.keys(subtitles).map((key) => {
+            return (
+              <track
+                default={Boolean(key === defaultLang)}
+                srcLang={key}
+                label={subtitles[key]}
+                key={`${key}-subtitles`}
+                kind="subtitles"
+                src={`./images/videos/recovery-onboarding/subtitles/${key}.vtt`}
+              />
+            );
+          })}
+        </video>
+      </Box>
+      <Box
+        margin={8}
+        width="10/12"
+        justifyContent={JUSTIFY_CONTENT.SPACE_BETWEEN}
+        className="secure-your-wallet__actions"
+      >
+        <Button
+          type="secondary"
+          rounded
+          large
+          onClick={handleClickNotRecommended}
+        >
+          {t('seedPhraseIntroNotRecommendedButtonCopy')}
+        </Button>
+        <Button type="primary" rounded large onClick={handleClickRecommended}>
+          {t('seedPhraseIntroRecommendedButtonCopy')}
+        </Button>
+      </Box>
+      <Box marginBottom={4} textAlign={TEXT_ALIGN.CENTER}>
+        <Typography
+          tag="span"
+          variant={TYPOGRAPHY.H4}
+          fontWeight={FONT_WEIGHT.BOLD}
+          boxProps={{ display: 'block' }}
+        >
+          {t('seedPhraseIntroSidebarTitleOne')}
+        </Typography>
+        <Typography tag="span" variant={TYPOGRAPHY.H4}>
+          {t('seedPhraseIntroSidebarCopyOne')}
+        </Typography>
+      </Box>
+      <Box marginBottom={4} textAlign={TEXT_ALIGN.CENTER}>
+        <Typography
+          tag="span"
+          variant={TYPOGRAPHY.H4}
+          fontWeight={FONT_WEIGHT.BOLD}
+          boxProps={{ display: 'block' }}
+        >
+          {t('seedPhraseIntroSidebarTitleTwo')}
+        </Typography>
+        <ul className="secure-your-wallet__list">
+          <li>{t('seedPhraseIntroSidebarBulletOne')}</li>
+          <li>{t('seedPhraseIntroSidebarBulletTwo')}</li>
+          <li>{t('seedPhraseIntroSidebarBulletThree')}</li>
+          <li>{t('seedPhraseIntroSidebarBulletFour')}</li>
+        </ul>
+      </Box>
+      <Box marginBottom={6} textAlign={TEXT_ALIGN.CENTER}>
+        <Typography
+          tag="span"
+          variant={TYPOGRAPHY.H4}
+          fontWeight={FONT_WEIGHT.BOLD}
+          boxProps={{ display: 'block' }}
+        >
+          {t('seedPhraseIntroSidebarTitleThree')}
+        </Typography>
+        <Typography tag="span" variant={TYPOGRAPHY.H4}>
+          {t('seedPhraseIntroSidebarCopyTwo')}
+        </Typography>
+      </Box>
+      <Box
+        className="secure-your-wallet__highlighted"
+        marginBottom={2}
+        textAlign={TEXT_ALIGN.CENTER}
+      >
+        <Typography tag="span" variant={TYPOGRAPHY.H4}>
+          {t('seedPhraseIntroSidebarCopyThree')}
+        </Typography>
+      </Box>
+    </div>
+  );
+}
diff --git a/ui/pages/onboarding-flow/secure-your-wallet/secure-your-wallet.stories.js b/ui/pages/onboarding-flow/secure-your-wallet/secure-your-wallet.stories.js
new file mode 100644
index 000000000..6951b001c
--- /dev/null
+++ b/ui/pages/onboarding-flow/secure-your-wallet/secure-your-wallet.stories.js
@@ -0,0 +1,15 @@
+import React from 'react';
+import SecureYourWallet from './secure-your-wallet';
+
+export default {
+  title: 'Onboarding - Secure Your Wallet',
+  id: __filename,
+};
+
+export const Base = () => {
+  return (
+    <div style={{ maxHeight: '2000px' }}>
+      <SecureYourWallet />
+    </div>
+  );
+};
diff --git a/ui/pages/onboarding-flow/secure-your-wallet/secure-your-wallet.test.js b/ui/pages/onboarding-flow/secure-your-wallet/secure-your-wallet.test.js
new file mode 100644
index 000000000..15e5db2c0
--- /dev/null
+++ b/ui/pages/onboarding-flow/secure-your-wallet/secure-your-wallet.test.js
@@ -0,0 +1,59 @@
+import React from 'react';
+import { fireEvent } from '@testing-library/react';
+import configureMockStore from 'redux-mock-store';
+import reactRouterDom from 'react-router-dom';
+import { renderWithProvider } from '../../../../test/lib/render-helpers';
+import { ONBOARDING_COMPLETION_ROUTE } from '../../../helpers/constants/routes';
+import SecureYourWallet from './secure-your-wallet';
+
+describe('Secure Your Wallet Onboarding View', () => {
+  const useHistoryOriginal = reactRouterDom.useHistory;
+  const pushMock = jest.fn();
+  beforeAll(() => {
+    jest
+      .spyOn(reactRouterDom, 'useHistory')
+      .mockImplementation()
+      .mockReturnValue({ push: pushMock });
+  });
+
+  afterAll(() => {
+    reactRouterDom.useHistory = useHistoryOriginal;
+  });
+
+  const mockStore = {
+    metamask: {
+      provider: {
+        type: 'test',
+      },
+    },
+  };
+
+  const store = configureMockStore()(mockStore);
+  it('should show a popover asking the user if they want to skip account security if they click "Remind me later"', () => {
+    const { queryAllByText, getByText } = renderWithProvider(
+      <SecureYourWallet />,
+      store,
+    );
+    const remindMeLaterButton = getByText('Remind me later (not recommended)');
+    expect(queryAllByText('Skip Account Security?')).toHaveLength(0);
+    fireEvent.click(remindMeLaterButton);
+    expect(queryAllByText('Skip Account Security?')).toHaveLength(1);
+  });
+
+  it('should not be able to click "skip" until "Skip Account Security" terms are agreed to', () => {
+    const { getByText, getByTestId } = renderWithProvider(
+      <SecureYourWallet />,
+      store,
+    );
+    const remindMeLaterButton = getByText('Remind me later (not recommended)');
+    fireEvent.click(remindMeLaterButton);
+    const skipButton = getByText('Skip');
+    fireEvent.click(skipButton);
+    expect(pushMock).toHaveBeenCalledTimes(0);
+    const checkbox = getByTestId('skip-srp-backup-popover-checkbox');
+    fireEvent.click(checkbox);
+    fireEvent.click(skipButton);
+    expect(pushMock).toHaveBeenCalledTimes(1);
+    expect(pushMock).toHaveBeenCalledWith(ONBOARDING_COMPLETION_ROUTE);
+  });
+});
diff --git a/ui/pages/onboarding-flow/secure-your-wallet/skip-srp-backup-popover.js b/ui/pages/onboarding-flow/secure-your-wallet/skip-srp-backup-popover.js
new file mode 100644
index 000000000..00ceb4558
--- /dev/null
+++ b/ui/pages/onboarding-flow/secure-your-wallet/skip-srp-backup-popover.js
@@ -0,0 +1,79 @@
+import React, { useState } from 'react';
+import PropTypes from 'prop-types';
+import { useHistory } from 'react-router-dom';
+import { useI18nContext } from '../../../hooks/useI18nContext';
+import Button from '../../../components/ui/button';
+import Popover from '../../../components/ui/popover';
+import Box from '../../../components/ui/box';
+import Typography from '../../../components/ui/typography';
+import {
+  ALIGN_ITEMS,
+  FLEX_DIRECTION,
+  FONT_WEIGHT,
+  JUSTIFY_CONTENT,
+  TYPOGRAPHY,
+} from '../../../helpers/constants/design-system';
+import Checkbox from '../../../components/ui/check-box';
+import { ONBOARDING_COMPLETION_ROUTE } from '../../../helpers/constants/routes';
+
+export default function SkipSRPBackup({ handleClose }) {
+  const [checked, setChecked] = useState(false);
+  const t = useI18nContext();
+  const history = useHistory();
+  return (
+    <Popover
+      className="skip-srp-backup-popover"
+      footer={
+        <Box
+          className="skip-srp-backup-popover__footer"
+          justifyContent={JUSTIFY_CONTENT.CENTER}
+          alignItems={ALIGN_ITEMS.CENTER}
+        >
+          <Button onClick={handleClose} type="secondary" rounded>
+            {t('goBack')}
+          </Button>
+          <Button
+            disabled={!checked}
+            type="primary"
+            rounded
+            onClick={() => history.push(ONBOARDING_COMPLETION_ROUTE)}
+          >
+            {t('skip')}
+          </Button>
+        </Box>
+      }
+    >
+      <Box
+        flexDirection={FLEX_DIRECTION.COLUMN}
+        alignItems={ALIGN_ITEMS.CENTER}
+        justifyContent={JUSTIFY_CONTENT.CENTER}
+        margin={4}
+      >
+        <img src="./images/warning-icon.png" />
+        <Typography variant={TYPOGRAPHY.h3} fontWeight={FONT_WEIGHT.BOLD}>
+          {t('skipAccountSecurity')}
+        </Typography>
+        <Box justifyContent={JUSTIFY_CONTENT.CENTER} margin={3}>
+          <Checkbox
+            className="skip-srp-backup-popover__checkbox"
+            onClick={() => {
+              setChecked(!checked);
+            }}
+            checked={checked}
+            dataTestId="skip-srp-backup-popover-checkbox"
+          />
+          <Typography
+            className="skip-srp-backup-popover__details"
+            variant={TYPOGRAPHY.h7}
+          >
+            {t('skipAccountSecurityDetails')}
+          </Typography>
+        </Box>
+      </Box>
+    </Popover>
+  );
+}
+
+SkipSRPBackup.propTypes = {
+  handleClose: PropTypes.func,
+};
diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js
index a6008025e..23515aa89 100644
--- a/ui/pages/routes/routes.component.js
+++ b/ui/pages/routes/routes.component.js
@@ -40,7 +40,6 @@ import {
   CONFIRM_TRANSACTION_ROUTE,
   CONNECT_ROUTE,
   DEFAULT_ROUTE,
-  INITIALIZE_ROUTE,
   INITIALIZE_UNLOCK_ROUTE,
   LOCK_ROUTE,
   MOBILE_SYNC_ROUTE,
@@ -54,6 +53,7 @@ import {
   BUILD_QUOTE_ROUTE,
   CONFIRMATION_V_NEXT_ROUTE,
   CONFIRM_IMPORT_TOKEN_ROUTE,
+  INITIALIZE_ROUTE,
   ONBOARDING_ROUTE,
 } from '../../helpers/constants/routes';
 

From 7a5b48e01845d2bd4df597acf0464af5bed3f4a9 Mon Sep 17 00:00:00 2001
From: Mark Stacey <markjstacey@gmail.com>
Date: Fri, 8 Oct 2021 11:50:07 -0230
Subject: [PATCH 48/56] Update `immer` from v8 to v9 (#12303)

`immer` has been updated to v9. This didn't require any changes on our
part; the only breaking changes are to the TypeScript types [1].

The `@reduxjs/toolkit` library has been updated as well, to ensure that
it's using the updated version of Immer internally as well. This update
makes our patch of that package obsolete, as the problematic pattern
that were were patching out is no longer present.

[1]: https://github.com/immerjs/immer/releases/tag/v9.0.0
---
 package.json                         |  6 +-
 patches/@reduxjs+toolkit+1.5.0.patch | 82 ----------------------------
 yarn.lock                            | 38 +++++--------
 3 files changed, 17 insertions(+), 109 deletions(-)
 delete mode 100644 patches/@reduxjs+toolkit+1.5.0.patch

diff --git a/package.json b/package.json
index 6b0e1e337..e1577d3cb 100644
--- a/package.json
+++ b/package.json
@@ -88,7 +88,7 @@
     "3box/ipfs/prometheus-gc-stats/gc-stats/node-pre-gyp/tar": "^6.1.2",
     "3box/**/libp2p-crypto/node-forge": "^0.10.0",
     "3box/**/libp2p-keychain/node-forge": "^0.10.0",
-    "analytics-node/axios": "^0.21.1",
+    "analytics-node/axios": "^0.21.2",
     "ganache-core/lodash": "^4.17.21",
     "netmask": "^2.0.1",
     "pubnub/superagent-proxy": "^3.0.0",
@@ -116,7 +116,7 @@
     "@metamask/post-message-stream": "^4.0.0",
     "@metamask/providers": "^8.1.1",
     "@popperjs/core": "^2.4.0",
-    "@reduxjs/toolkit": "^1.5.0",
+    "@reduxjs/toolkit": "^1.6.2",
     "@sentry/browser": "^5.26.0",
     "@sentry/integrations": "^5.26.0",
     "@zxing/library": "^0.8.0",
@@ -159,7 +159,7 @@
     "fuse.js": "^3.2.0",
     "globalthis": "^1.0.1",
     "human-standard-token-abi": "^2.0.0",
-    "immer": "^8.0.1",
+    "immer": "^9.0.6",
     "json-rpc-engine": "^6.1.0",
     "json-rpc-middleware-stream": "^2.1.1",
     "jsonschema": "^1.2.4",
diff --git a/patches/@reduxjs+toolkit+1.5.0.patch b/patches/@reduxjs+toolkit+1.5.0.patch
deleted file mode 100644
index ae707ae5e..000000000
--- a/patches/@reduxjs+toolkit+1.5.0.patch
+++ /dev/null
@@ -1,82 +0,0 @@
-diff --git a/node_modules/@reduxjs/toolkit/dist/redux-toolkit.cjs.development.js b/node_modules/@reduxjs/toolkit/dist/redux-toolkit.cjs.development.js
-index 96ead94..ce7bd95 100644
---- a/node_modules/@reduxjs/toolkit/dist/redux-toolkit.cjs.development.js
-+++ b/node_modules/@reduxjs/toolkit/dist/redux-toolkit.cjs.development.js
-@@ -204,7 +204,7 @@ function (_Array) {
- 
-   var _proto = MiddlewareArray.prototype;
- 
--  _proto.concat = function concat() {
-+  Object.defineProperty(_proto, 'concat', { value: function concat() {
-     var _Array$prototype$conc;
- 
-     for (var _len = arguments.length, arr = new Array(_len), _key = 0; _key < _len; _key++) {
-@@ -212,7 +212,7 @@ function (_Array) {
-     }
- 
-     return _construct(MiddlewareArray, (_Array$prototype$conc = _Array.prototype.concat).call.apply(_Array$prototype$conc, [this].concat(arr)));
--  };
-+  } })
- 
-   _proto.prepend = function prepend() {
-     for (var _len2 = arguments.length, arr = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
-diff --git a/node_modules/@reduxjs/toolkit/dist/redux-toolkit.cjs.production.min.js b/node_modules/@reduxjs/toolkit/dist/redux-toolkit.cjs.production.min.js
-index eb2dd1c..d44d517 100644
---- a/node_modules/@reduxjs/toolkit/dist/redux-toolkit.cjs.production.min.js
-+++ b/node_modules/@reduxjs/toolkit/dist/redux-toolkit.cjs.production.min.js
-@@ -1,2 +1,2 @@
--"use strict";function e(e){return e&&"object"==typeof e&&"default"in e?e.default:e}var t=require("immer"),r=e(t),n=require("redux"),o=require("reselect"),i=e(require("redux-thunk")),u=function(){var e=o.createSelector.apply(void 0,arguments),r=function(r){for(var n=arguments.length,o=new Array(n>1?n-1:0),i=1;i<n;i++)o[i-1]=arguments[i];return e.apply(void 0,[t.isDraft(r)?t.current(r):r].concat(o))};return r};function a(){return(a=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e}).apply(this,arguments)}function c(e){return(c=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function f(e,t){return(f=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function s(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(e){return!1}}function l(e,t,r){return(l=s()?Reflect.construct:function(e,t,r){var n=[null];n.push.apply(n,t);var o=new(Function.bind.apply(e,n));return r&&f(o,r.prototype),o}).apply(null,arguments)}function d(e){var t="function"==typeof Map?new Map:void 0;return(d=function(e){if(null===e||-1===Function.toString.call(e).indexOf("[native code]"))return e;if("function"!=typeof e)throw new TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,r)}function r(){return l(e,arguments,c(this).constructor)}return r.prototype=Object.create(e.prototype,{constructor:{value:r,enumerable:!1,writable:!0,configurable:!0}}),f(r,e)})(e)}var p="undefined"!=typeof window&&window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__?window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__:function(){if(0!==arguments.length)return"object"==typeof arguments[0]?n.compose:n.compose.apply(null,arguments)};function y(e){if("object"!=typeof e||null===e)return!1;for(var t=e;null!==Object.getPrototypeOf(t);)t=Object.getPrototypeOf(t);return Object.getPrototypeOf(e)===t}var v=function(e){var t,r;function n(){return e.apply(this,arguments)||this}r=e,(t=n).prototype=Object.create(r.prototype),t.prototype.constructor=t,t.__proto__=r;var o=n.prototype;return o.concat=function(){for(var t,r=arguments.length,o=new Array(r),i=0;i<r;i++)o[i]=arguments[i];return l(n,(t=e.prototype.concat).call.apply(t,[this].concat(o)))},o.prepend=function(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return 1===t.length&&Array.isArray(t[0])?l(n,t[0].concat(this)):l(n,t.concat(this))},n}(d(Array));function h(e){return null==e||"string"==typeof e||"boolean"==typeof e||"number"==typeof e||Array.isArray(e)||y(e)}function b(e){void 0===e&&(e={});var t=e.thunk,r=void 0===t||t,n=new v;return r&&n.push("boolean"==typeof r?i:i.withExtraArgument(r.extraArgument)),n}function m(e,t){function r(){if(t){var r=t.apply(void 0,arguments);if(!r)throw new Error("prepareAction did not return an object");return a({type:e,payload:r.payload},"meta"in r&&{meta:r.meta},{},"error"in r&&{error:r.error})}return{type:e,payload:arguments.length<=0?void 0:arguments[0]}}return r.toString=function(){return""+e},r.type=e,r.match=function(t){return t.type===e},r}function g(e){return["type","payload","error","meta"].indexOf(e)>-1}function O(e){var t,r={},n=[],o={addCase:function(e,t){var n="string"==typeof e?e:e.type;if(n in r)throw new Error("addCase cannot be called with two reducers for the same action type");return r[n]=t,o},addMatcher:function(e,t){return n.push({matcher:e,reducer:t}),o},addDefaultCase:function(e){return t=e,o}};return e(o),[r,n,t]}function j(e,n,o,i){void 0===o&&(o=[]);var u="function"==typeof n?O(n):[n,o,i],a=u[0],c=u[1],f=u[2];return function(n,o){void 0===n&&(n=e);var i=[a[o.type]].concat(c.filter((function(e){return(0,e.matcher)(o)})).map((function(e){return e.reducer})));return 0===i.filter((function(e){return!!e})).length&&(i=[f]),i.reduce((function(e,n){if(n){if(t.isDraft(e)){var i=n(e,o);return void 0===i?e:i}if(t.isDraftable(e))return r(e,(function(e){return n(e,o)}));var u=n(e,o);if(void 0===u){if(null===e)return e;throw Error("A case reducer on a non-draftable value must not return undefined")}return u}return e}),n)}}function A(e){return function(n,o){var i=function(t){!function(e){return y(t=e)&&"string"==typeof t.type&&Object.keys(t).every(g);var t}(o)?e(o,t):e(o.payload,t)};return t.isDraft(n)?(i(n),n):r(n,i)}}function x(e,t){return t(e)}function S(e){function t(t,r){var n=x(t,e);n in r.entities||(r.ids.push(n),r.entities[n]=t)}function r(e,r){Array.isArray(e)||(e=Object.values(e));var n=e,o=Array.isArray(n),i=0;for(n=o?n:n[Symbol.iterator]();;){var u;if(o){if(i>=n.length)break;u=n[i++]}else{if((i=n.next()).done)break;u=i.value}t(u,r)}}function n(e,t){var r=!1;e.forEach((function(e){e in t.entities&&(delete t.entities[e],r=!0)})),r&&(t.ids=t.ids.filter((function(e){return e in t.entities})))}function o(t,r){var n={},o={};t.forEach((function(e){e.id in r.entities&&(o[e.id]={id:e.id,changes:a({},o[e.id]?o[e.id].changes:null,{},e.changes)})})),(t=Object.values(o)).length>0&&t.filter((function(t){return function(t,r,n){var o=Object.assign({},n.entities[r.id],r.changes),i=x(o,e),u=i!==r.id;return u&&(t[r.id]=i,delete n.entities[r.id]),n.entities[i]=o,u}(n,t,r)})).length>0&&(r.ids=r.ids.map((function(e){return n[e]||e})))}function i(t,n){Array.isArray(t)||(t=Object.values(t));var i=[],u=[],a=t,c=Array.isArray(a),f=0;for(a=c?a:a[Symbol.iterator]();;){var s;if(c){if(f>=a.length)break;s=a[f++]}else{if((f=a.next()).done)break;s=f.value}var l=s,d=x(l,e);d in n.entities?u.push({id:d,changes:l}):i.push(l)}o(u,n),r(i,n)}return{removeAll:(u=function(e){Object.assign(e,{ids:[],entities:{}})},c=A((function(e,t){return u(t)})),function(e){return c(e,void 0)}),addOne:A(t),addMany:A(r),setAll:A((function(e,t){Array.isArray(e)||(e=Object.values(e)),t.ids=[],t.entities={},r(e,t)})),updateOne:A((function(e,t){return o([e],t)})),updateMany:A(o),upsertOne:A((function(e,t){return i([e],t)})),upsertMany:A(i),removeOne:A((function(e,t){return n([e],t)})),removeMany:A(n)};var u,c}"undefined"!=typeof Symbol&&(Symbol.iterator||(Symbol.iterator=Symbol("Symbol.iterator"))),"undefined"!=typeof Symbol&&(Symbol.asyncIterator||(Symbol.asyncIterator=Symbol("Symbol.asyncIterator")));var w=function(e){void 0===e&&(e=21);for(var t="",r=e;r--;)t+="ModuleSymbhasOwnPr-0123456789ABCDEFGHNRVfgctiUvz_KqYTJkLxpZXIjQW"[64*Math.random()|0];return t},E=["name","message","stack","code"],P=function(e){this.payload=e,this.name="RejectWithValue",this.message="Rejected"},_=function(e){if("object"==typeof e&&null!==e){var t={},r=E,n=Array.isArray(r),o=0;for(r=n?r:r[Symbol.iterator]();;){var i;if(n){if(o>=r.length)break;i=r[o++]}else{if((o=r.next()).done)break;i=o.value}"string"==typeof e[i]&&(t[i]=e[i])}return t}return{message:String(e)}},k=function(e,t){return(r=e)&&"function"==typeof r.match?e.match(t):e(t);var r};function I(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return function(e){return t.some((function(t){return k(t,e)}))}}function R(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return function(e){return t.every((function(t){return k(t,e)}))}}function M(e,t){if(!e||!e.meta)return!1;var r="string"==typeof e.meta.requestId,n=t.indexOf(e.meta.requestStatus)>-1;return r&&n}function q(e){return"function"==typeof e[0]&&"pending"in e[0]&&"fulfilled"in e[0]&&"rejected"in e[0]}function D(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return 0===t.length?function(e){return M(e,["rejected"])}:q(t)?function(e){var r=t.map((function(e){return e.rejected}));return I.apply(void 0,r)(e)}:D()(t[0])}t.enableES5(),Object.keys(n).forEach((function(e){"default"!==e&&Object.defineProperty(exports,e,{enumerable:!0,get:function(){return n[e]}})})),exports.createNextState=r,Object.defineProperty(exports,"current",{enumerable:!0,get:function(){return t.current}}),Object.defineProperty(exports,"freeze",{enumerable:!0,get:function(){return t.freeze}}),Object.defineProperty(exports,"createSelector",{enumerable:!0,get:function(){return o.createSelector}}),exports.MiddlewareArray=v,exports.configureStore=function(e){var t,r=function(e){return b(e)},o=e||{},i=o.reducer,u=void 0===i?void 0:i,c=o.middleware,f=void 0===c?r():c,s=o.devTools,l=void 0===s||s,d=o.preloadedState,v=void 0===d?void 0:d,h=o.enhancers,m=void 0===h?void 0:h;if("function"==typeof u)t=u;else{if(!y(u))throw new Error('"reducer" is a required argument, and must be a function or an object of functions that can be passed to combineReducers');t=n.combineReducers(u)}var g=n.applyMiddleware.apply(void 0,"function"==typeof f?f(r):f),O=n.compose;l&&(O=p(a({trace:!1},"object"==typeof l&&l)));var j=[g];Array.isArray(m)?j=[g].concat(m):"function"==typeof m&&(j=m(j));var A=O.apply(void 0,j);return n.createStore(t,v,A)},exports.createAction=m,exports.createAsyncThunk=function(e,t,r){var n=m(e+"/fulfilled",(function(e,t,r){return{payload:e,meta:{arg:r,requestId:t,requestStatus:"fulfilled"}}})),o=m(e+"/pending",(function(e,t){return{payload:void 0,meta:{arg:t,requestId:e,requestStatus:"pending"}}})),i=m(e+"/rejected",(function(e,t,n){var o=e instanceof P,i=!!e&&"AbortError"===e.name,u=!!e&&"ConditionError"===e.name;return{payload:e instanceof P?e.payload:void 0,error:(r&&r.serializeError||_)(e||"Rejected"),meta:{arg:n,requestId:t,rejectedWithValue:o,requestStatus:"rejected",aborted:i,condition:u}}})),u="undefined"!=typeof AbortController?AbortController:function(){function e(){this.signal={aborted:!1,addEventListener:function(){},dispatchEvent:function(){return!1},onabort:function(){},removeEventListener:function(){}}}return e.prototype.abort=function(){},e}();return Object.assign((function(e){return function(a,c,f){var s,l=w(),d=new u,p=new Promise((function(e,t){return d.signal.addEventListener("abort",(function(){return t({name:"AbortError",message:s||"Aborted"})}))})),y=!1,v=function(){try{var u,s=function(e){return v?e:(r&&!r.dispatchConditionRejection&&i.match(u)&&u.meta.condition||a(u),u)},v=!1,h=function(s,v){try{var h=function(){if(r&&r.condition&&!1===r.condition(e,{getState:c,extra:f}))throw{name:"ConditionError",message:"Aborted due to condition callback returning false."};return y=!0,a(o(l,e)),Promise.resolve(Promise.race([p,Promise.resolve(t(e,{dispatch:a,getState:c,extra:f,requestId:l,signal:d.signal,rejectWithValue:function(e){return new P(e)}})).then((function(t){return t instanceof P?i(t,l,e):n(t,l,e)}))])).then((function(e){u=e}))}()}catch(e){return v(e)}return h&&h.then?h.then(void 0,v):h}(0,(function(t){u=i(t,l,e)}));return Promise.resolve(h&&h.then?h.then(s):s(h))}catch(e){return Promise.reject(e)}}();return Object.assign(v,{abort:function(e){y&&(s=e,d.abort())},requestId:l,arg:e})}}),{pending:o,rejected:i,fulfilled:n,typePrefix:e})},exports.createDraftSafeSelector=u,exports.createEntityAdapter=function(e){void 0===e&&(e={});var t=a({sortComparer:!1,selectId:function(e){return e.id}},e),r=t.selectId,n=t.sortComparer;return a({selectId:r,sortComparer:n},{getInitialState:function(e){return void 0===e&&(e={}),Object.assign({ids:[],entities:{}},e)}},{},{getSelectors:function(e){var t=function(e){return e.ids},r=function(e){return e.entities},n=u(t,r,(function(e,t){return e.map((function(e){return t[e]}))})),o=function(e,t){return t},i=function(e,t){return e[t]},a=u(t,(function(e){return e.length}));if(!e)return{selectIds:t,selectEntities:r,selectAll:n,selectTotal:a,selectById:u(r,o,i)};var c=u(e,r);return{selectIds:u(e,t),selectEntities:c,selectAll:u(e,n),selectTotal:u(e,a),selectById:u(c,o,i)}}},{},n?function(e,t){var r=S(e);function n(t,r){Array.isArray(t)||(t=Object.values(t));var n=t.filter((function(t){return!(x(t,e)in r.entities)}));0!==n.length&&u(n,r)}function o(t,r){var n=[];t.forEach((function(t){return function(t,r,n){if(!(r.id in n.entities))return!1;var o=Object.assign({},n.entities[r.id],r.changes),i=x(o,e);return delete n.entities[r.id],t.push(o),i!==r.id}(n,t,r)})),0!==n.length&&u(n,r)}function i(t,r){Array.isArray(t)||(t=Object.values(t));var i=[],u=[],a=t,c=Array.isArray(a),f=0;for(a=c?a:a[Symbol.iterator]();;){var s;if(c){if(f>=a.length)break;s=a[f++]}else{if((f=a.next()).done)break;s=f.value}var l=s,d=x(l,e);d in r.entities?u.push({id:d,changes:l}):i.push(l)}o(u,r),n(i,r)}function u(r,n){r.sort(t),r.forEach((function(t){n.entities[e(t)]=t}));var o=Object.values(n.entities);o.sort(t);var i=o.map(e);(function(e,t){if(e.length!==t.length)return!1;for(var r=0;r<e.length&&r<t.length;r++)if(e[r]!==t[r])return!1;return!0})(n.ids,i)||(n.ids=i)}return{removeOne:r.removeOne,removeMany:r.removeMany,removeAll:r.removeAll,addOne:A((function(e,t){return n([e],t)})),updateOne:A((function(e,t){return o([e],t)})),upsertOne:A((function(e,t){return i([e],t)})),setAll:A((function(e,t){Array.isArray(e)||(e=Object.values(e)),t.entities={},t.ids=[],n(e,t)})),addMany:A(n),updateMany:A(o),upsertMany:A(i)}}(r,n):S(r))},exports.createImmutableStateInvariantMiddleware=function(e){return function(){return function(e){return function(t){return e(t)}}}},exports.createReducer=j,exports.createSerializableStateInvariantMiddleware=function(e){return function(){return function(e){return function(t){return e(t)}}}},exports.createSlice=function(e){var t=e.name,r=e.initialState;if(!t)throw new Error("`name` is a required option for createSlice");var n=e.reducers||{},o=void 0===e.extraReducers?[]:"function"==typeof e.extraReducers?O(e.extraReducers):[e.extraReducers],i=o[0],u=void 0===i?{}:i,c=o[1],f=void 0===c?[]:c,s=o[2],l=void 0===s?void 0:s,d=Object.keys(n),p={},y={},v={};d.forEach((function(e){var r,o,i=n[e],u=t+"/"+e;"reducer"in i?(r=i.reducer,o=i.prepare):r=i,p[e]=r,y[u]=r,v[e]=o?m(u,o):m(u)}));var h=j(r,a({},u,{},y),f,l);return{name:t,reducer:h,actions:v,caseReducers:p}},exports.findNonSerializableValue=function e(t,r,n,o,i){var u;if(void 0===r&&(r=[]),void 0===n&&(n=h),void 0===i&&(i=[]),!n(t))return{keyPath:r.join(".")||"<root>",value:t};if("object"!=typeof t||null===t)return!1;var a=null!=o?o(t):Object.entries(t),c=i.length>0,f=a,s=Array.isArray(f),l=0;for(f=s?f:f[Symbol.iterator]();;){var d;if(s){if(l>=f.length)break;d=f[l++]}else{if((l=f.next()).done)break;d=l.value}var p=d[1],y=r.concat(d[0]);if(!(c&&i.indexOf(y.join("."))>=0)){if(!n(p))return{keyPath:y.join("."),value:p};if("object"==typeof p&&(u=e(p,y,n,o,i)))return u}}return!1},exports.getDefaultMiddleware=b,exports.getType=function(e){return""+e},exports.isAllOf=R,exports.isAnyOf=I,exports.isAsyncThunkAction=function e(){for(var t=arguments.length,r=new Array(t),n=0;n<t;n++)r[n]=arguments[n];return 0===r.length?function(e){return M(e,["pending","fulfilled","rejected"])}:q(r)?function(e){var t=[],n=r,o=Array.isArray(n),i=0;for(n=o?n:n[Symbol.iterator]();;){var u;if(o){if(i>=n.length)break;u=n[i++]}else{if((i=n.next()).done)break;u=i.value}t.push(u.pending,u.rejected,u.fulfilled)}return I.apply(void 0,t)(e)}:e()(r[0])},exports.isFulfilled=function e(){for(var t=arguments.length,r=new Array(t),n=0;n<t;n++)r[n]=arguments[n];return 0===r.length?function(e){return M(e,["fulfilled"])}:q(r)?function(e){var t=r.map((function(e){return e.fulfilled}));return I.apply(void 0,t)(e)}:e()(r[0])},exports.isImmutableDefault=function(e){return"object"!=typeof e||null==e},exports.isPending=function e(){for(var t=arguments.length,r=new Array(t),n=0;n<t;n++)r[n]=arguments[n];return 0===r.length?function(e){return M(e,["pending"])}:q(r)?function(e){var t=r.map((function(e){return e.pending}));return I.apply(void 0,t)(e)}:e()(r[0])},exports.isPlain=h,exports.isPlainObject=y,exports.isRejected=D,exports.isRejectedWithValue=function e(){for(var t=arguments.length,r=new Array(t),n=0;n<t;n++)r[n]=arguments[n];var o=function(e){return e&&e.meta&&e.meta.rejectedWithValue};return 0===r.length?function(e){return R(D.apply(void 0,r),o)(e)}:q(r)?function(e){return R(D.apply(void 0,r),o)(e)}:e()(r[0])},exports.nanoid=w,exports.unwrapResult=function(e){if(e.meta&&e.meta.rejectedWithValue)throw e.payload;if(e.error)throw e.error;return e.payload};
-+"use strict";function e(e){return e&&"object"==typeof e&&"default"in e?e.default:e}var t=require("immer"),r=e(t),n=require("redux"),o=require("reselect"),i=e(require("redux-thunk")),u=function(){var e=o.createSelector.apply(void 0,arguments),r=function(r){for(var n=arguments.length,o=new Array(n>1?n-1:0),i=1;i<n;i++)o[i-1]=arguments[i];return e.apply(void 0,[t.isDraft(r)?t.current(r):r].concat(o))};return r};function a(){return(a=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e}).apply(this,arguments)}function c(e){return(c=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function f(e,t){return(f=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function s(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(e){return!1}}function l(e,t,r){return(l=s()?Reflect.construct:function(e,t,r){var n=[null];n.push.apply(n,t);var o=new(Function.bind.apply(e,n));return r&&f(o,r.prototype),o}).apply(null,arguments)}function d(e){var t="function"==typeof Map?new Map:void 0;return(d=function(e){if(null===e||-1===Function.toString.call(e).indexOf("[native code]"))return e;if("function"!=typeof e)throw new TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,r)}function r(){return l(e,arguments,c(this).constructor)}return r.prototype=Object.create(e.prototype,{constructor:{value:r,enumerable:!1,writable:!0,configurable:!0}}),f(r,e)})(e)}var p="undefined"!=typeof window&&window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__?window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__:function(){if(0!==arguments.length)return"object"==typeof arguments[0]?n.compose:n.compose.apply(null,arguments)};function y(e){if("object"!=typeof e||null===e)return!1;for(var t=e;null!==Object.getPrototypeOf(t);)t=Object.getPrototypeOf(t);return Object.getPrototypeOf(e)===t}var v=function(e){var t,r;function n(){return e.apply(this,arguments)||this}r=e,(t=n).prototype=Object.create(r.prototype),t.prototype.constructor=t,t.__proto__=r;var o=n.prototype;return Object.defineProperty(o,'concat',{value:function(){for(var t,r=arguments.length,o=new Array(r),i=0;i<r;i++)o[i]=arguments[i];return l(n,(t=e.prototype.concat).call.apply(t,[this].concat(o)))}}),o.prepend=function(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return 1===t.length&&Array.isArray(t[0])?l(n,t[0].concat(this)):l(n,t.concat(this))},n}(d(Array));function h(e){return null==e||"string"==typeof e||"boolean"==typeof e||"number"==typeof e||Array.isArray(e)||y(e)}function b(e){void 0===e&&(e={});var t=e.thunk,r=void 0===t||t,n=new v;return r&&n.push("boolean"==typeof r?i:i.withExtraArgument(r.extraArgument)),n}function m(e,t){function r(){if(t){var r=t.apply(void 0,arguments);if(!r)throw new Error("prepareAction did not return an object");return a({type:e,payload:r.payload},"meta"in r&&{meta:r.meta},{},"error"in r&&{error:r.error})}return{type:e,payload:arguments.length<=0?void 0:arguments[0]}}return r.toString=function(){return""+e},r.type=e,r.match=function(t){return t.type===e},r}function g(e){return["type","payload","error","meta"].indexOf(e)>-1}function O(e){var t,r={},n=[],o={addCase:function(e,t){var n="string"==typeof e?e:e.type;if(n in r)throw new Error("addCase cannot be called with two reducers for the same action type");return r[n]=t,o},addMatcher:function(e,t){return n.push({matcher:e,reducer:t}),o},addDefaultCase:function(e){return t=e,o}};return e(o),[r,n,t]}function j(e,n,o,i){void 0===o&&(o=[]);var u="function"==typeof n?O(n):[n,o,i],a=u[0],c=u[1],f=u[2];return function(n,o){void 0===n&&(n=e);var i=[a[o.type]].concat(c.filter((function(e){return(0,e.matcher)(o)})).map((function(e){return e.reducer})));return 0===i.filter((function(e){return!!e})).length&&(i=[f]),i.reduce((function(e,n){if(n){if(t.isDraft(e)){var i=n(e,o);return void 0===i?e:i}if(t.isDraftable(e))return r(e,(function(e){return n(e,o)}));var u=n(e,o);if(void 0===u){if(null===e)return e;throw Error("A case reducer on a non-draftable value must not return undefined")}return u}return e}),n)}}function A(e){return function(n,o){var i=function(t){!function(e){return y(t=e)&&"string"==typeof t.type&&Object.keys(t).every(g);var t}(o)?e(o,t):e(o.payload,t)};return t.isDraft(n)?(i(n),n):r(n,i)}}function x(e,t){return t(e)}function S(e){function t(t,r){var n=x(t,e);n in r.entities||(r.ids.push(n),r.entities[n]=t)}function r(e,r){Array.isArray(e)||(e=Object.values(e));var n=e,o=Array.isArray(n),i=0;for(n=o?n:n[Symbol.iterator]();;){var u;if(o){if(i>=n.length)break;u=n[i++]}else{if((i=n.next()).done)break;u=i.value}t(u,r)}}function n(e,t){var r=!1;e.forEach((function(e){e in t.entities&&(delete t.entities[e],r=!0)})),r&&(t.ids=t.ids.filter((function(e){return e in t.entities})))}function o(t,r){var n={},o={};t.forEach((function(e){e.id in r.entities&&(o[e.id]={id:e.id,changes:a({},o[e.id]?o[e.id].changes:null,{},e.changes)})})),(t=Object.values(o)).length>0&&t.filter((function(t){return function(t,r,n){var o=Object.assign({},n.entities[r.id],r.changes),i=x(o,e),u=i!==r.id;return u&&(t[r.id]=i,delete n.entities[r.id]),n.entities[i]=o,u}(n,t,r)})).length>0&&(r.ids=r.ids.map((function(e){return n[e]||e})))}function i(t,n){Array.isArray(t)||(t=Object.values(t));var i=[],u=[],a=t,c=Array.isArray(a),f=0;for(a=c?a:a[Symbol.iterator]();;){var s;if(c){if(f>=a.length)break;s=a[f++]}else{if((f=a.next()).done)break;s=f.value}var l=s,d=x(l,e);d in n.entities?u.push({id:d,changes:l}):i.push(l)}o(u,n),r(i,n)}return{removeAll:(u=function(e){Object.assign(e,{ids:[],entities:{}})},c=A((function(e,t){return u(t)})),function(e){return c(e,void 0)}),addOne:A(t),addMany:A(r),setAll:A((function(e,t){Array.isArray(e)||(e=Object.values(e)),t.ids=[],t.entities={},r(e,t)})),updateOne:A((function(e,t){return o([e],t)})),updateMany:A(o),upsertOne:A((function(e,t){return i([e],t)})),upsertMany:A(i),removeOne:A((function(e,t){return n([e],t)})),removeMany:A(n)};var u,c}"undefined"!=typeof Symbol&&(Symbol.iterator||(Symbol.iterator=Symbol("Symbol.iterator"))),"undefined"!=typeof Symbol&&(Symbol.asyncIterator||(Symbol.asyncIterator=Symbol("Symbol.asyncIterator")));var w=function(e){void 0===e&&(e=21);for(var t="",r=e;r--;)t+="ModuleSymbhasOwnPr-0123456789ABCDEFGHNRVfgctiUvz_KqYTJkLxpZXIjQW"[64*Math.random()|0];return t},E=["name","message","stack","code"],P=function(e){this.payload=e,this.name="RejectWithValue",this.message="Rejected"},_=function(e){if("object"==typeof e&&null!==e){var t={},r=E,n=Array.isArray(r),o=0;for(r=n?r:r[Symbol.iterator]();;){var i;if(n){if(o>=r.length)break;i=r[o++]}else{if((o=r.next()).done)break;i=o.value}"string"==typeof e[i]&&(t[i]=e[i])}return t}return{message:String(e)}},k=function(e,t){return(r=e)&&"function"==typeof r.match?e.match(t):e(t);var r};function I(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return function(e){return t.some((function(t){return k(t,e)}))}}function R(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return function(e){return t.every((function(t){return k(t,e)}))}}function M(e,t){if(!e||!e.meta)return!1;var r="string"==typeof e.meta.requestId,n=t.indexOf(e.meta.requestStatus)>-1;return r&&n}function q(e){return"function"==typeof e[0]&&"pending"in e[0]&&"fulfilled"in e[0]&&"rejected"in e[0]}function D(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return 0===t.length?function(e){return M(e,["rejected"])}:q(t)?function(e){var r=t.map((function(e){return e.rejected}));return I.apply(void 0,r)(e)}:D()(t[0])}t.enableES5(),Object.keys(n).forEach((function(e){"default"!==e&&Object.defineProperty(exports,e,{enumerable:!0,get:function(){return n[e]}})})),exports.createNextState=r,Object.defineProperty(exports,"current",{enumerable:!0,get:function(){return t.current}}),Object.defineProperty(exports,"freeze",{enumerable:!0,get:function(){return t.freeze}}),Object.defineProperty(exports,"createSelector",{enumerable:!0,get:function(){return o.createSelector}}),exports.MiddlewareArray=v,exports.configureStore=function(e){var t,r=function(e){return b(e)},o=e||{},i=o.reducer,u=void 0===i?void 0:i,c=o.middleware,f=void 0===c?r():c,s=o.devTools,l=void 0===s||s,d=o.preloadedState,v=void 0===d?void 0:d,h=o.enhancers,m=void 0===h?void 0:h;if("function"==typeof u)t=u;else{if(!y(u))throw new Error('"reducer" is a required argument, and must be a function or an object of functions that can be passed to combineReducers');t=n.combineReducers(u)}var g=n.applyMiddleware.apply(void 0,"function"==typeof f?f(r):f),O=n.compose;l&&(O=p(a({trace:!1},"object"==typeof l&&l)));var j=[g];Array.isArray(m)?j=[g].concat(m):"function"==typeof m&&(j=m(j));var A=O.apply(void 0,j);return n.createStore(t,v,A)},exports.createAction=m,exports.createAsyncThunk=function(e,t,r){var n=m(e+"/fulfilled",(function(e,t,r){return{payload:e,meta:{arg:r,requestId:t,requestStatus:"fulfilled"}}})),o=m(e+"/pending",(function(e,t){return{payload:void 0,meta:{arg:t,requestId:e,requestStatus:"pending"}}})),i=m(e+"/rejected",(function(e,t,n){var o=e instanceof P,i=!!e&&"AbortError"===e.name,u=!!e&&"ConditionError"===e.name;return{payload:e instanceof P?e.payload:void 0,error:(r&&r.serializeError||_)(e||"Rejected"),meta:{arg:n,requestId:t,rejectedWithValue:o,requestStatus:"rejected",aborted:i,condition:u}}})),u="undefined"!=typeof AbortController?AbortController:function(){function e(){this.signal={aborted:!1,addEventListener:function(){},dispatchEvent:function(){return!1},onabort:function(){},removeEventListener:function(){}}}return e.prototype.abort=function(){},e}();return Object.assign((function(e){return function(a,c,f){var s,l=w(),d=new u,p=new Promise((function(e,t){return d.signal.addEventListener("abort",(function(){return t({name:"AbortError",message:s||"Aborted"})}))})),y=!1,v=function(){try{var u,s=function(e){return v?e:(r&&!r.dispatchConditionRejection&&i.match(u)&&u.meta.condition||a(u),u)},v=!1,h=function(s,v){try{var h=function(){if(r&&r.condition&&!1===r.condition(e,{getState:c,extra:f}))throw{name:"ConditionError",message:"Aborted due to condition callback returning false."};return y=!0,a(o(l,e)),Promise.resolve(Promise.race([p,Promise.resolve(t(e,{dispatch:a,getState:c,extra:f,requestId:l,signal:d.signal,rejectWithValue:function(e){return new P(e)}})).then((function(t){return t instanceof P?i(t,l,e):n(t,l,e)}))])).then((function(e){u=e}))}()}catch(e){return v(e)}return h&&h.then?h.then(void 0,v):h}(0,(function(t){u=i(t,l,e)}));return Promise.resolve(h&&h.then?h.then(s):s(h))}catch(e){return Promise.reject(e)}}();return Object.assign(v,{abort:function(e){y&&(s=e,d.abort())},requestId:l,arg:e})}}),{pending:o,rejected:i,fulfilled:n,typePrefix:e})},exports.createDraftSafeSelector=u,exports.createEntityAdapter=function(e){void 0===e&&(e={});var t=a({sortComparer:!1,selectId:function(e){return e.id}},e),r=t.selectId,n=t.sortComparer;return a({selectId:r,sortComparer:n},{getInitialState:function(e){return void 0===e&&(e={}),Object.assign({ids:[],entities:{}},e)}},{},{getSelectors:function(e){var t=function(e){return e.ids},r=function(e){return e.entities},n=u(t,r,(function(e,t){return e.map((function(e){return t[e]}))})),o=function(e,t){return t},i=function(e,t){return e[t]},a=u(t,(function(e){return e.length}));if(!e)return{selectIds:t,selectEntities:r,selectAll:n,selectTotal:a,selectById:u(r,o,i)};var c=u(e,r);return{selectIds:u(e,t),selectEntities:c,selectAll:u(e,n),selectTotal:u(e,a),selectById:u(c,o,i)}}},{},n?function(e,t){var r=S(e);function n(t,r){Array.isArray(t)||(t=Object.values(t));var n=t.filter((function(t){return!(x(t,e)in r.entities)}));0!==n.length&&u(n,r)}function o(t,r){var n=[];t.forEach((function(t){return function(t,r,n){if(!(r.id in n.entities))return!1;var o=Object.assign({},n.entities[r.id],r.changes),i=x(o,e);return delete n.entities[r.id],t.push(o),i!==r.id}(n,t,r)})),0!==n.length&&u(n,r)}function i(t,r){Array.isArray(t)||(t=Object.values(t));var i=[],u=[],a=t,c=Array.isArray(a),f=0;for(a=c?a:a[Symbol.iterator]();;){var s;if(c){if(f>=a.length)break;s=a[f++]}else{if((f=a.next()).done)break;s=f.value}var l=s,d=x(l,e);d in r.entities?u.push({id:d,changes:l}):i.push(l)}o(u,r),n(i,r)}function u(r,n){r.sort(t),r.forEach((function(t){n.entities[e(t)]=t}));var o=Object.values(n.entities);o.sort(t);var i=o.map(e);(function(e,t){if(e.length!==t.length)return!1;for(var r=0;r<e.length&&r<t.length;r++)if(e[r]!==t[r])return!1;return!0})(n.ids,i)||(n.ids=i)}return{removeOne:r.removeOne,removeMany:r.removeMany,removeAll:r.removeAll,addOne:A((function(e,t){return n([e],t)})),updateOne:A((function(e,t){return o([e],t)})),upsertOne:A((function(e,t){return i([e],t)})),setAll:A((function(e,t){Array.isArray(e)||(e=Object.values(e)),t.entities={},t.ids=[],n(e,t)})),addMany:A(n),updateMany:A(o),upsertMany:A(i)}}(r,n):S(r))},exports.createImmutableStateInvariantMiddleware=function(e){return function(){return function(e){return function(t){return e(t)}}}},exports.createReducer=j,exports.createSerializableStateInvariantMiddleware=function(e){return function(){return function(e){return function(t){return e(t)}}}},exports.createSlice=function(e){var t=e.name,r=e.initialState;if(!t)throw new Error("`name` is a required option for createSlice");var n=e.reducers||{},o=void 0===e.extraReducers?[]:"function"==typeof e.extraReducers?O(e.extraReducers):[e.extraReducers],i=o[0],u=void 0===i?{}:i,c=o[1],f=void 0===c?[]:c,s=o[2],l=void 0===s?void 0:s,d=Object.keys(n),p={},y={},v={};d.forEach((function(e){var r,o,i=n[e],u=t+"/"+e;"reducer"in i?(r=i.reducer,o=i.prepare):r=i,p[e]=r,y[u]=r,v[e]=o?m(u,o):m(u)}));var h=j(r,a({},u,{},y),f,l);return{name:t,reducer:h,actions:v,caseReducers:p}},exports.findNonSerializableValue=function e(t,r,n,o,i){var u;if(void 0===r&&(r=[]),void 0===n&&(n=h),void 0===i&&(i=[]),!n(t))return{keyPath:r.join(".")||"<root>",value:t};if("object"!=typeof t||null===t)return!1;var a=null!=o?o(t):Object.entries(t),c=i.length>0,f=a,s=Array.isArray(f),l=0;for(f=s?f:f[Symbol.iterator]();;){var d;if(s){if(l>=f.length)break;d=f[l++]}else{if((l=f.next()).done)break;d=l.value}var p=d[1],y=r.concat(d[0]);if(!(c&&i.indexOf(y.join("."))>=0)){if(!n(p))return{keyPath:y.join("."),value:p};if("object"==typeof p&&(u=e(p,y,n,o,i)))return u}}return!1},exports.getDefaultMiddleware=b,exports.getType=function(e){return""+e},exports.isAllOf=R,exports.isAnyOf=I,exports.isAsyncThunkAction=function e(){for(var t=arguments.length,r=new Array(t),n=0;n<t;n++)r[n]=arguments[n];return 0===r.length?function(e){return M(e,["pending","fulfilled","rejected"])}:q(r)?function(e){var t=[],n=r,o=Array.isArray(n),i=0;for(n=o?n:n[Symbol.iterator]();;){var u;if(o){if(i>=n.length)break;u=n[i++]}else{if((i=n.next()).done)break;u=i.value}t.push(u.pending,u.rejected,u.fulfilled)}return I.apply(void 0,t)(e)}:e()(r[0])},exports.isFulfilled=function e(){for(var t=arguments.length,r=new Array(t),n=0;n<t;n++)r[n]=arguments[n];return 0===r.length?function(e){return M(e,["fulfilled"])}:q(r)?function(e){var t=r.map((function(e){return e.fulfilled}));return I.apply(void 0,t)(e)}:e()(r[0])},exports.isImmutableDefault=function(e){return"object"!=typeof e||null==e},exports.isPending=function e(){for(var t=arguments.length,r=new Array(t),n=0;n<t;n++)r[n]=arguments[n];return 0===r.length?function(e){return M(e,["pending"])}:q(r)?function(e){var t=r.map((function(e){return e.pending}));return I.apply(void 0,t)(e)}:e()(r[0])},exports.isPlain=h,exports.isPlainObject=y,exports.isRejected=D,exports.isRejectedWithValue=function e(){for(var t=arguments.length,r=new Array(t),n=0;n<t;n++)r[n]=arguments[n];var o=function(e){return e&&e.meta&&e.meta.rejectedWithValue};return 0===r.length?function(e){return R(D.apply(void 0,r),o)(e)}:q(r)?function(e){return R(D.apply(void 0,r),o)(e)}:e()(r[0])},exports.nanoid=w,exports.unwrapResult=function(e){if(e.meta&&e.meta.rejectedWithValue)throw e.payload;if(e.error)throw e.error;return e.payload};
- //# sourceMappingURL=redux-toolkit.cjs.production.min.js.map
-diff --git a/node_modules/@reduxjs/toolkit/dist/redux-toolkit.esm.js b/node_modules/@reduxjs/toolkit/dist/redux-toolkit.esm.js
-index 859834f..24f3b8a 100644
---- a/node_modules/@reduxjs/toolkit/dist/redux-toolkit.esm.js
-+++ b/node_modules/@reduxjs/toolkit/dist/redux-toolkit.esm.js
-@@ -202,7 +202,7 @@ function (_Array) {
- 
-   var _proto = MiddlewareArray.prototype;
- 
--  _proto.concat = function concat() {
-+  Object.defineProperty(_proto, 'concat', { value: function concat() {
-     var _Array$prototype$conc;
- 
-     for (var _len = arguments.length, arr = new Array(_len), _key = 0; _key < _len; _key++) {
-@@ -210,7 +210,7 @@ function (_Array) {
-     }
- 
-     return _construct(MiddlewareArray, (_Array$prototype$conc = _Array.prototype.concat).call.apply(_Array$prototype$conc, [this].concat(arr)));
--  };
-+  } })
- 
-   _proto.prepend = function prepend() {
-     for (var _len2 = arguments.length, arr = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
-diff --git a/node_modules/@reduxjs/toolkit/dist/redux-toolkit.umd.js b/node_modules/@reduxjs/toolkit/dist/redux-toolkit.umd.js
-index 185aa69..224dd31 100644
---- a/node_modules/@reduxjs/toolkit/dist/redux-toolkit.umd.js
-+++ b/node_modules/@reduxjs/toolkit/dist/redux-toolkit.umd.js
-@@ -1017,7 +1017,7 @@ function (_Array) {
- 
-   var _proto = MiddlewareArray.prototype;
- 
--  _proto.concat = function concat() {
-+  Object.defineProperty(_proto, 'concat', { value: function concat() {
-     var _Array$prototype$conc;
- 
-     for (var _len = arguments.length, arr = new Array(_len), _key = 0; _key < _len; _key++) {
-@@ -1025,7 +1025,7 @@ function (_Array) {
-     }
- 
-     return _construct(MiddlewareArray, (_Array$prototype$conc = _Array.prototype.concat).call.apply(_Array$prototype$conc, [this].concat(arr)));
--  };
-+  } })
- 
-   _proto.prepend = function prepend() {
-     for (var _len2 = arguments.length, arr = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
-diff --git a/node_modules/@reduxjs/toolkit/dist/redux-toolkit.umd.min.js b/node_modules/@reduxjs/toolkit/dist/redux-toolkit.umd.min.js
-index e0ef0e0..6207189 100644
---- a/node_modules/@reduxjs/toolkit/dist/redux-toolkit.umd.min.js
-+++ b/node_modules/@reduxjs/toolkit/dist/redux-toolkit.umd.min.js
-@@ -1,2 +1,2 @@
--!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e=e||self).RTK={})}(this,(function(e){"use strict";function t(e){for(var t=arguments.length,r=Array(t>1?t-1:0),n=1;n<t;n++)r[n-1]=arguments[n];throw Error("[Immer] minified error nr: "+e+(r.length?" "+r.map((function(e){return"'"+e+"'"})).join(","):"")+". Find the full error at: https://bit.ly/3cXEKWf")}function r(e){return!!e&&!!e[U]}function n(e){return!!e&&(function(e){if(!e||"object"!=typeof e)return!1;var t=Object.getPrototypeOf(e);return!t||t===Object.prototype}(e)||Array.isArray(e)||!!e[K]||!!e.constructor[K]||f(e)||l(e))}function o(e,t,r){void 0===r&&(r=!1),0===i(e)?(r?Object.keys:V)(e).forEach((function(n){r&&"symbol"==typeof n||t(n,e[n],e)})):e.forEach((function(r,n){return t(n,r,e)}))}function i(e){var t=e[U];return t?t.i>3?t.i-4:t.i:Array.isArray(e)?1:f(e)?2:l(e)?3:0}function u(e,t){return 2===i(e)?e.has(t):Object.prototype.hasOwnProperty.call(e,t)}function a(e,t,r){var n=i(e);2===n?e.set(t,r):3===n?(e.delete(t),e.add(r)):e[t]=r}function c(e,t){return e===t?0!==e||1/e==1/t:e!=e&&t!=t}function f(e){return W&&e instanceof Map}function l(e){return F&&e instanceof Set}function s(e){return e.o||e.t}function d(e){if(Array.isArray(e))return Array.prototype.slice.call(e);var t=L(e);delete t[U];for(var r=V(t),n=0;n<r.length;n++){var o=r[n],i=t[o];!1===i.writable&&(i.writable=!0,i.configurable=!0),(i.get||i.set)&&(t[o]={configurable:!0,writable:!0,enumerable:i.enumerable,value:e[o]})}return Object.create(Object.getPrototypeOf(e),t)}function p(e,t){return void 0===t&&(t=!1),v(e)||r(e)||!n(e)?e:(i(e)>1&&(e.set=e.add=e.clear=e.delete=y),Object.freeze(e),t&&o(e,(function(e,t){return p(t,!0)}),!0),e)}function y(){t(2)}function v(e){return null==e||"object"!=typeof e||Object.isFrozen(e)}function h(e){var r=B[e];return r||t(18,e),r}function b(){return M}function g(e,t){t&&(h("Patches"),e.u=[],e.s=[],e.v=t)}function m(e){O(e),e.p.forEach(j),e.p=null}function O(e){e===M&&(M=e.l)}function w(e){return M={p:[],l:M,h:e,m:!0,_:0}}function j(e){var t=e[U];0===t.i||1===t.i?t.j():t.g=!0}function A(e,r){r._=r.p.length;var o=r.p[0],i=void 0!==e&&e!==o;return r.h.O||h("ES5").S(r,e,i),i?(o[U].P&&(m(r),t(4)),n(e)&&(e=P(r,e),r.l||E(r,e)),r.u&&h("Patches").M(o[U],e,r.u,r.s)):e=P(r,o,[]),m(r),r.u&&r.v(r.u,r.s),e!==z?e:void 0}function P(e,t,r){if(v(t))return t;var n=t[U];if(!n)return o(t,(function(o,i){return S(e,n,t,o,i,r)}),!0),t;if(n.A!==e)return t;if(!n.P)return E(e,n.t,!0),n.t;if(!n.I){n.I=!0,n.A._--;var i=4===n.i||5===n.i?n.o=d(n.k):n.o;o(3===n.i?new Set(i):i,(function(t,o){return S(e,n,i,t,o,r)})),E(e,i,!1),r&&e.u&&h("Patches").R(n,r,e.u,e.s)}return n.o}function S(e,t,o,i,c,f){if(r(c)){var l=P(e,c,f&&t&&3!==t.i&&!u(t.D,i)?f.concat(i):void 0);if(a(o,i,l),!r(l))return;e.m=!1}if(n(c)&&!v(c)){if(!e.h.N&&e._<1)return;P(e,c),t&&t.A.l||E(e,c)}}function E(e,t,r){void 0===r&&(r=!1),e.h.N&&e.m&&p(t,r)}function x(e,t){var r=e[U];return(r?s(r):e)[t]}function I(e,t){if(t in e)for(var r=Object.getPrototypeOf(e);r;){var n=Object.getOwnPropertyDescriptor(r,t);if(n)return n;r=Object.getPrototypeOf(r)}}function _(e){e.P||(e.P=!0,e.l&&_(e.l))}function k(e){e.o||(e.o=d(e.t))}function R(e,t,r){var n=f(t)?h("MapSet").T(t,r):l(t)?h("MapSet").F(t,r):e.O?function(e,t){var r=Array.isArray(e),n={i:r?1:0,A:t?t.A:b(),P:!1,I:!1,D:{},l:t,t:e,k:null,o:null,j:null,C:!1},o=n,i=X;r&&(o=[n],i=Y);var u=Proxy.revocable(o,i),a=u.revoke,c=u.proxy;return n.k=c,n.j=a,c}(t,r):h("ES5").J(t,r);return(r?r.A:b()).p.push(n),n}function D(e){return r(e)||t(22,e),function e(t){if(!n(t))return t;var r,u=t[U],c=i(t);if(u){if(!u.P&&(u.i<4||!h("ES5").K(u)))return u.t;u.I=!0,r=N(t,c),u.I=!1}else r=N(t,c);return o(r,(function(t,n){u&&function(e,t){return 2===i(e)?e.get(t):e[t]}(u.t,t)===n||a(r,t,e(n))})),3===c?new Set(r):r}(e)}function N(e,t){switch(t){case 2:return new Map(e);case 3:return Array.from(e)}return d(e)}var T,M,C="undefined"!=typeof Symbol&&"symbol"==typeof Symbol("x"),W="undefined"!=typeof Map,F="undefined"!=typeof Set,q="undefined"!=typeof Proxy&&void 0!==Proxy.revocable&&"undefined"!=typeof Reflect,z=C?Symbol.for("immer-nothing"):((T={})["immer-nothing"]=!0,T),K=C?Symbol.for("immer-draftable"):"__$immer_draftable",U=C?Symbol.for("immer-state"):"__$immer_state",V="undefined"!=typeof Reflect&&Reflect.ownKeys?Reflect.ownKeys:void 0!==Object.getOwnPropertySymbols?function(e){return Object.getOwnPropertyNames(e).concat(Object.getOwnPropertySymbols(e))}:Object.getOwnPropertyNames,L=Object.getOwnPropertyDescriptors||function(e){var t={};return V(e).forEach((function(r){t[r]=Object.getOwnPropertyDescriptor(e,r)})),t},B={},X={get:function(e,t){if(t===U)return e;var r=s(e);if(!u(r,t))return function(e,t,r){var n,o=I(t,r);return o?"value"in o?o.value:null===(n=o.get)||void 0===n?void 0:n.call(e.k):void 0}(e,r,t);var o=r[t];return e.I||!n(o)?o:o===x(e.t,t)?(k(e),e.o[t]=R(e.A.h,o,e)):o},has:function(e,t){return t in s(e)},ownKeys:function(e){return Reflect.ownKeys(s(e))},set:function(e,t,r){var n=I(s(e),t);if(null==n?void 0:n.set)return n.set.call(e.k,r),!0;if(!e.P){var o=x(s(e),t),i=null==o?void 0:o[U];if(i&&i.t===r)return e.o[t]=r,e.D[t]=!1,!0;if(c(r,o)&&(void 0!==r||u(e.t,t)))return!0;k(e),_(e)}return e.o[t]=r,e.D[t]=!0,!0},deleteProperty:function(e,t){return void 0!==x(e.t,t)||t in e.t?(e.D[t]=!1,k(e),_(e)):delete e.D[t],e.o&&delete e.o[t],!0},getOwnPropertyDescriptor:function(e,t){var r=s(e),n=Reflect.getOwnPropertyDescriptor(r,t);return n?{writable:!0,configurable:1!==e.i||"length"!==t,enumerable:n.enumerable,value:r[t]}:n},defineProperty:function(){t(11)},getPrototypeOf:function(e){return Object.getPrototypeOf(e.t)},setPrototypeOf:function(){t(12)}},Y={};o(X,(function(e,t){Y[e]=function(){return arguments[0]=arguments[0][0],t.apply(this,arguments)}})),Y.deleteProperty=function(e,t){return X.deleteProperty.call(this,e[0],t)},Y.set=function(e,t,r){return X.set.call(this,e[0],t,r,e[0])};var J=new(function(){function e(e){this.O=q,this.N=!0,"boolean"==typeof(null==e?void 0:e.useProxies)&&this.setUseProxies(e.useProxies),"boolean"==typeof(null==e?void 0:e.autoFreeze)&&this.setAutoFreeze(e.autoFreeze),this.produce=this.produce.bind(this),this.produceWithPatches=this.produceWithPatches.bind(this)}var o=e.prototype;return o.produce=function(e,r,o){if("function"==typeof e&&"function"!=typeof r){var i=r;r=e;var u=this;return function(e){var t=this;void 0===e&&(e=i);for(var n=arguments.length,o=Array(n>1?n-1:0),a=1;a<n;a++)o[a-1]=arguments[a];return u.produce(e,(function(e){var n;return(n=r).call.apply(n,[t,e].concat(o))}))}}var a;if("function"!=typeof r&&t(6),void 0!==o&&"function"!=typeof o&&t(7),n(e)){var c=w(this),f=R(this,e,void 0),l=!0;try{a=r(f),l=!1}finally{l?m(c):O(c)}return"undefined"!=typeof Promise&&a instanceof Promise?a.then((function(e){return g(c,o),A(e,c)}),(function(e){throw m(c),e})):(g(c,o),A(a,c))}if(!e||"object"!=typeof e){if((a=r(e))===z)return;return void 0===a&&(a=e),this.N&&p(a,!0),a}t(21,e)},o.produceWithPatches=function(e,t){var r,n,o=this;return"function"==typeof e?function(t){for(var r=arguments.length,n=Array(r>1?r-1:0),i=1;i<r;i++)n[i-1]=arguments[i];return o.produceWithPatches(t,(function(t){return e.apply(void 0,[t].concat(n))}))}:[this.produce(e,t,(function(e,t){r=e,n=t})),r,n]},o.createDraft=function(e){n(e)||t(8),r(e)&&(e=D(e));var o=w(this),i=R(this,e,void 0);return i[U].C=!0,O(o),i},o.finishDraft=function(e,t){var r=(e&&e[U]).A;return g(r,t),A(void 0,r)},o.setAutoFreeze=function(e){this.N=e},o.setUseProxies=function(e){e&&!q&&t(20),this.O=e},o.applyPatches=function(e,t){var n;for(n=t.length-1;n>=0;n--){var o=t[n];if(0===o.path.length&&"replace"===o.op){e=o.value;break}}var i=h("Patches").$;return r(e)?i(e,t):this.produce(e,(function(e){return i(e,t.slice(n+1))}))},e}()),$=J.produce;J.produceWithPatches.bind(J),J.setAutoFreeze.bind(J),J.setUseProxies.bind(J),J.applyPatches.bind(J),J.createDraft.bind(J),J.finishDraft.bind(J);var G=function(e){var t,r=("undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof module?module:Function("return this")()).Symbol;return"function"==typeof r?r.observable?t=r.observable:(t=r("observable"),r.observable=t):t="@@observable",t}(),H=function(){return Math.random().toString(36).substring(7).split("").join(".")},Q={INIT:"@@redux/INIT"+H(),REPLACE:"@@redux/REPLACE"+H(),PROBE_UNKNOWN_ACTION:function(){return"@@redux/PROBE_UNKNOWN_ACTION"+H()}};function Z(e){if("object"!=typeof e||null===e)return!1;for(var t=e;null!==Object.getPrototypeOf(t);)t=Object.getPrototypeOf(t);return Object.getPrototypeOf(e)===t}function ee(e,t,r){var n;if("function"==typeof t&&"function"==typeof r||"function"==typeof r&&"function"==typeof arguments[3])throw new Error("It looks like you are passing several store enhancers to createStore(). This is not supported. Instead, compose them together to a single function.");if("function"==typeof t&&void 0===r&&(r=t,t=void 0),void 0!==r){if("function"!=typeof r)throw new Error("Expected the enhancer to be a function.");return r(ee)(e,t)}if("function"!=typeof e)throw new Error("Expected the reducer to be a function.");var o=e,i=t,u=[],a=u,c=!1;function f(){a===u&&(a=u.slice())}function l(){if(c)throw new Error("You may not call store.getState() while the reducer is executing. The reducer has already received the state as an argument. Pass it down from the top reducer instead of reading it from the store.");return i}function s(e){if("function"!=typeof e)throw new Error("Expected the listener to be a function.");if(c)throw new Error("You may not call store.subscribe() while the reducer is executing. If you would like to be notified after the store has been updated, subscribe from a component and invoke store.getState() in the callback to access the latest state. See https://redux.js.org/api-reference/store#subscribelistener for more details.");var t=!0;return f(),a.push(e),function(){if(t){if(c)throw new Error("You may not unsubscribe from a store listener while the reducer is executing. See https://redux.js.org/api-reference/store#subscribelistener for more details.");t=!1,f();var r=a.indexOf(e);a.splice(r,1),u=null}}}function d(e){if(!Z(e))throw new Error("Actions must be plain objects. Use custom middleware for async actions.");if(void 0===e.type)throw new Error('Actions may not have an undefined "type" property. Have you misspelled a constant?');if(c)throw new Error("Reducers may not dispatch actions.");try{c=!0,i=o(i,e)}finally{c=!1}for(var t=u=a,r=0;r<t.length;r++)(0,t[r])();return e}function p(e){if("function"!=typeof e)throw new Error("Expected the nextReducer to be a function.");o=e,d({type:Q.REPLACE})}function y(){var e,t=s;return(e={subscribe:function(e){if("object"!=typeof e||null===e)throw new TypeError("Expected the observer to be an object.");function r(){e.next&&e.next(l())}return r(),{unsubscribe:t(r)}}})[G]=function(){return this},e}return d({type:Q.INIT}),(n={dispatch:d,subscribe:s,getState:l,replaceReducer:p})[G]=y,n}function te(e,t){var r=t&&t.type;return"Given "+(r&&'action "'+String(r)+'"'||"an action")+', reducer "'+e+'" returned undefined. To ignore an action, you must explicitly return the previous state. If you want this reducer to hold no value, you can return null instead of undefined.'}function re(e){for(var t=Object.keys(e),r={},n=0;n<t.length;n++){var o=t[n];"function"==typeof e[o]&&(r[o]=e[o])}var i,u=Object.keys(r);try{!function(e){Object.keys(e).forEach((function(t){var r=e[t];if(void 0===r(void 0,{type:Q.INIT}))throw new Error('Reducer "'+t+"\" returned undefined during initialization. If the state passed to the reducer is undefined, you must explicitly return the initial state. The initial state may not be undefined. If you don't want to set a value for this reducer, you can use null instead of undefined.");if(void 0===r(void 0,{type:Q.PROBE_UNKNOWN_ACTION()}))throw new Error('Reducer "'+t+"\" returned undefined when probed with a random type. Don't try to handle "+Q.INIT+' or other actions in "redux/*" namespace. They are considered private. Instead, you must return the current state for any unknown actions, unless it is undefined, in which case you must return the initial state, regardless of the action type. The initial state may not be undefined, but can be null.')}))}(r)}catch(e){i=e}return function(e,t){if(void 0===e&&(e={}),i)throw i;for(var n=!1,o={},a=0;a<u.length;a++){var c=u[a],f=e[c],l=(0,r[c])(f,t);if(void 0===l){var s=te(c,t);throw new Error(s)}o[c]=l,n=n||l!==f}return(n=n||u.length!==Object.keys(e).length)?o:e}}function ne(e,t){return function(){return t(e.apply(this,arguments))}}function oe(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function ie(e,t){var r=Object.keys(e);return Object.getOwnPropertySymbols&&r.push.apply(r,Object.getOwnPropertySymbols(e)),t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r}function ue(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?ie(r,!0).forEach((function(t){oe(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):ie(r).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}function ae(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return 0===t.length?function(e){return e}:1===t.length?t[0]:t.reduce((function(e,t){return function(){return e(t.apply(void 0,arguments))}}))}function ce(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return function(e){return function(){var r=e.apply(void 0,arguments),n=function(){throw new Error("Dispatching while constructing your middleware is not allowed. Other middleware would not be applied to this dispatch.")},o={getState:r.getState,dispatch:function(){return n.apply(void 0,arguments)}},i=t.map((function(e){return e(o)}));return ue({},r,{dispatch:n=ae.apply(void 0,i)(r.dispatch)})}}}function fe(e,t){return e===t}function le(e,t,r){if(null===t||null===r||t.length!==r.length)return!1;for(var n=t.length,o=0;o<n;o++)if(!e(t[o],r[o]))return!1;return!0}function se(e){var t=Array.isArray(e[0])?e[0]:e;if(!t.every((function(e){return"function"==typeof e}))){var r=t.map((function(e){return typeof e})).join(", ");throw new Error("Selector creators expect all input-selectors to be functions, instead received the following types: ["+r+"]")}return t}var de=function(e){for(var t=arguments.length,r=Array(t>1?t-1:0),n=1;n<t;n++)r[n-1]=arguments[n];return function(){for(var t=arguments.length,n=Array(t),o=0;o<t;o++)n[o]=arguments[o];var i=0,u=n.pop(),a=se(n),c=e.apply(void 0,[function(){return i++,u.apply(null,arguments)}].concat(r)),f=e((function(){for(var e=[],t=a.length,r=0;r<t;r++)e.push(a[r].apply(null,arguments));return c.apply(null,e)}));return f.resultFunc=u,f.dependencies=a,f.recomputations=function(){return i},f.resetRecomputations=function(){return i=0},f}}((function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:fe,r=null,n=null;return function(){return le(t,r,arguments)||(n=e.apply(null,arguments)),r=arguments,n}})),pe=function(){var e=de.apply(void 0,arguments),t=function(t){for(var n=arguments.length,o=new Array(n>1?n-1:0),i=1;i<n;i++)o[i-1]=arguments[i];return e.apply(void 0,[r(t)?D(t):t].concat(o))};return t};function ye(){return(ye=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e}).apply(this,arguments)}function ve(e){return(ve=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function he(e,t){return(he=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function be(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(e){return!1}}function ge(e,t,r){return(ge=be()?Reflect.construct:function(e,t,r){var n=[null];n.push.apply(n,t);var o=new(Function.bind.apply(e,n));return r&&he(o,r.prototype),o}).apply(null,arguments)}function me(e){var t="function"==typeof Map?new Map:void 0;return(me=function(e){if(null===e||-1===Function.toString.call(e).indexOf("[native code]"))return e;if("function"!=typeof e)throw new TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,r)}function r(){return ge(e,arguments,ve(this).constructor)}return r.prototype=Object.create(e.prototype,{constructor:{value:r,enumerable:!1,writable:!0,configurable:!0}}),he(r,e)})(e)}var Oe="undefined"!=typeof window&&window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__?window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__:function(){if(0!==arguments.length)return"object"==typeof arguments[0]?ae:ae.apply(null,arguments)};function we(e){if("object"!=typeof e||null===e)return!1;for(var t=e;null!==Object.getPrototypeOf(t);)t=Object.getPrototypeOf(t);return Object.getPrototypeOf(e)===t}function je(e){return function(t){var r=t.dispatch,n=t.getState;return function(t){return function(o){return"function"==typeof o?o(r,n,e):t(o)}}}}var Ae=je();Ae.withExtraArgument=je;var Pe=function(e){var t,r;function n(){return e.apply(this,arguments)||this}r=e,(t=n).prototype=Object.create(r.prototype),t.prototype.constructor=t,t.__proto__=r;var o=n.prototype;return o.concat=function(){for(var t,r=arguments.length,o=new Array(r),i=0;i<r;i++)o[i]=arguments[i];return ge(n,(t=e.prototype.concat).call.apply(t,[this].concat(o)))},o.prepend=function(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return 1===t.length&&Array.isArray(t[0])?ge(n,t[0].concat(this)):ge(n,t.concat(this))},n}(me(Array));function Se(e){return null==e||"string"==typeof e||"boolean"==typeof e||"number"==typeof e||Array.isArray(e)||we(e)}function Ee(e){void 0===e&&(e={});var t=e.thunk,r=void 0===t||t,n=new Pe;return r&&(function(e){return"boolean"==typeof e}(r)?n.push(Ae):n.push(Ae.withExtraArgument(r.extraArgument))),n}function xe(e,t){function r(){if(t){var r=t.apply(void 0,arguments);if(!r)throw new Error("prepareAction did not return an object");return ye({type:e,payload:r.payload},"meta"in r&&{meta:r.meta},{},"error"in r&&{error:r.error})}return{type:e,payload:arguments.length<=0?void 0:arguments[0]}}return r.toString=function(){return""+e},r.type=e,r.match=function(t){return t.type===e},r}function Ie(e){return["type","payload","error","meta"].indexOf(e)>-1}function _e(e){var t,r={},n=[],o={addCase:function(e,t){var n="string"==typeof e?e:e.type;if(n in r)throw new Error("addCase cannot be called with two reducers for the same action type");return r[n]=t,o},addMatcher:function(e,t){return n.push({matcher:e,reducer:t}),o},addDefaultCase:function(e){return t=e,o}};return e(o),[r,n,t]}function ke(e,t,o,i){void 0===o&&(o=[]);var u="function"==typeof t?_e(t):[t,o,i],a=u[0],c=u[1],f=u[2];return function(t,o){void 0===t&&(t=e);var i=[a[o.type]].concat(c.filter((function(e){return(0,e.matcher)(o)})).map((function(e){return e.reducer})));return 0===i.filter((function(e){return!!e})).length&&(i=[f]),i.reduce((function(e,t){if(t){if(r(e)){var i=t(e,o);return void 0===i?e:i}if(n(e))return $(e,(function(e){return t(e,o)}));var u=t(e,o);if(void 0===u){if(null===e)return e;throw Error("A case reducer on a non-draftable value must not return undefined")}return u}return e}),t)}}function Re(e){return function(t,n){var o=function(t){!function(e){return we(t=e)&&"string"==typeof t.type&&Object.keys(t).every(Ie);var t}(n)?e(n,t):e(n.payload,t)};return r(t)?(o(t),t):$(t,o)}}function De(e,t){return t(e)}function Ne(e){function t(t,r){var n=De(t,e);n in r.entities||(r.ids.push(n),r.entities[n]=t)}function r(e,r){Array.isArray(e)||(e=Object.values(e));var n=e,o=Array.isArray(n),i=0;for(n=o?n:n[Symbol.iterator]();;){var u;if(o){if(i>=n.length)break;u=n[i++]}else{if((i=n.next()).done)break;u=i.value}t(u,r)}}function n(e,t){var r=!1;e.forEach((function(e){e in t.entities&&(delete t.entities[e],r=!0)})),r&&(t.ids=t.ids.filter((function(e){return e in t.entities})))}function o(t,r){var n={},o={};t.forEach((function(e){e.id in r.entities&&(o[e.id]={id:e.id,changes:ye({},o[e.id]?o[e.id].changes:null,{},e.changes)})})),(t=Object.values(o)).length>0&&t.filter((function(t){return function(t,r,n){var o=Object.assign({},n.entities[r.id],r.changes),i=De(o,e),u=i!==r.id;return u&&(t[r.id]=i,delete n.entities[r.id]),n.entities[i]=o,u}(n,t,r)})).length>0&&(r.ids=r.ids.map((function(e){return n[e]||e})))}function i(t,n){Array.isArray(t)||(t=Object.values(t));var i=[],u=[],a=t,c=Array.isArray(a),f=0;for(a=c?a:a[Symbol.iterator]();;){var l;if(c){if(f>=a.length)break;l=a[f++]}else{if((f=a.next()).done)break;l=f.value}var s=l,d=De(s,e);d in n.entities?u.push({id:d,changes:s}):i.push(s)}o(u,n),r(i,n)}return{removeAll:(u=function(e){Object.assign(e,{ids:[],entities:{}})},a=Re((function(e,t){return u(t)})),function(e){return a(e,void 0)}),addOne:Re(t),addMany:Re(r),setAll:Re((function(e,t){Array.isArray(e)||(e=Object.values(e)),t.ids=[],t.entities={},r(e,t)})),updateOne:Re((function(e,t){return o([e],t)})),updateMany:Re(o),upsertOne:Re((function(e,t){return i([e],t)})),upsertMany:Re(i),removeOne:Re((function(e,t){return n([e],t)})),removeMany:Re(n)};var u,a}"undefined"!=typeof Symbol&&(Symbol.iterator||(Symbol.iterator=Symbol("Symbol.iterator"))),"undefined"!=typeof Symbol&&(Symbol.asyncIterator||(Symbol.asyncIterator=Symbol("Symbol.asyncIterator")));var Te=function(e){void 0===e&&(e=21);for(var t="",r=e;r--;)t+="ModuleSymbhasOwnPr-0123456789ABCDEFGHNRVfgctiUvz_KqYTJkLxpZXIjQW"[64*Math.random()|0];return t},Me=["name","message","stack","code"],Ce=function(e){this.payload=e,this.name="RejectWithValue",this.message="Rejected"},We=function(e){if("object"==typeof e&&null!==e){var t={},r=Me,n=Array.isArray(r),o=0;for(r=n?r:r[Symbol.iterator]();;){var i;if(n){if(o>=r.length)break;i=r[o++]}else{if((o=r.next()).done)break;i=o.value}"string"==typeof e[i]&&(t[i]=e[i])}return t}return{message:String(e)}},Fe=function(e,t){return function(e){return e&&"function"==typeof e.match}(e)?e.match(t):e(t)};function qe(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return function(e){return t.some((function(t){return Fe(t,e)}))}}function ze(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return function(e){return t.every((function(t){return Fe(t,e)}))}}function Ke(e,t){if(!e||!e.meta)return!1;var r="string"==typeof e.meta.requestId,n=t.indexOf(e.meta.requestStatus)>-1;return r&&n}function Ue(e){return"function"==typeof e[0]&&"pending"in e[0]&&"fulfilled"in e[0]&&"rejected"in e[0]}function Ve(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return 0===t.length?function(e){return Ke(e,["rejected"])}:Ue(t)?function(e){var r=t.map((function(e){return e.rejected}));return qe.apply(void 0,r)(e)}:Ve()(t[0])}!function(){function e(e,t){var r=a[e];return r?r.enumerable=t:a[e]=r={configurable:!0,enumerable:t,get:function(){return X.get(this[U],e)},set:function(t){X.set(this[U],e,t)}},r}function t(e){for(var t=e.length-1;t>=0;t--){var r=e[t][U];if(!r.P)switch(r.i){case 5:i(r)&&_(r);break;case 4:n(r)&&_(r)}}}function n(e){for(var t=e.t,r=e.k,n=V(r),o=n.length-1;o>=0;o--){var i=n[o];if(i!==U){var a=t[i];if(void 0===a&&!u(t,i))return!0;var f=r[i],l=f&&f[U];if(l?l.t!==a:!c(f,a))return!0}}var s=!!t[U];return n.length!==V(t).length+(s?0:1)}function i(e){var t=e.k;if(t.length!==e.t.length)return!0;var r=Object.getOwnPropertyDescriptor(t,t.length-1);return!(!r||r.get)}var a={};!function(e,t){B.ES5||(B.ES5=t)}(0,{J:function(t,r){var n=Array.isArray(t),o=function(t,r){if(t){for(var n=Array(r.length),o=0;o<r.length;o++)Object.defineProperty(n,""+o,e(o,!0));return n}var i=L(r);delete i[U];for(var u=V(i),a=0;a<u.length;a++){var c=u[a];i[c]=e(c,t||!!i[c].enumerable)}return Object.create(Object.getPrototypeOf(r),i)}(n,t),i={i:n?5:4,A:r?r.A:b(),P:!1,I:!1,D:{},l:r,t:t,k:o,o:null,g:!1,C:!1};return Object.defineProperty(o,U,{value:i,writable:!0}),o},S:function(e,n,a){a?r(n)&&n[U].A===e&&t(e.p):(e.u&&function e(t){if(t&&"object"==typeof t){var r=t[U];if(r){var n=r.t,a=r.k,c=r.D,f=r.i;if(4===f)o(a,(function(t){t!==U&&(void 0!==n[t]||u(n,t)?c[t]||e(a[t]):(c[t]=!0,_(r)))})),o(n,(function(e){void 0!==a[e]||u(a,e)||(c[e]=!1,_(r))}));else if(5===f){if(i(r)&&(_(r),c.length=!0),a.length<n.length)for(var l=a.length;l<n.length;l++)c[l]=!1;else for(var s=n.length;s<a.length;s++)c[s]=!0;for(var d=Math.min(a.length,n.length),p=0;p<d;p++)void 0===c[p]&&e(a[p])}}}}(e.p[0]),t(e.p))},K:function(e){return 4===e.i?n(e):i(e)}})}(),e.MiddlewareArray=Pe,e.__DO_NOT_USE__ActionTypes=Q,e.applyMiddleware=ce,e.bindActionCreators=function(e,t){if("function"==typeof e)return ne(e,t);if("object"!=typeof e||null===e)throw new Error("bindActionCreators expected an object or a function, instead received "+(null===e?"null":typeof e)+'. Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?');var r={};for(var n in e){var o=e[n];"function"==typeof o&&(r[n]=ne(o,t))}return r},e.combineReducers=re,e.compose=ae,e.configureStore=function(e){var t,r=function(e){return Ee(e)},n=e||{},o=n.reducer,i=void 0===o?void 0:o,u=n.middleware,a=void 0===u?r():u,c=n.devTools,f=void 0===c||c,l=n.preloadedState,s=void 0===l?void 0:l,d=n.enhancers,p=void 0===d?void 0:d;if("function"==typeof i)t=i;else{if(!we(i))throw new Error('"reducer" is a required argument, and must be a function or an object of functions that can be passed to combineReducers');t=re(i)}var y=ce.apply(void 0,"function"==typeof a?a(r):a),v=ae;f&&(v=Oe(ye({trace:!1},"object"==typeof f&&f)));var h=[y];return Array.isArray(p)?h=[y].concat(p):"function"==typeof p&&(h=p(h)),ee(t,s,v.apply(void 0,h))},e.createAction=xe,e.createAsyncThunk=function(e,t,r){var n=xe(e+"/fulfilled",(function(e,t,r){return{payload:e,meta:{arg:r,requestId:t,requestStatus:"fulfilled"}}})),o=xe(e+"/pending",(function(e,t){return{payload:void 0,meta:{arg:t,requestId:e,requestStatus:"pending"}}})),i=xe(e+"/rejected",(function(e,t,n){var o=e instanceof Ce,i=!!e&&"AbortError"===e.name,u=!!e&&"ConditionError"===e.name;return{payload:e instanceof Ce?e.payload:void 0,error:(r&&r.serializeError||We)(e||"Rejected"),meta:{arg:n,requestId:t,rejectedWithValue:o,requestStatus:"rejected",aborted:i,condition:u}}})),u="undefined"!=typeof AbortController?AbortController:function(){function e(){this.signal={aborted:!1,addEventListener:function(){},dispatchEvent:function(){return!1},onabort:function(){},removeEventListener:function(){}}}return e.prototype.abort=function(){},e}();return Object.assign((function(e){return function(a,c,f){var l,s=Te(),d=new u,p=new Promise((function(e,t){return d.signal.addEventListener("abort",(function(){return t({name:"AbortError",message:l||"Aborted"})}))})),y=!1,v=function(){try{var u,l=function(e){return v?e:(r&&!r.dispatchConditionRejection&&i.match(u)&&u.meta.condition||a(u),u)},v=!1,h=function(l,v){try{var h=function(){if(r&&r.condition&&!1===r.condition(e,{getState:c,extra:f}))throw{name:"ConditionError",message:"Aborted due to condition callback returning false."};return y=!0,a(o(s,e)),Promise.resolve(Promise.race([p,Promise.resolve(t(e,{dispatch:a,getState:c,extra:f,requestId:s,signal:d.signal,rejectWithValue:function(e){return new Ce(e)}})).then((function(t){return t instanceof Ce?i(t,s,e):n(t,s,e)}))])).then((function(e){u=e}))}()}catch(e){return v(e)}return h&&h.then?h.then(void 0,v):h}(0,(function(t){u=i(t,s,e)}));return Promise.resolve(h&&h.then?h.then(l):l(h))}catch(e){return Promise.reject(e)}}();return Object.assign(v,{abort:function(e){y&&(l=e,d.abort())},requestId:s,arg:e})}}),{pending:o,rejected:i,fulfilled:n,typePrefix:e})},e.createDraftSafeSelector=pe,e.createEntityAdapter=function(e){void 0===e&&(e={});var t=ye({sortComparer:!1,selectId:function(e){return e.id}},e),r=t.selectId,n=t.sortComparer;return ye({selectId:r,sortComparer:n},{getInitialState:function(e){return void 0===e&&(e={}),Object.assign({ids:[],entities:{}},e)}},{},{getSelectors:function(e){var t=function(e){return e.ids},r=function(e){return e.entities},n=pe(t,r,(function(e,t){return e.map((function(e){return t[e]}))})),o=function(e,t){return t},i=function(e,t){return e[t]},u=pe(t,(function(e){return e.length}));if(!e)return{selectIds:t,selectEntities:r,selectAll:n,selectTotal:u,selectById:pe(r,o,i)};var a=pe(e,r);return{selectIds:pe(e,t),selectEntities:a,selectAll:pe(e,n),selectTotal:pe(e,u),selectById:pe(a,o,i)}}},{},n?function(e,t){var r=Ne(e);function n(t,r){Array.isArray(t)||(t=Object.values(t));var n=t.filter((function(t){return!(De(t,e)in r.entities)}));0!==n.length&&u(n,r)}function o(t,r){var n=[];t.forEach((function(t){return function(t,r,n){if(!(r.id in n.entities))return!1;var o=Object.assign({},n.entities[r.id],r.changes),i=De(o,e);return delete n.entities[r.id],t.push(o),i!==r.id}(n,t,r)})),0!==n.length&&u(n,r)}function i(t,r){Array.isArray(t)||(t=Object.values(t));var i=[],u=[],a=t,c=Array.isArray(a),f=0;for(a=c?a:a[Symbol.iterator]();;){var l;if(c){if(f>=a.length)break;l=a[f++]}else{if((f=a.next()).done)break;l=f.value}var s=l,d=De(s,e);d in r.entities?u.push({id:d,changes:s}):i.push(s)}o(u,r),n(i,r)}function u(r,n){r.sort(t),r.forEach((function(t){n.entities[e(t)]=t}));var o=Object.values(n.entities);o.sort(t);var i=o.map(e);(function(e,t){if(e.length!==t.length)return!1;for(var r=0;r<e.length&&r<t.length;r++)if(e[r]!==t[r])return!1;return!0})(n.ids,i)||(n.ids=i)}return{removeOne:r.removeOne,removeMany:r.removeMany,removeAll:r.removeAll,addOne:Re((function(e,t){return n([e],t)})),updateOne:Re((function(e,t){return o([e],t)})),upsertOne:Re((function(e,t){return i([e],t)})),setAll:Re((function(e,t){Array.isArray(e)||(e=Object.values(e)),t.entities={},t.ids=[],n(e,t)})),addMany:Re(n),updateMany:Re(o),upsertMany:Re(i)}}(r,n):Ne(r))},e.createImmutableStateInvariantMiddleware=function(e){return function(){return function(e){return function(t){return e(t)}}}},e.createNextState=$,e.createReducer=ke,e.createSelector=de,e.createSerializableStateInvariantMiddleware=function(e){return function(){return function(e){return function(t){return e(t)}}}},e.createSlice=function(e){var t=e.name,r=e.initialState;if(!t)throw new Error("`name` is a required option for createSlice");var n=e.reducers||{},o=void 0===e.extraReducers?[]:"function"==typeof e.extraReducers?_e(e.extraReducers):[e.extraReducers],i=o[0],u=void 0===i?{}:i,a=o[1],c=void 0===a?[]:a,f=o[2],l=void 0===f?void 0:f,s=Object.keys(n),d={},p={},y={};s.forEach((function(e){var r,o,i=n[e],u=t+"/"+e;"reducer"in i?(r=i.reducer,o=i.prepare):r=i,d[e]=r,p[u]=r,y[e]=o?xe(u,o):xe(u)}));var v=ke(r,ye({},u,{},p),c,l);return{name:t,reducer:v,actions:y,caseReducers:d}},e.createStore=ee,e.current=D,e.findNonSerializableValue=function e(t,r,n,o,i){var u;if(void 0===r&&(r=[]),void 0===n&&(n=Se),void 0===i&&(i=[]),!n(t))return{keyPath:r.join(".")||"<root>",value:t};if("object"!=typeof t||null===t)return!1;var a=null!=o?o(t):Object.entries(t),c=i.length>0,f=a,l=Array.isArray(f),s=0;for(f=l?f:f[Symbol.iterator]();;){var d;if(l){if(s>=f.length)break;d=f[s++]}else{if((s=f.next()).done)break;d=s.value}var p=d[1],y=r.concat(d[0]);if(!(c&&i.indexOf(y.join("."))>=0)){if(!n(p))return{keyPath:y.join("."),value:p};if("object"==typeof p&&(u=e(p,y,n,o,i)))return u}}return!1},e.freeze=p,e.getDefaultMiddleware=Ee,e.getType=function(e){return""+e},e.isAllOf=ze,e.isAnyOf=qe,e.isAsyncThunkAction=function e(){for(var t=arguments.length,r=new Array(t),n=0;n<t;n++)r[n]=arguments[n];return 0===r.length?function(e){return Ke(e,["pending","fulfilled","rejected"])}:Ue(r)?function(e){var t=[],n=r,o=Array.isArray(n),i=0;for(n=o?n:n[Symbol.iterator]();;){var u;if(o){if(i>=n.length)break;u=n[i++]}else{if((i=n.next()).done)break;u=i.value}t.push(u.pending,u.rejected,u.fulfilled)}return qe.apply(void 0,t)(e)}:e()(r[0])},e.isFulfilled=function e(){for(var t=arguments.length,r=new Array(t),n=0;n<t;n++)r[n]=arguments[n];return 0===r.length?function(e){return Ke(e,["fulfilled"])}:Ue(r)?function(e){var t=r.map((function(e){return e.fulfilled}));return qe.apply(void 0,t)(e)}:e()(r[0])},e.isImmutableDefault=function(e){return"object"!=typeof e||null==e},e.isPending=function e(){for(var t=arguments.length,r=new Array(t),n=0;n<t;n++)r[n]=arguments[n];return 0===r.length?function(e){return Ke(e,["pending"])}:Ue(r)?function(e){var t=r.map((function(e){return e.pending}));return qe.apply(void 0,t)(e)}:e()(r[0])},e.isPlain=Se,e.isPlainObject=we,e.isRejected=Ve,e.isRejectedWithValue=function e(){for(var t=arguments.length,r=new Array(t),n=0;n<t;n++)r[n]=arguments[n];var o=function(e){return e&&e.meta&&e.meta.rejectedWithValue};return 0===r.length?function(e){return ze(Ve.apply(void 0,r),o)(e)}:Ue(r)?function(e){return ze(Ve.apply(void 0,r),o)(e)}:e()(r[0])},e.nanoid=Te,e.unwrapResult=function(e){if(e.meta&&e.meta.rejectedWithValue)throw e.payload;if(e.error)throw e.error;return e.payload}}));
-+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e=e||self).RTK={})}(this,(function(e){"use strict";function t(e){for(var t=arguments.length,r=Array(t>1?t-1:0),n=1;n<t;n++)r[n-1]=arguments[n];throw Error("[Immer] minified error nr: "+e+(r.length?" "+r.map((function(e){return"'"+e+"'"})).join(","):"")+". Find the full error at: https://bit.ly/3cXEKWf")}function r(e){return!!e&&!!e[U]}function n(e){return!!e&&(function(e){if(!e||"object"!=typeof e)return!1;var t=Object.getPrototypeOf(e);return!t||t===Object.prototype}(e)||Array.isArray(e)||!!e[K]||!!e.constructor[K]||f(e)||l(e))}function o(e,t,r){void 0===r&&(r=!1),0===i(e)?(r?Object.keys:V)(e).forEach((function(n){r&&"symbol"==typeof n||t(n,e[n],e)})):e.forEach((function(r,n){return t(n,r,e)}))}function i(e){var t=e[U];return t?t.i>3?t.i-4:t.i:Array.isArray(e)?1:f(e)?2:l(e)?3:0}function u(e,t){return 2===i(e)?e.has(t):Object.prototype.hasOwnProperty.call(e,t)}function a(e,t,r){var n=i(e);2===n?e.set(t,r):3===n?(e.delete(t),e.add(r)):e[t]=r}function c(e,t){return e===t?0!==e||1/e==1/t:e!=e&&t!=t}function f(e){return W&&e instanceof Map}function l(e){return F&&e instanceof Set}function s(e){return e.o||e.t}function d(e){if(Array.isArray(e))return Array.prototype.slice.call(e);var t=L(e);delete t[U];for(var r=V(t),n=0;n<r.length;n++){var o=r[n],i=t[o];!1===i.writable&&(i.writable=!0,i.configurable=!0),(i.get||i.set)&&(t[o]={configurable:!0,writable:!0,enumerable:i.enumerable,value:e[o]})}return Object.create(Object.getPrototypeOf(e),t)}function p(e,t){return void 0===t&&(t=!1),v(e)||r(e)||!n(e)?e:(i(e)>1&&(e.set=e.add=e.clear=e.delete=y),Object.freeze(e),t&&o(e,(function(e,t){return p(t,!0)}),!0),e)}function y(){t(2)}function v(e){return null==e||"object"!=typeof e||Object.isFrozen(e)}function h(e){var r=B[e];return r||t(18,e),r}function b(){return M}function g(e,t){t&&(h("Patches"),e.u=[],e.s=[],e.v=t)}function m(e){O(e),e.p.forEach(j),e.p=null}function O(e){e===M&&(M=e.l)}function w(e){return M={p:[],l:M,h:e,m:!0,_:0}}function j(e){var t=e[U];0===t.i||1===t.i?t.j():t.g=!0}function A(e,r){r._=r.p.length;var o=r.p[0],i=void 0!==e&&e!==o;return r.h.O||h("ES5").S(r,e,i),i?(o[U].P&&(m(r),t(4)),n(e)&&(e=P(r,e),r.l||E(r,e)),r.u&&h("Patches").M(o[U],e,r.u,r.s)):e=P(r,o,[]),m(r),r.u&&r.v(r.u,r.s),e!==z?e:void 0}function P(e,t,r){if(v(t))return t;var n=t[U];if(!n)return o(t,(function(o,i){return S(e,n,t,o,i,r)}),!0),t;if(n.A!==e)return t;if(!n.P)return E(e,n.t,!0),n.t;if(!n.I){n.I=!0,n.A._--;var i=4===n.i||5===n.i?n.o=d(n.k):n.o;o(3===n.i?new Set(i):i,(function(t,o){return S(e,n,i,t,o,r)})),E(e,i,!1),r&&e.u&&h("Patches").R(n,r,e.u,e.s)}return n.o}function S(e,t,o,i,c,f){if(r(c)){var l=P(e,c,f&&t&&3!==t.i&&!u(t.D,i)?f.concat(i):void 0);if(a(o,i,l),!r(l))return;e.m=!1}if(n(c)&&!v(c)){if(!e.h.N&&e._<1)return;P(e,c),t&&t.A.l||E(e,c)}}function E(e,t,r){void 0===r&&(r=!1),e.h.N&&e.m&&p(t,r)}function x(e,t){var r=e[U];return(r?s(r):e)[t]}function I(e,t){if(t in e)for(var r=Object.getPrototypeOf(e);r;){var n=Object.getOwnPropertyDescriptor(r,t);if(n)return n;r=Object.getPrototypeOf(r)}}function _(e){e.P||(e.P=!0,e.l&&_(e.l))}function k(e){e.o||(e.o=d(e.t))}function R(e,t,r){var n=f(t)?h("MapSet").T(t,r):l(t)?h("MapSet").F(t,r):e.O?function(e,t){var r=Array.isArray(e),n={i:r?1:0,A:t?t.A:b(),P:!1,I:!1,D:{},l:t,t:e,k:null,o:null,j:null,C:!1},o=n,i=X;r&&(o=[n],i=Y);var u=Proxy.revocable(o,i),a=u.revoke,c=u.proxy;return n.k=c,n.j=a,c}(t,r):h("ES5").J(t,r);return(r?r.A:b()).p.push(n),n}function D(e){return r(e)||t(22,e),function e(t){if(!n(t))return t;var r,u=t[U],c=i(t);if(u){if(!u.P&&(u.i<4||!h("ES5").K(u)))return u.t;u.I=!0,r=N(t,c),u.I=!1}else r=N(t,c);return o(r,(function(t,n){u&&function(e,t){return 2===i(e)?e.get(t):e[t]}(u.t,t)===n||a(r,t,e(n))})),3===c?new Set(r):r}(e)}function N(e,t){switch(t){case 2:return new Map(e);case 3:return Array.from(e)}return d(e)}var T,M,C="undefined"!=typeof Symbol&&"symbol"==typeof Symbol("x"),W="undefined"!=typeof Map,F="undefined"!=typeof Set,q="undefined"!=typeof Proxy&&void 0!==Proxy.revocable&&"undefined"!=typeof Reflect,z=C?Symbol.for("immer-nothing"):((T={})["immer-nothing"]=!0,T),K=C?Symbol.for("immer-draftable"):"__$immer_draftable",U=C?Symbol.for("immer-state"):"__$immer_state",V="undefined"!=typeof Reflect&&Reflect.ownKeys?Reflect.ownKeys:void 0!==Object.getOwnPropertySymbols?function(e){return Object.getOwnPropertyNames(e).concat(Object.getOwnPropertySymbols(e))}:Object.getOwnPropertyNames,L=Object.getOwnPropertyDescriptors||function(e){var t={};return V(e).forEach((function(r){t[r]=Object.getOwnPropertyDescriptor(e,r)})),t},B={},X={get:function(e,t){if(t===U)return e;var r=s(e);if(!u(r,t))return function(e,t,r){var n,o=I(t,r);return o?"value"in o?o.value:null===(n=o.get)||void 0===n?void 0:n.call(e.k):void 0}(e,r,t);var o=r[t];return e.I||!n(o)?o:o===x(e.t,t)?(k(e),e.o[t]=R(e.A.h,o,e)):o},has:function(e,t){return t in s(e)},ownKeys:function(e){return Reflect.ownKeys(s(e))},set:function(e,t,r){var n=I(s(e),t);if(null==n?void 0:n.set)return n.set.call(e.k,r),!0;if(!e.P){var o=x(s(e),t),i=null==o?void 0:o[U];if(i&&i.t===r)return e.o[t]=r,e.D[t]=!1,!0;if(c(r,o)&&(void 0!==r||u(e.t,t)))return!0;k(e),_(e)}return e.o[t]=r,e.D[t]=!0,!0},deleteProperty:function(e,t){return void 0!==x(e.t,t)||t in e.t?(e.D[t]=!1,k(e),_(e)):delete e.D[t],e.o&&delete e.o[t],!0},getOwnPropertyDescriptor:function(e,t){var r=s(e),n=Reflect.getOwnPropertyDescriptor(r,t);return n?{writable:!0,configurable:1!==e.i||"length"!==t,enumerable:n.enumerable,value:r[t]}:n},defineProperty:function(){t(11)},getPrototypeOf:function(e){return Object.getPrototypeOf(e.t)},setPrototypeOf:function(){t(12)}},Y={};o(X,(function(e,t){Y[e]=function(){return arguments[0]=arguments[0][0],t.apply(this,arguments)}})),Y.deleteProperty=function(e,t){return X.deleteProperty.call(this,e[0],t)},Y.set=function(e,t,r){return X.set.call(this,e[0],t,r,e[0])};var J=new(function(){function e(e){this.O=q,this.N=!0,"boolean"==typeof(null==e?void 0:e.useProxies)&&this.setUseProxies(e.useProxies),"boolean"==typeof(null==e?void 0:e.autoFreeze)&&this.setAutoFreeze(e.autoFreeze),this.produce=this.produce.bind(this),this.produceWithPatches=this.produceWithPatches.bind(this)}var o=e.prototype;return o.produce=function(e,r,o){if("function"==typeof e&&"function"!=typeof r){var i=r;r=e;var u=this;return function(e){var t=this;void 0===e&&(e=i);for(var n=arguments.length,o=Array(n>1?n-1:0),a=1;a<n;a++)o[a-1]=arguments[a];return u.produce(e,(function(e){var n;return(n=r).call.apply(n,[t,e].concat(o))}))}}var a;if("function"!=typeof r&&t(6),void 0!==o&&"function"!=typeof o&&t(7),n(e)){var c=w(this),f=R(this,e,void 0),l=!0;try{a=r(f),l=!1}finally{l?m(c):O(c)}return"undefined"!=typeof Promise&&a instanceof Promise?a.then((function(e){return g(c,o),A(e,c)}),(function(e){throw m(c),e})):(g(c,o),A(a,c))}if(!e||"object"!=typeof e){if((a=r(e))===z)return;return void 0===a&&(a=e),this.N&&p(a,!0),a}t(21,e)},o.produceWithPatches=function(e,t){var r,n,o=this;return"function"==typeof e?function(t){for(var r=arguments.length,n=Array(r>1?r-1:0),i=1;i<r;i++)n[i-1]=arguments[i];return o.produceWithPatches(t,(function(t){return e.apply(void 0,[t].concat(n))}))}:[this.produce(e,t,(function(e,t){r=e,n=t})),r,n]},o.createDraft=function(e){n(e)||t(8),r(e)&&(e=D(e));var o=w(this),i=R(this,e,void 0);return i[U].C=!0,O(o),i},o.finishDraft=function(e,t){var r=(e&&e[U]).A;return g(r,t),A(void 0,r)},o.setAutoFreeze=function(e){this.N=e},o.setUseProxies=function(e){e&&!q&&t(20),this.O=e},o.applyPatches=function(e,t){var n;for(n=t.length-1;n>=0;n--){var o=t[n];if(0===o.path.length&&"replace"===o.op){e=o.value;break}}var i=h("Patches").$;return r(e)?i(e,t):this.produce(e,(function(e){return i(e,t.slice(n+1))}))},e}()),$=J.produce;J.produceWithPatches.bind(J),J.setAutoFreeze.bind(J),J.setUseProxies.bind(J),J.applyPatches.bind(J),J.createDraft.bind(J),J.finishDraft.bind(J);var G=function(e){var t,r=("undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof module?module:Function("return this")()).Symbol;return"function"==typeof r?r.observable?t=r.observable:(t=r("observable"),r.observable=t):t="@@observable",t}(),H=function(){return Math.random().toString(36).substring(7).split("").join(".")},Q={INIT:"@@redux/INIT"+H(),REPLACE:"@@redux/REPLACE"+H(),PROBE_UNKNOWN_ACTION:function(){return"@@redux/PROBE_UNKNOWN_ACTION"+H()}};function Z(e){if("object"!=typeof e||null===e)return!1;for(var t=e;null!==Object.getPrototypeOf(t);)t=Object.getPrototypeOf(t);return Object.getPrototypeOf(e)===t}function ee(e,t,r){var n;if("function"==typeof t&&"function"==typeof r||"function"==typeof r&&"function"==typeof arguments[3])throw new Error("It looks like you are passing several store enhancers to createStore(). This is not supported. Instead, compose them together to a single function.");if("function"==typeof t&&void 0===r&&(r=t,t=void 0),void 0!==r){if("function"!=typeof r)throw new Error("Expected the enhancer to be a function.");return r(ee)(e,t)}if("function"!=typeof e)throw new Error("Expected the reducer to be a function.");var o=e,i=t,u=[],a=u,c=!1;function f(){a===u&&(a=u.slice())}function l(){if(c)throw new Error("You may not call store.getState() while the reducer is executing. The reducer has already received the state as an argument. Pass it down from the top reducer instead of reading it from the store.");return i}function s(e){if("function"!=typeof e)throw new Error("Expected the listener to be a function.");if(c)throw new Error("You may not call store.subscribe() while the reducer is executing. If you would like to be notified after the store has been updated, subscribe from a component and invoke store.getState() in the callback to access the latest state. See https://redux.js.org/api-reference/store#subscribelistener for more details.");var t=!0;return f(),a.push(e),function(){if(t){if(c)throw new Error("You may not unsubscribe from a store listener while the reducer is executing. See https://redux.js.org/api-reference/store#subscribelistener for more details.");t=!1,f();var r=a.indexOf(e);a.splice(r,1),u=null}}}function d(e){if(!Z(e))throw new Error("Actions must be plain objects. Use custom middleware for async actions.");if(void 0===e.type)throw new Error('Actions may not have an undefined "type" property. Have you misspelled a constant?');if(c)throw new Error("Reducers may not dispatch actions.");try{c=!0,i=o(i,e)}finally{c=!1}for(var t=u=a,r=0;r<t.length;r++)(0,t[r])();return e}function p(e){if("function"!=typeof e)throw new Error("Expected the nextReducer to be a function.");o=e,d({type:Q.REPLACE})}function y(){var e,t=s;return(e={subscribe:function(e){if("object"!=typeof e||null===e)throw new TypeError("Expected the observer to be an object.");function r(){e.next&&e.next(l())}return r(),{unsubscribe:t(r)}}})[G]=function(){return this},e}return d({type:Q.INIT}),(n={dispatch:d,subscribe:s,getState:l,replaceReducer:p})[G]=y,n}function te(e,t){var r=t&&t.type;return"Given "+(r&&'action "'+String(r)+'"'||"an action")+', reducer "'+e+'" returned undefined. To ignore an action, you must explicitly return the previous state. If you want this reducer to hold no value, you can return null instead of undefined.'}function re(e){for(var t=Object.keys(e),r={},n=0;n<t.length;n++){var o=t[n];"function"==typeof e[o]&&(r[o]=e[o])}var i,u=Object.keys(r);try{!function(e){Object.keys(e).forEach((function(t){var r=e[t];if(void 0===r(void 0,{type:Q.INIT}))throw new Error('Reducer "'+t+"\" returned undefined during initialization. If the state passed to the reducer is undefined, you must explicitly return the initial state. The initial state may not be undefined. If you don't want to set a value for this reducer, you can use null instead of undefined.");if(void 0===r(void 0,{type:Q.PROBE_UNKNOWN_ACTION()}))throw new Error('Reducer "'+t+"\" returned undefined when probed with a random type. Don't try to handle "+Q.INIT+' or other actions in "redux/*" namespace. They are considered private. Instead, you must return the current state for any unknown actions, unless it is undefined, in which case you must return the initial state, regardless of the action type. The initial state may not be undefined, but can be null.')}))}(r)}catch(e){i=e}return function(e,t){if(void 0===e&&(e={}),i)throw i;for(var n=!1,o={},a=0;a<u.length;a++){var c=u[a],f=e[c],l=(0,r[c])(f,t);if(void 0===l){var s=te(c,t);throw new Error(s)}o[c]=l,n=n||l!==f}return(n=n||u.length!==Object.keys(e).length)?o:e}}function ne(e,t){return function(){return t(e.apply(this,arguments))}}function oe(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function ie(e,t){var r=Object.keys(e);return Object.getOwnPropertySymbols&&r.push.apply(r,Object.getOwnPropertySymbols(e)),t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r}function ue(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?ie(r,!0).forEach((function(t){oe(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):ie(r).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}function ae(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return 0===t.length?function(e){return e}:1===t.length?t[0]:t.reduce((function(e,t){return function(){return e(t.apply(void 0,arguments))}}))}function ce(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return function(e){return function(){var r=e.apply(void 0,arguments),n=function(){throw new Error("Dispatching while constructing your middleware is not allowed. Other middleware would not be applied to this dispatch.")},o={getState:r.getState,dispatch:function(){return n.apply(void 0,arguments)}},i=t.map((function(e){return e(o)}));return ue({},r,{dispatch:n=ae.apply(void 0,i)(r.dispatch)})}}}function fe(e,t){return e===t}function le(e,t,r){if(null===t||null===r||t.length!==r.length)return!1;for(var n=t.length,o=0;o<n;o++)if(!e(t[o],r[o]))return!1;return!0}function se(e){var t=Array.isArray(e[0])?e[0]:e;if(!t.every((function(e){return"function"==typeof e}))){var r=t.map((function(e){return typeof e})).join(", ");throw new Error("Selector creators expect all input-selectors to be functions, instead received the following types: ["+r+"]")}return t}var de=function(e){for(var t=arguments.length,r=Array(t>1?t-1:0),n=1;n<t;n++)r[n-1]=arguments[n];return function(){for(var t=arguments.length,n=Array(t),o=0;o<t;o++)n[o]=arguments[o];var i=0,u=n.pop(),a=se(n),c=e.apply(void 0,[function(){return i++,u.apply(null,arguments)}].concat(r)),f=e((function(){for(var e=[],t=a.length,r=0;r<t;r++)e.push(a[r].apply(null,arguments));return c.apply(null,e)}));return f.resultFunc=u,f.dependencies=a,f.recomputations=function(){return i},f.resetRecomputations=function(){return i=0},f}}((function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:fe,r=null,n=null;return function(){return le(t,r,arguments)||(n=e.apply(null,arguments)),r=arguments,n}})),pe=function(){var e=de.apply(void 0,arguments),t=function(t){for(var n=arguments.length,o=new Array(n>1?n-1:0),i=1;i<n;i++)o[i-1]=arguments[i];return e.apply(void 0,[r(t)?D(t):t].concat(o))};return t};function ye(){return(ye=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e}).apply(this,arguments)}function ve(e){return(ve=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function he(e,t){return(he=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function be(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(e){return!1}}function ge(e,t,r){return(ge=be()?Reflect.construct:function(e,t,r){var n=[null];n.push.apply(n,t);var o=new(Function.bind.apply(e,n));return r&&he(o,r.prototype),o}).apply(null,arguments)}function me(e){var t="function"==typeof Map?new Map:void 0;return(me=function(e){if(null===e||-1===Function.toString.call(e).indexOf("[native code]"))return e;if("function"!=typeof e)throw new TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,r)}function r(){return ge(e,arguments,ve(this).constructor)}return r.prototype=Object.create(e.prototype,{constructor:{value:r,enumerable:!1,writable:!0,configurable:!0}}),he(r,e)})(e)}var Oe="undefined"!=typeof window&&window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__?window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__:function(){if(0!==arguments.length)return"object"==typeof arguments[0]?ae:ae.apply(null,arguments)};function we(e){if("object"!=typeof e||null===e)return!1;for(var t=e;null!==Object.getPrototypeOf(t);)t=Object.getPrototypeOf(t);return Object.getPrototypeOf(e)===t}function je(e){return function(t){var r=t.dispatch,n=t.getState;return function(t){return function(o){return"function"==typeof o?o(r,n,e):t(o)}}}}var Ae=je();Ae.withExtraArgument=je;var Pe=function(e){var t,r;function n(){return e.apply(this,arguments)||this}r=e,(t=n).prototype=Object.create(r.prototype),t.prototype.constructor=t,t.__proto__=r;var o=n.prototype;return Object.defineProperty(o,'concat',{value:function(){for(var t,r=arguments.length,o=new Array(r),i=0;i<r;i++)o[i]=arguments[i];return l(n,(t=e.prototype.concat).call.apply(t,[this].concat(o)))}}),o.prepend=function(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return 1===t.length&&Array.isArray(t[0])?ge(n,t[0].concat(this)):ge(n,t.concat(this))},n}(me(Array));function Se(e){return null==e||"string"==typeof e||"boolean"==typeof e||"number"==typeof e||Array.isArray(e)||we(e)}function Ee(e){void 0===e&&(e={});var t=e.thunk,r=void 0===t||t,n=new Pe;return r&&(function(e){return"boolean"==typeof e}(r)?n.push(Ae):n.push(Ae.withExtraArgument(r.extraArgument))),n}function xe(e,t){function r(){if(t){var r=t.apply(void 0,arguments);if(!r)throw new Error("prepareAction did not return an object");return ye({type:e,payload:r.payload},"meta"in r&&{meta:r.meta},{},"error"in r&&{error:r.error})}return{type:e,payload:arguments.length<=0?void 0:arguments[0]}}return r.toString=function(){return""+e},r.type=e,r.match=function(t){return t.type===e},r}function Ie(e){return["type","payload","error","meta"].indexOf(e)>-1}function _e(e){var t,r={},n=[],o={addCase:function(e,t){var n="string"==typeof e?e:e.type;if(n in r)throw new Error("addCase cannot be called with two reducers for the same action type");return r[n]=t,o},addMatcher:function(e,t){return n.push({matcher:e,reducer:t}),o},addDefaultCase:function(e){return t=e,o}};return e(o),[r,n,t]}function ke(e,t,o,i){void 0===o&&(o=[]);var u="function"==typeof t?_e(t):[t,o,i],a=u[0],c=u[1],f=u[2];return function(t,o){void 0===t&&(t=e);var i=[a[o.type]].concat(c.filter((function(e){return(0,e.matcher)(o)})).map((function(e){return e.reducer})));return 0===i.filter((function(e){return!!e})).length&&(i=[f]),i.reduce((function(e,t){if(t){if(r(e)){var i=t(e,o);return void 0===i?e:i}if(n(e))return $(e,(function(e){return t(e,o)}));var u=t(e,o);if(void 0===u){if(null===e)return e;throw Error("A case reducer on a non-draftable value must not return undefined")}return u}return e}),t)}}function Re(e){return function(t,n){var o=function(t){!function(e){return we(t=e)&&"string"==typeof t.type&&Object.keys(t).every(Ie);var t}(n)?e(n,t):e(n.payload,t)};return r(t)?(o(t),t):$(t,o)}}function De(e,t){return t(e)}function Ne(e){function t(t,r){var n=De(t,e);n in r.entities||(r.ids.push(n),r.entities[n]=t)}function r(e,r){Array.isArray(e)||(e=Object.values(e));var n=e,o=Array.isArray(n),i=0;for(n=o?n:n[Symbol.iterator]();;){var u;if(o){if(i>=n.length)break;u=n[i++]}else{if((i=n.next()).done)break;u=i.value}t(u,r)}}function n(e,t){var r=!1;e.forEach((function(e){e in t.entities&&(delete t.entities[e],r=!0)})),r&&(t.ids=t.ids.filter((function(e){return e in t.entities})))}function o(t,r){var n={},o={};t.forEach((function(e){e.id in r.entities&&(o[e.id]={id:e.id,changes:ye({},o[e.id]?o[e.id].changes:null,{},e.changes)})})),(t=Object.values(o)).length>0&&t.filter((function(t){return function(t,r,n){var o=Object.assign({},n.entities[r.id],r.changes),i=De(o,e),u=i!==r.id;return u&&(t[r.id]=i,delete n.entities[r.id]),n.entities[i]=o,u}(n,t,r)})).length>0&&(r.ids=r.ids.map((function(e){return n[e]||e})))}function i(t,n){Array.isArray(t)||(t=Object.values(t));var i=[],u=[],a=t,c=Array.isArray(a),f=0;for(a=c?a:a[Symbol.iterator]();;){var l;if(c){if(f>=a.length)break;l=a[f++]}else{if((f=a.next()).done)break;l=f.value}var s=l,d=De(s,e);d in n.entities?u.push({id:d,changes:s}):i.push(s)}o(u,n),r(i,n)}return{removeAll:(u=function(e){Object.assign(e,{ids:[],entities:{}})},a=Re((function(e,t){return u(t)})),function(e){return a(e,void 0)}),addOne:Re(t),addMany:Re(r),setAll:Re((function(e,t){Array.isArray(e)||(e=Object.values(e)),t.ids=[],t.entities={},r(e,t)})),updateOne:Re((function(e,t){return o([e],t)})),updateMany:Re(o),upsertOne:Re((function(e,t){return i([e],t)})),upsertMany:Re(i),removeOne:Re((function(e,t){return n([e],t)})),removeMany:Re(n)};var u,a}"undefined"!=typeof Symbol&&(Symbol.iterator||(Symbol.iterator=Symbol("Symbol.iterator"))),"undefined"!=typeof Symbol&&(Symbol.asyncIterator||(Symbol.asyncIterator=Symbol("Symbol.asyncIterator")));var Te=function(e){void 0===e&&(e=21);for(var t="",r=e;r--;)t+="ModuleSymbhasOwnPr-0123456789ABCDEFGHNRVfgctiUvz_KqYTJkLxpZXIjQW"[64*Math.random()|0];return t},Me=["name","message","stack","code"],Ce=function(e){this.payload=e,this.name="RejectWithValue",this.message="Rejected"},We=function(e){if("object"==typeof e&&null!==e){var t={},r=Me,n=Array.isArray(r),o=0;for(r=n?r:r[Symbol.iterator]();;){var i;if(n){if(o>=r.length)break;i=r[o++]}else{if((o=r.next()).done)break;i=o.value}"string"==typeof e[i]&&(t[i]=e[i])}return t}return{message:String(e)}},Fe=function(e,t){return function(e){return e&&"function"==typeof e.match}(e)?e.match(t):e(t)};function qe(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return function(e){return t.some((function(t){return Fe(t,e)}))}}function ze(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return function(e){return t.every((function(t){return Fe(t,e)}))}}function Ke(e,t){if(!e||!e.meta)return!1;var r="string"==typeof e.meta.requestId,n=t.indexOf(e.meta.requestStatus)>-1;return r&&n}function Ue(e){return"function"==typeof e[0]&&"pending"in e[0]&&"fulfilled"in e[0]&&"rejected"in e[0]}function Ve(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return 0===t.length?function(e){return Ke(e,["rejected"])}:Ue(t)?function(e){var r=t.map((function(e){return e.rejected}));return qe.apply(void 0,r)(e)}:Ve()(t[0])}!function(){function e(e,t){var r=a[e];return r?r.enumerable=t:a[e]=r={configurable:!0,enumerable:t,get:function(){return X.get(this[U],e)},set:function(t){X.set(this[U],e,t)}},r}function t(e){for(var t=e.length-1;t>=0;t--){var r=e[t][U];if(!r.P)switch(r.i){case 5:i(r)&&_(r);break;case 4:n(r)&&_(r)}}}function n(e){for(var t=e.t,r=e.k,n=V(r),o=n.length-1;o>=0;o--){var i=n[o];if(i!==U){var a=t[i];if(void 0===a&&!u(t,i))return!0;var f=r[i],l=f&&f[U];if(l?l.t!==a:!c(f,a))return!0}}var s=!!t[U];return n.length!==V(t).length+(s?0:1)}function i(e){var t=e.k;if(t.length!==e.t.length)return!0;var r=Object.getOwnPropertyDescriptor(t,t.length-1);return!(!r||r.get)}var a={};!function(e,t){B.ES5||(B.ES5=t)}(0,{J:function(t,r){var n=Array.isArray(t),o=function(t,r){if(t){for(var n=Array(r.length),o=0;o<r.length;o++)Object.defineProperty(n,""+o,e(o,!0));return n}var i=L(r);delete i[U];for(var u=V(i),a=0;a<u.length;a++){var c=u[a];i[c]=e(c,t||!!i[c].enumerable)}return Object.create(Object.getPrototypeOf(r),i)}(n,t),i={i:n?5:4,A:r?r.A:b(),P:!1,I:!1,D:{},l:r,t:t,k:o,o:null,g:!1,C:!1};return Object.defineProperty(o,U,{value:i,writable:!0}),o},S:function(e,n,a){a?r(n)&&n[U].A===e&&t(e.p):(e.u&&function e(t){if(t&&"object"==typeof t){var r=t[U];if(r){var n=r.t,a=r.k,c=r.D,f=r.i;if(4===f)o(a,(function(t){t!==U&&(void 0!==n[t]||u(n,t)?c[t]||e(a[t]):(c[t]=!0,_(r)))})),o(n,(function(e){void 0!==a[e]||u(a,e)||(c[e]=!1,_(r))}));else if(5===f){if(i(r)&&(_(r),c.length=!0),a.length<n.length)for(var l=a.length;l<n.length;l++)c[l]=!1;else for(var s=n.length;s<a.length;s++)c[s]=!0;for(var d=Math.min(a.length,n.length),p=0;p<d;p++)void 0===c[p]&&e(a[p])}}}}(e.p[0]),t(e.p))},K:function(e){return 4===e.i?n(e):i(e)}})}(),e.MiddlewareArray=Pe,e.__DO_NOT_USE__ActionTypes=Q,e.applyMiddleware=ce,e.bindActionCreators=function(e,t){if("function"==typeof e)return ne(e,t);if("object"!=typeof e||null===e)throw new Error("bindActionCreators expected an object or a function, instead received "+(null===e?"null":typeof e)+'. Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?');var r={};for(var n in e){var o=e[n];"function"==typeof o&&(r[n]=ne(o,t))}return r},e.combineReducers=re,e.compose=ae,e.configureStore=function(e){var t,r=function(e){return Ee(e)},n=e||{},o=n.reducer,i=void 0===o?void 0:o,u=n.middleware,a=void 0===u?r():u,c=n.devTools,f=void 0===c||c,l=n.preloadedState,s=void 0===l?void 0:l,d=n.enhancers,p=void 0===d?void 0:d;if("function"==typeof i)t=i;else{if(!we(i))throw new Error('"reducer" is a required argument, and must be a function or an object of functions that can be passed to combineReducers');t=re(i)}var y=ce.apply(void 0,"function"==typeof a?a(r):a),v=ae;f&&(v=Oe(ye({trace:!1},"object"==typeof f&&f)));var h=[y];return Array.isArray(p)?h=[y].concat(p):"function"==typeof p&&(h=p(h)),ee(t,s,v.apply(void 0,h))},e.createAction=xe,e.createAsyncThunk=function(e,t,r){var n=xe(e+"/fulfilled",(function(e,t,r){return{payload:e,meta:{arg:r,requestId:t,requestStatus:"fulfilled"}}})),o=xe(e+"/pending",(function(e,t){return{payload:void 0,meta:{arg:t,requestId:e,requestStatus:"pending"}}})),i=xe(e+"/rejected",(function(e,t,n){var o=e instanceof Ce,i=!!e&&"AbortError"===e.name,u=!!e&&"ConditionError"===e.name;return{payload:e instanceof Ce?e.payload:void 0,error:(r&&r.serializeError||We)(e||"Rejected"),meta:{arg:n,requestId:t,rejectedWithValue:o,requestStatus:"rejected",aborted:i,condition:u}}})),u="undefined"!=typeof AbortController?AbortController:function(){function e(){this.signal={aborted:!1,addEventListener:function(){},dispatchEvent:function(){return!1},onabort:function(){},removeEventListener:function(){}}}return e.prototype.abort=function(){},e}();return Object.assign((function(e){return function(a,c,f){var l,s=Te(),d=new u,p=new Promise((function(e,t){return d.signal.addEventListener("abort",(function(){return t({name:"AbortError",message:l||"Aborted"})}))})),y=!1,v=function(){try{var u,l=function(e){return v?e:(r&&!r.dispatchConditionRejection&&i.match(u)&&u.meta.condition||a(u),u)},v=!1,h=function(l,v){try{var h=function(){if(r&&r.condition&&!1===r.condition(e,{getState:c,extra:f}))throw{name:"ConditionError",message:"Aborted due to condition callback returning false."};return y=!0,a(o(s,e)),Promise.resolve(Promise.race([p,Promise.resolve(t(e,{dispatch:a,getState:c,extra:f,requestId:s,signal:d.signal,rejectWithValue:function(e){return new Ce(e)}})).then((function(t){return t instanceof Ce?i(t,s,e):n(t,s,e)}))])).then((function(e){u=e}))}()}catch(e){return v(e)}return h&&h.then?h.then(void 0,v):h}(0,(function(t){u=i(t,s,e)}));return Promise.resolve(h&&h.then?h.then(l):l(h))}catch(e){return Promise.reject(e)}}();return Object.assign(v,{abort:function(e){y&&(l=e,d.abort())},requestId:s,arg:e})}}),{pending:o,rejected:i,fulfilled:n,typePrefix:e})},e.createDraftSafeSelector=pe,e.createEntityAdapter=function(e){void 0===e&&(e={});var t=ye({sortComparer:!1,selectId:function(e){return e.id}},e),r=t.selectId,n=t.sortComparer;return ye({selectId:r,sortComparer:n},{getInitialState:function(e){return void 0===e&&(e={}),Object.assign({ids:[],entities:{}},e)}},{},{getSelectors:function(e){var t=function(e){return e.ids},r=function(e){return e.entities},n=pe(t,r,(function(e,t){return e.map((function(e){return t[e]}))})),o=function(e,t){return t},i=function(e,t){return e[t]},u=pe(t,(function(e){return e.length}));if(!e)return{selectIds:t,selectEntities:r,selectAll:n,selectTotal:u,selectById:pe(r,o,i)};var a=pe(e,r);return{selectIds:pe(e,t),selectEntities:a,selectAll:pe(e,n),selectTotal:pe(e,u),selectById:pe(a,o,i)}}},{},n?function(e,t){var r=Ne(e);function n(t,r){Array.isArray(t)||(t=Object.values(t));var n=t.filter((function(t){return!(De(t,e)in r.entities)}));0!==n.length&&u(n,r)}function o(t,r){var n=[];t.forEach((function(t){return function(t,r,n){if(!(r.id in n.entities))return!1;var o=Object.assign({},n.entities[r.id],r.changes),i=De(o,e);return delete n.entities[r.id],t.push(o),i!==r.id}(n,t,r)})),0!==n.length&&u(n,r)}function i(t,r){Array.isArray(t)||(t=Object.values(t));var i=[],u=[],a=t,c=Array.isArray(a),f=0;for(a=c?a:a[Symbol.iterator]();;){var l;if(c){if(f>=a.length)break;l=a[f++]}else{if((f=a.next()).done)break;l=f.value}var s=l,d=De(s,e);d in r.entities?u.push({id:d,changes:s}):i.push(s)}o(u,r),n(i,r)}function u(r,n){r.sort(t),r.forEach((function(t){n.entities[e(t)]=t}));var o=Object.values(n.entities);o.sort(t);var i=o.map(e);(function(e,t){if(e.length!==t.length)return!1;for(var r=0;r<e.length&&r<t.length;r++)if(e[r]!==t[r])return!1;return!0})(n.ids,i)||(n.ids=i)}return{removeOne:r.removeOne,removeMany:r.removeMany,removeAll:r.removeAll,addOne:Re((function(e,t){return n([e],t)})),updateOne:Re((function(e,t){return o([e],t)})),upsertOne:Re((function(e,t){return i([e],t)})),setAll:Re((function(e,t){Array.isArray(e)||(e=Object.values(e)),t.entities={},t.ids=[],n(e,t)})),addMany:Re(n),updateMany:Re(o),upsertMany:Re(i)}}(r,n):Ne(r))},e.createImmutableStateInvariantMiddleware=function(e){return function(){return function(e){return function(t){return e(t)}}}},e.createNextState=$,e.createReducer=ke,e.createSelector=de,e.createSerializableStateInvariantMiddleware=function(e){return function(){return function(e){return function(t){return e(t)}}}},e.createSlice=function(e){var t=e.name,r=e.initialState;if(!t)throw new Error("`name` is a required option for createSlice");var n=e.reducers||{},o=void 0===e.extraReducers?[]:"function"==typeof e.extraReducers?_e(e.extraReducers):[e.extraReducers],i=o[0],u=void 0===i?{}:i,a=o[1],c=void 0===a?[]:a,f=o[2],l=void 0===f?void 0:f,s=Object.keys(n),d={},p={},y={};s.forEach((function(e){var r,o,i=n[e],u=t+"/"+e;"reducer"in i?(r=i.reducer,o=i.prepare):r=i,d[e]=r,p[u]=r,y[e]=o?xe(u,o):xe(u)}));var v=ke(r,ye({},u,{},p),c,l);return{name:t,reducer:v,actions:y,caseReducers:d}},e.createStore=ee,e.current=D,e.findNonSerializableValue=function e(t,r,n,o,i){var u;if(void 0===r&&(r=[]),void 0===n&&(n=Se),void 0===i&&(i=[]),!n(t))return{keyPath:r.join(".")||"<root>",value:t};if("object"!=typeof t||null===t)return!1;var a=null!=o?o(t):Object.entries(t),c=i.length>0,f=a,l=Array.isArray(f),s=0;for(f=l?f:f[Symbol.iterator]();;){var d;if(l){if(s>=f.length)break;d=f[s++]}else{if((s=f.next()).done)break;d=s.value}var p=d[1],y=r.concat(d[0]);if(!(c&&i.indexOf(y.join("."))>=0)){if(!n(p))return{keyPath:y.join("."),value:p};if("object"==typeof p&&(u=e(p,y,n,o,i)))return u}}return!1},e.freeze=p,e.getDefaultMiddleware=Ee,e.getType=function(e){return""+e},e.isAllOf=ze,e.isAnyOf=qe,e.isAsyncThunkAction=function e(){for(var t=arguments.length,r=new Array(t),n=0;n<t;n++)r[n]=arguments[n];return 0===r.length?function(e){return Ke(e,["pending","fulfilled","rejected"])}:Ue(r)?function(e){var t=[],n=r,o=Array.isArray(n),i=0;for(n=o?n:n[Symbol.iterator]();;){var u;if(o){if(i>=n.length)break;u=n[i++]}else{if((i=n.next()).done)break;u=i.value}t.push(u.pending,u.rejected,u.fulfilled)}return qe.apply(void 0,t)(e)}:e()(r[0])},e.isFulfilled=function e(){for(var t=arguments.length,r=new Array(t),n=0;n<t;n++)r[n]=arguments[n];return 0===r.length?function(e){return Ke(e,["fulfilled"])}:Ue(r)?function(e){var t=r.map((function(e){return e.fulfilled}));return qe.apply(void 0,t)(e)}:e()(r[0])},e.isImmutableDefault=function(e){return"object"!=typeof e||null==e},e.isPending=function e(){for(var t=arguments.length,r=new Array(t),n=0;n<t;n++)r[n]=arguments[n];return 0===r.length?function(e){return Ke(e,["pending"])}:Ue(r)?function(e){var t=r.map((function(e){return e.pending}));return qe.apply(void 0,t)(e)}:e()(r[0])},e.isPlain=Se,e.isPlainObject=we,e.isRejected=Ve,e.isRejectedWithValue=function e(){for(var t=arguments.length,r=new Array(t),n=0;n<t;n++)r[n]=arguments[n];var o=function(e){return e&&e.meta&&e.meta.rejectedWithValue};return 0===r.length?function(e){return ze(Ve.apply(void 0,r),o)(e)}:Ue(r)?function(e){return ze(Ve.apply(void 0,r),o)(e)}:e()(r[0])},e.nanoid=Te,e.unwrapResult=function(e){if(e.meta&&e.meta.rejectedWithValue)throw e.payload;if(e.error)throw e.error;return e.payload}}));
- //# sourceMappingURL=redux-toolkit.umd.min.js.map
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index dd43cfdc2..ebf724f2f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3030,13 +3030,13 @@
     prop-types "^15.6.1"
     react-lifecycles-compat "^3.0.4"
 
-"@reduxjs/toolkit@^1.5.0":
-  version "1.5.0"
-  resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.5.0.tgz#1025c1ccb224d1fc06d8d98a61f6717d57e6d477"
-  integrity sha512-E/FUraRx+8guw9Hlg/Ja8jI/hwCrmIKed8Annt9YsZw3BQp+F24t5I5b2OWR6pkEHY4hn1BgP08FrTZFRKsdaQ==
+"@reduxjs/toolkit@^1.6.2":
+  version "1.6.2"
+  resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.6.2.tgz#2f2b5365df77dd6697da28fdf44f33501ed9ba37"
+  integrity sha512-HbfI/hOVrAcMGAYsMWxw3UJyIoAS9JTdwddsjlr5w3S50tXhWb+EMyhIw+IAvCVCLETkzdjgH91RjDSYZekVBA==
   dependencies:
-    immer "^8.0.0"
-    redux "^4.0.0"
+    immer "^9.0.6"
+    redux "^4.1.0"
     redux-thunk "^2.3.0"
     reselect "^4.0.0"
 
@@ -5801,14 +5801,7 @@ axios-retry@^3.0.2:
   dependencies:
     is-retry-allowed "^1.1.0"
 
-axios@^0.19.2, axios@^0.21.1:
-  version "0.21.1"
-  resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
-  integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
-  dependencies:
-    follow-redirects "^1.10.0"
-
-axios@^0.21.2:
+axios@^0.19.2, axios@^0.21.2:
   version "0.21.4"
   resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575"
   integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==
@@ -12791,11 +12784,6 @@ fnv1a@^1.0.1:
   resolved "https://registry.yarnpkg.com/fnv1a/-/fnv1a-1.0.1.tgz#915e2d6d023c43d5224ad9f6d2a3c4156f5712f5"
   integrity sha1-kV4tbQI8Q9UiStn20qPEFW9XEvU=
 
-follow-redirects@^1.10.0:
-  version "1.13.1"
-  resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7"
-  integrity sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==
-
 follow-redirects@^1.14.0:
   version "1.14.3"
   resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.3.tgz#6ada78118d8d24caee595595accdc0ac6abd022e"
@@ -14598,11 +14586,6 @@ immer@1.10.0:
   resolved "https://registry.yarnpkg.com/immer/-/immer-1.10.0.tgz#bad67605ba9c810275d91e1c2a47d4582e98286d"
   integrity sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg==
 
-immer@^8.0.0, immer@^8.0.1:
-  version "8.0.1"
-  resolved "https://registry.yarnpkg.com/immer/-/immer-8.0.1.tgz#9c73db683e2b3975c424fb0572af5889877ae656"
-  integrity sha512-aqXhGP7//Gui2+UrEtvxZxSquQVXTpZ7KDxfCcKAF3Vysvw0CViVaW9RZ1j1xlIYqaaaipBoqdqeibkc18PNvA==
-
 immer@^9.0.6:
   version "9.0.6"
   resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.6.tgz#7a96bf2674d06c8143e327cbf73539388ddf1a73"
@@ -23558,6 +23541,13 @@ redux@*, redux@^4.0.0, redux@^4.0.1, redux@^4.0.5:
     loose-envify "^1.4.0"
     symbol-observable "^1.2.0"
 
+redux@^4.1.0:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.1.tgz#76f1c439bb42043f985fbd9bf21990e60bd67f47"
+  integrity sha512-hZQZdDEM25UY2P493kPYuKqviVwZ58lEmGQNeQ+gXa+U0gYPUBf7NKYazbe3m+bs/DzM/ahN12DbF+NG8i0CWw==
+  dependencies:
+    "@babel/runtime" "^7.9.2"
+
 reflect.ownkeys@^0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460"

From 1879fb48d7dbe0601aec036857a027e68f440f12 Mon Sep 17 00:00:00 2001
From: Niranjana Binoy <43930900+NiranjanaBinoy@users.noreply.github.com>
Date: Fri, 8 Oct 2021 12:20:08 -0400
Subject: [PATCH 49/56] removing the icronUrl in tokens fron the state.json for
 fixtures (#12309)

---
 test/e2e/fixtures/imported-account/state.json | 32 +++++++++----------
 .../fixtures/navigate-transactions/state.json | 32 +++++++++----------
 test/jest/mock-store.js                       |  8 ++---
 3 files changed, 35 insertions(+), 37 deletions(-)

diff --git a/test/e2e/fixtures/imported-account/state.json b/test/e2e/fixtures/imported-account/state.json
index edb463d87..42f5a7a54 100644
--- a/test/e2e/fixtures/imported-account/state.json
+++ b/test/e2e/fixtures/imported-account/state.json
@@ -95,7 +95,7 @@
           "symbol": "LRC",
           "decimals": 18,
           "name": "Loopring",
-          "iconUrl": "https://airswap-token-images.s3.amazonaws.com/LRC.png",
+          "iconUrl": "",
           "aggregators": [
             "airswapLight",
             "bancor",
@@ -117,7 +117,7 @@
           "symbol": "UMA",
           "decimals": 18,
           "name": "UMA",
-          "iconUrl": "https://assets.coingecko.com/coins/images/10951/thumb/UMA.png?1586307916",
+          "iconUrl": "",
           "aggregators": [
             "bancor",
             "cmc",
@@ -138,7 +138,7 @@
           "symbol": "SUSHI",
           "decimals": 18,
           "name": "SushiSwap",
-          "iconUrl": "https://assets.coingecko.com/coins/images/12271/thumb/512x512_Logo_no_chop.png?1606986688",
+          "iconUrl": "",
           "aggregators": [
             "bancor",
             "cmc",
@@ -159,7 +159,7 @@
           "symbol": "CRV",
           "decimals": 18,
           "name": "Curve DAO Token",
-          "iconUrl": "https://assets.coingecko.com/coins/images/12124/thumb/Curve.png?1597369484",
+          "iconUrl": "",
           "aggregators": [
             "bancor",
             "cmc",
@@ -180,7 +180,7 @@
           "symbol": "COMP",
           "decimals": 18,
           "name": "Compound",
-          "iconUrl": "https://assets.coingecko.com/coins/images/10775/thumb/COMP.png?1592625425",
+          "iconUrl": "",
           "aggregators": [
             "bancor",
             "cmc",
@@ -201,7 +201,7 @@
           "symbol": "BAL",
           "decimals": 18,
           "name": "Balancer",
-          "iconUrl": "https://assets.coingecko.com/coins/images/11683/thumb/Balancer.png?1592792958",
+          "iconUrl": "",
           "aggregators": [
             "bancor",
             "cmc",
@@ -222,7 +222,7 @@
           "symbol": "MATIC",
           "decimals": 18,
           "name": "Polygon",
-          "iconUrl": "https://raw.githubusercontent.com/MetaMask/eth-contract-metadata/master/images/matic-network-logo.svg",
+          "iconUrl": "",
           "aggregators": [
             "airswapLight",
             "bancor",
@@ -243,7 +243,7 @@
           "symbol": "BAT",
           "decimals": 18,
           "name": "Basic Attention Tok",
-          "iconUrl": "https://s3.amazonaws.com/airswap-token-images/BAT.png",
+          "iconUrl": "",
           "aggregators": [
             "airswapLight",
             "bancor",
@@ -269,7 +269,7 @@
               "symbol": "LRC",
               "decimals": 18,
               "name": "Loopring",
-              "iconUrl": "https://airswap-token-images.s3.amazonaws.com/LRC.png",
+              "iconUrl": "",
               "aggregators": [
                 "airswapLight",
                 "bancor",
@@ -291,7 +291,7 @@
               "symbol": "UMA",
               "decimals": 18,
               "name": "UMA",
-              "iconUrl": "https://assets.coingecko.com/coins/images/10951/thumb/UMA.png?1586307916",
+              "iconUrl": "",
               "aggregators": [
                 "bancor",
                 "cmc",
@@ -312,7 +312,7 @@
               "symbol": "SUSHI",
               "decimals": 18,
               "name": "SushiSwap",
-              "iconUrl": "https://assets.coingecko.com/coins/images/12271/thumb/512x512_Logo_no_chop.png?1606986688",
+              "iconUrl": "",
               "aggregators": [
                 "bancor",
                 "cmc",
@@ -333,7 +333,7 @@
               "symbol": "CRV",
               "decimals": 18,
               "name": "Curve DAO Token",
-              "iconUrl": "https://assets.coingecko.com/coins/images/12124/thumb/Curve.png?1597369484",
+              "iconUrl": "",
               "aggregators": [
                 "bancor",
                 "cmc",
@@ -354,7 +354,7 @@
               "symbol": "COMP",
               "decimals": 18,
               "name": "Compound",
-              "iconUrl": "https://assets.coingecko.com/coins/images/10775/thumb/COMP.png?1592625425",
+              "iconUrl": "",
               "aggregators": [
                 "bancor",
                 "cmc",
@@ -375,7 +375,7 @@
               "symbol": "BAL",
               "decimals": 18,
               "name": "Balancer",
-              "iconUrl": "https://assets.coingecko.com/coins/images/11683/thumb/Balancer.png?1592792958",
+              "iconUrl": "",
               "aggregators": [
                 "bancor",
                 "cmc",
@@ -396,7 +396,7 @@
               "symbol": "MATIC",
               "decimals": 18,
               "name": "Polygon",
-              "iconUrl": "https://raw.githubusercontent.com/MetaMask/eth-contract-metadata/master/images/matic-network-logo.svg",
+              "iconUrl": "",
               "aggregators": [
                 "airswapLight",
                 "bancor",
@@ -417,7 +417,7 @@
               "symbol": "BAT",
               "decimals": 18,
               "name": "Basic Attention Tok",
-              "iconUrl": "https://s3.amazonaws.com/airswap-token-images/BAT.png",
+              "iconUrl": "",
               "aggregators": [
                 "airswapLight",
                 "bancor",
diff --git a/test/e2e/fixtures/navigate-transactions/state.json b/test/e2e/fixtures/navigate-transactions/state.json
index 988098e68..3f08971b0 100644
--- a/test/e2e/fixtures/navigate-transactions/state.json
+++ b/test/e2e/fixtures/navigate-transactions/state.json
@@ -95,7 +95,7 @@
           "symbol": "LRC",
           "decimals": 18,
           "name": "Loopring",
-          "iconUrl": "https://airswap-token-images.s3.amazonaws.com/LRC.png",
+          "iconUrl": "",
           "aggregators": [
             "airswapLight",
             "bancor",
@@ -117,7 +117,7 @@
           "symbol": "UMA",
           "decimals": 18,
           "name": "UMA",
-          "iconUrl": "https://assets.coingecko.com/coins/images/10951/thumb/UMA.png?1586307916",
+          "iconUrl": "",
           "aggregators": [
             "bancor",
             "cmc",
@@ -138,7 +138,7 @@
           "symbol": "SUSHI",
           "decimals": 18,
           "name": "SushiSwap",
-          "iconUrl": "https://assets.coingecko.com/coins/images/12271/thumb/512x512_Logo_no_chop.png?1606986688",
+          "iconUrl": "",
           "aggregators": [
             "bancor",
             "cmc",
@@ -159,7 +159,7 @@
           "symbol": "CRV",
           "decimals": 18,
           "name": "Curve DAO Token",
-          "iconUrl": "https://assets.coingecko.com/coins/images/12124/thumb/Curve.png?1597369484",
+          "iconUrl": "",
           "aggregators": [
             "bancor",
             "cmc",
@@ -180,7 +180,7 @@
           "symbol": "COMP",
           "decimals": 18,
           "name": "Compound",
-          "iconUrl": "https://assets.coingecko.com/coins/images/10775/thumb/COMP.png?1592625425",
+          "iconUrl": "",
           "aggregators": [
             "bancor",
             "cmc",
@@ -201,7 +201,7 @@
           "symbol": "BAL",
           "decimals": 18,
           "name": "Balancer",
-          "iconUrl": "https://assets.coingecko.com/coins/images/11683/thumb/Balancer.png?1592792958",
+          "iconUrl": "",
           "aggregators": [
             "bancor",
             "cmc",
@@ -222,7 +222,7 @@
           "symbol": "MATIC",
           "decimals": 18,
           "name": "Polygon",
-          "iconUrl": "https://raw.githubusercontent.com/MetaMask/eth-contract-metadata/master/images/matic-network-logo.svg",
+          "iconUrl": "",
           "aggregators": [
             "airswapLight",
             "bancor",
@@ -243,7 +243,7 @@
           "symbol": "BAT",
           "decimals": 18,
           "name": "Basic Attention Tok",
-          "iconUrl": "https://s3.amazonaws.com/airswap-token-images/BAT.png",
+          "iconUrl": "",
           "aggregators": [
             "airswapLight",
             "bancor",
@@ -269,7 +269,7 @@
               "symbol": "LRC",
               "decimals": 18,
               "name": "Loopring",
-              "iconUrl": "https://airswap-token-images.s3.amazonaws.com/LRC.png",
+              "iconUrl": "",
               "aggregators": [
                 "airswapLight",
                 "bancor",
@@ -291,7 +291,7 @@
               "symbol": "UMA",
               "decimals": 18,
               "name": "UMA",
-              "iconUrl": "https://assets.coingecko.com/coins/images/10951/thumb/UMA.png?1586307916",
+              "iconUrl": "",
               "aggregators": [
                 "bancor",
                 "cmc",
@@ -312,7 +312,7 @@
               "symbol": "SUSHI",
               "decimals": 18,
               "name": "SushiSwap",
-              "iconUrl": "https://assets.coingecko.com/coins/images/12271/thumb/512x512_Logo_no_chop.png?1606986688",
+              "iconUrl": "",
               "aggregators": [
                 "bancor",
                 "cmc",
@@ -333,7 +333,7 @@
               "symbol": "CRV",
               "decimals": 18,
               "name": "Curve DAO Token",
-              "iconUrl": "https://assets.coingecko.com/coins/images/12124/thumb/Curve.png?1597369484",
+              "iconUrl": "",
               "aggregators": [
                 "bancor",
                 "cmc",
@@ -354,7 +354,7 @@
               "symbol": "COMP",
               "decimals": 18,
               "name": "Compound",
-              "iconUrl": "https://assets.coingecko.com/coins/images/10775/thumb/COMP.png?1592625425",
+              "iconUrl": "",
               "aggregators": [
                 "bancor",
                 "cmc",
@@ -375,7 +375,7 @@
               "symbol": "BAL",
               "decimals": 18,
               "name": "Balancer",
-              "iconUrl": "https://assets.coingecko.com/coins/images/11683/thumb/Balancer.png?1592792958",
+              "iconUrl": "",
               "aggregators": [
                 "bancor",
                 "cmc",
@@ -396,7 +396,7 @@
               "symbol": "MATIC",
               "decimals": 18,
               "name": "Polygon",
-              "iconUrl": "https://raw.githubusercontent.com/MetaMask/eth-contract-metadata/master/images/matic-network-logo.svg",
+              "iconUrl": "",
               "aggregators": [
                 "airswapLight",
                 "bancor",
@@ -417,7 +417,7 @@
               "symbol": "BAT",
               "decimals": 18,
               "name": "Basic Attention Tok",
-              "iconUrl": "https://s3.amazonaws.com/airswap-token-images/BAT.png",
+              "iconUrl": "",
               "aggregators": [
                 "airswapLight",
                 "bancor",
diff --git a/test/jest/mock-store.js b/test/jest/mock-store.js
index a23d7ad35..3d397a52c 100644
--- a/test/jest/mock-store.js
+++ b/test/jest/mock-store.js
@@ -230,8 +230,7 @@ export const createSwapsMockStore = () => {
           symbol: 'UNI',
           decimals: 18,
           name: 'Uniswap',
-          iconUrl:
-            'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/logo.png',
+          iconUrl: '',
           aggregators: [
             'airswapLight',
             'bancor',
@@ -253,7 +252,7 @@ export const createSwapsMockStore = () => {
           symbol: 'LINK',
           decimals: 18,
           name: 'Chainlink',
-          iconUrl: 'https://s3.amazonaws.com/airswap-token-images/LINK.png',
+          iconUrl: '',
           aggregators: [
             'airswapLight',
             'bancor',
@@ -275,8 +274,7 @@ export const createSwapsMockStore = () => {
           symbol: 'SUSHI',
           decimals: 18,
           name: 'SushiSwap',
-          iconUrl:
-            'https://assets.coingecko.com/coins/images/12271/thumb/512x512_Logo_no_chop.png?1606986688',
+          iconUrl: '',
           aggregators: [
             'bancor',
             'cmc',

From 3b5e33bc4cfa59b2499d4ac5556a92d216634270 Mon Sep 17 00:00:00 2001
From: Alex Donesky <adonesky@gmail.com>
Date: Fri, 8 Oct 2021 12:18:38 -0500
Subject: [PATCH 50/56] use improved-yarn-audit and exclude 1002401 and 1002581
 (#12310)

* use improved-yarn-audit and exclude 1002401
---
 .circleci/scripts/yarn-audit.sh | 4 +++-
 .depcheckrc.yml                 | 1 +
 package.json                    | 1 +
 yarn.lock                       | 5 +++++
 4 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/.circleci/scripts/yarn-audit.sh b/.circleci/scripts/yarn-audit.sh
index ebe036815..717b5f456 100755
--- a/.circleci/scripts/yarn-audit.sh
+++ b/.circleci/scripts/yarn-audit.sh
@@ -3,7 +3,9 @@
 set -u
 set -o pipefail
 
-yarn audit --level moderate --groups dependencies
+# use `improved-yarn-audit` since that allows for exclude
+# exclude 1002401 until we remove use of 3Box, 1002581 until we can find a better solution
+yarn run improved-yarn-audit --ignore-dev-deps --min-severity moderate --exclude 1002401,1002581
 audit_status="$?"
 
 # Use a bitmask to ignore INFO and LOW severity audit results
diff --git a/.depcheckrc.yml b/.depcheckrc.yml
index 70214b911..c99ca2f85 100644
--- a/.depcheckrc.yml
+++ b/.depcheckrc.yml
@@ -29,6 +29,7 @@ ignores:
   - "source-map-explorer"
   # development tool
   - "yarn-deduplicate"
+  - "improved-yarn-audit"
   # storybook
   - "@storybook/core"
   - "@storybook/addon-backgrounds"
diff --git a/package.json b/package.json
index e1577d3cb..0c7609ab6 100644
--- a/package.json
+++ b/package.json
@@ -287,6 +287,7 @@
     "gulp-watch": "^5.0.1",
     "gulp-zip": "^4.0.0",
     "history": "^5.0.0",
+    "improved-yarn-audit": "^2.3.3",
     "jest": "^26.6.3",
     "jsdom": "^11.2.0",
     "koa": "^2.7.0",
diff --git a/yarn.lock b/yarn.lock
index ebf724f2f..e534ce6d7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -14631,6 +14631,11 @@ import-local@^3.0.2:
     pkg-dir "^4.2.0"
     resolve-cwd "^3.0.0"
 
+improved-yarn-audit@^2.3.3:
+  version "2.3.3"
+  resolved "https://registry.yarnpkg.com/improved-yarn-audit/-/improved-yarn-audit-2.3.3.tgz#da0be78be4b678c73733066c9ccd21e1958fae8c"
+  integrity sha512-chZ7zPKGsA+CZeMExNPf9WZhETJLkC+u8cQlkQC9XyPZqQPctn3FavefTjXBXmX3Azin8WcoAbaok1FvjkLf6A==
+
 imurmurhash@^0.1.4:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"

From 78b7f32d97b49358ec3a10d17791afc463b82e0d Mon Sep 17 00:00:00 2001
From: ryanml <ryanlanese@gmail.com>
Date: Fri, 8 Oct 2021 11:10:01 -0700
Subject: [PATCH 51/56] Adding UrlIcon component to Storybook (#12285)

---
 ui/components/ui/url-icon/url-icon.stories.js | 27 +++++++++++++++++++
 1 file changed, 27 insertions(+)
 create mode 100644 ui/components/ui/url-icon/url-icon.stories.js

diff --git a/ui/components/ui/url-icon/url-icon.stories.js b/ui/components/ui/url-icon/url-icon.stories.js
new file mode 100644
index 000000000..630fefba3
--- /dev/null
+++ b/ui/components/ui/url-icon/url-icon.stories.js
@@ -0,0 +1,27 @@
+import React from 'react';
+import { text } from '@storybook/addon-knobs';
+import UrlIcon from './url-icon';
+
+export default {
+  title: 'UrlIcon',
+  id: __filename,
+};
+
+export const AST = () => {
+  return <UrlIcon name="AST" url="AST.png" />;
+};
+
+export const BAT = () => {
+  return <UrlIcon name="BAT" url="BAT_icon.svg" />;
+};
+
+export const CustomProps = () => {
+  return (
+    <UrlIcon
+      name={text('Symbol', '')}
+      url={text('Icon URL', '')}
+      className={text('className', '')}
+      fallbackClassName={text('fallbackClassName', '')}
+    />
+  );
+};

From 3d36fbd8ce7cac454c4de242305874fe714eb7c0 Mon Sep 17 00:00:00 2001
From: Alex Donesky <adonesky@gmail.com>
Date: Fri, 8 Oct 2021 14:50:36 -0500
Subject: [PATCH 52/56] Onboarding V2 Privacy settings view (#12253)

* add Set Advanced Privacy Settings onboarding view
---
 app/_locales/en/messages.json                 |  20 ++++
 ui/pages/onboarding-flow/index.scss           |   1 +
 ui/pages/onboarding-flow/onboarding-flow.js   |   8 +-
 .../privacy-settings/index.scss               |  50 ++++++++
 .../privacy-settings/privacy-settings.js      | 113 ++++++++++++++++++
 .../privacy-settings.stories.js               |  15 +++
 .../privacy-settings/privacy-settings.test.js |  66 ++++++++++
 .../privacy-settings/setting.js               |  33 +++++
 8 files changed, 305 insertions(+), 1 deletion(-)
 create mode 100644 ui/pages/onboarding-flow/privacy-settings/index.scss
 create mode 100644 ui/pages/onboarding-flow/privacy-settings/privacy-settings.js
 create mode 100644 ui/pages/onboarding-flow/privacy-settings/privacy-settings.stories.js
 create mode 100644 ui/pages/onboarding-flow/privacy-settings/privacy-settings.test.js
 create mode 100644 ui/pages/onboarding-flow/privacy-settings/setting.js

diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 887ab9e42..e1d91e249 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -1210,6 +1210,9 @@
   "ipfsGatewayDescription": {
     "message": "Enter the URL of the IPFS CID gateway to use for ENS content resolution."
   },
+  "jsDeliver": {
+    "message": "jsDeliver"
+  },
   "jsonFile": {
     "message": "JSON File",
     "description": "format for importing an account"
@@ -1620,6 +1623,14 @@
     "message": "\"$1\" will close this tab and direct back to $2",
     "description": "Return the user to the site that initiated onboarding"
   },
+  "onboardingShowIncomingTransactionsDescription": {
+    "message": "Showing incoming transactions in your wallet relies on communication with $1. Etherscan will have access to your Ethereum address and your IP address. View $2.",
+    "description": "$1 is a clickable link with text defined by the 'etherscan' key. $2 is a clickable link with text defined by the 'privacyMsg' key."
+  },
+  "onboardingUsePhishingDetectionDescription": {
+    "message": "Phishing detection alerts rely on communication with $1. jsDeliver will have access to your IP address. View $2.",
+    "description": "The $1 is the word 'jsDeliver', from key 'jsDeliver' and $2 is the words Privacy Policy from key 'privacyMsg', both separated here so that it can be wrapped as a link"
+  },
   "onlyAddTrustedNetworks": {
     "message": "A malicious network provider can lie about the state of the blockchain and record your network activity. Only add custom networks you trust."
   },
@@ -2020,6 +2031,12 @@
   "separateEachWord": {
     "message": "Separate each word with a single space"
   },
+  "setAdvancedPrivacySettings": {
+    "message": "Set advanced privacy settings"
+  },
+  "setAdvancedPrivacySettingsDetails": {
+    "message": "MetaMask uses these trusted third-party services to enhance product usability and safety."
+  },
   "settings": {
     "message": "Settings"
   },
@@ -2718,6 +2735,9 @@
   "tryAgain": {
     "message": "Try again"
   },
+  "turnOnTokenDetection": {
+    "message": "Turn on Token Detection"
+  },
   "typePassword": {
     "message": "Type your MetaMask password"
   },
diff --git a/ui/pages/onboarding-flow/index.scss b/ui/pages/onboarding-flow/index.scss
index 8d5a5cd0c..afcf7b2fa 100644
--- a/ui/pages/onboarding-flow/index.scss
+++ b/ui/pages/onboarding-flow/index.scss
@@ -2,6 +2,7 @@
 @import 'new-account/index';
 @import 'onboarding-app-header/index';
 @import 'secure-your-wallet/index';
+@import 'privacy-settings/index';
 
 .onboarding-flow {
   width: 100%;
diff --git a/ui/pages/onboarding-flow/onboarding-flow.js b/ui/pages/onboarding-flow/onboarding-flow.js
index 82c1b00e6..c00af0977 100644
--- a/ui/pages/onboarding-flow/onboarding-flow.js
+++ b/ui/pages/onboarding-flow/onboarding-flow.js
@@ -9,6 +9,7 @@ import {
   ONBOARDING_UNLOCK_ROUTE,
   DEFAULT_ROUTE,
   ONBOARDING_SECURE_YOUR_WALLET_ROUTE,
+  ONBOARDING_PRIVACY_SETTINGS_ROUTE,
 } from '../../helpers/constants/routes';
 import {
   getCompletedOnboarding,
@@ -26,6 +27,7 @@ import NewAccount from './new-account/new-account';
 import ReviewRecoveryPhrase from './recovery-phrase/review-recovery-phrase';
 import SecureYourWallet from './secure-your-wallet/secure-your-wallet';
 import ConfirmRecoveryPhrase from './recovery-phrase/confirm-recovery-phrase';
+import PrivacySettings from './privacy-settings/privacy-settings';
 
 export default function OnboardingFlow() {
   const [seedPhrase, setSeedPhrase] = useState('');
@@ -41,7 +43,7 @@ export default function OnboardingFlow() {
     // For ONBOARDING_V2 dev purposes,
     // Remove when ONBOARDING_V2 dev complete
     if (process.env.ONBOARDING_V2) {
-      history.push(ONBOARDING_SECURE_YOUR_WALLET_ROUTE);
+      history.push(ONBOARDING_PRIVACY_SETTINGS_ROUTE);
       return;
     }
 
@@ -108,6 +110,10 @@ export default function OnboardingFlow() {
               <Unlock {...routeProps} onSubmit={handleUnlock} />
             )}
           />
+          <Route
+            path={ONBOARDING_PRIVACY_SETTINGS_ROUTE}
+            component={PrivacySettings}
+          />
           <Route exact path="*" component={OnboardingFlowSwitch} />
         </Switch>
       </div>
diff --git a/ui/pages/onboarding-flow/privacy-settings/index.scss b/ui/pages/onboarding-flow/privacy-settings/index.scss
new file mode 100644
index 000000000..e5a6d059b
--- /dev/null
+++ b/ui/pages/onboarding-flow/privacy-settings/index.scss
@@ -0,0 +1,50 @@
+.privacy-settings {
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+
+  &__header {
+    display: flex;
+    justify-content: center;
+    flex-direction: column;
+    text-align: center;
+    max-width: 500px;
+    margin: 24px;
+  }
+
+  &__settings {
+    display: flex;
+    justify-content: center;
+    flex-direction: column;
+    text-align: center;
+    max-width: 620px;
+    margin-bottom: 20px;
+
+    a {
+      color: $Blue-500;
+
+      &:hover {
+        cursor: pointer;
+        color: $Blue-300;
+      }
+    }
+  }
+
+  &__setting {
+    display: flex;
+    justify-content: center;
+    flex-direction: column;
+    text-align: left;
+    max-width: 430px;
+
+    &__toggle {
+      margin-left: 42px;
+    }
+  }
+
+  & button {
+    max-width: 50%;
+    padding: 15px;
+  }
+}
diff --git a/ui/pages/onboarding-flow/privacy-settings/privacy-settings.js b/ui/pages/onboarding-flow/privacy-settings/privacy-settings.js
new file mode 100644
index 000000000..e372e1130
--- /dev/null
+++ b/ui/pages/onboarding-flow/privacy-settings/privacy-settings.js
@@ -0,0 +1,113 @@
+import React, { useState } from 'react';
+import { useHistory } from 'react-router-dom';
+import { useDispatch } from 'react-redux';
+
+import Button from '../../../components/ui/button';
+import Typography from '../../../components/ui/typography';
+import {
+  FONT_WEIGHT,
+  TYPOGRAPHY,
+} from '../../../helpers/constants/design-system';
+import { useI18nContext } from '../../../hooks/useI18nContext';
+import {
+  setFeatureFlag,
+  setUsePhishDetect,
+  setUseTokenDetection,
+} from '../../../store/actions';
+import { ONBOARDING_PIN_EXTENSION_ROUTE } from '../../../helpers/constants/routes';
+import { Setting } from './setting';
+
+export default function PrivacySettings() {
+  const t = useI18nContext();
+  const dispatch = useDispatch();
+  const history = useHistory();
+  const [usePhishingDetection, setUsePhishingDetection] = useState(true);
+  const [turnOnTokenDetection, setTurnOnTokenDetection] = useState(true);
+  const [showIncomingTransactions, setShowIncomingTransactions] = useState(
+    true,
+  );
+
+  const handleSubmit = () => {
+    dispatch(
+      setFeatureFlag('showIncomingTransactions', showIncomingTransactions),
+    );
+    dispatch(setUsePhishDetect(usePhishingDetection));
+    dispatch(setUseTokenDetection(turnOnTokenDetection));
+    history.push(ONBOARDING_PIN_EXTENSION_ROUTE);
+  };
+
+  return (
+    <>
+      <div className="privacy-settings">
+        <div className="privacy-settings__header">
+          <Typography variant={TYPOGRAPHY.H2} fontWeight={FONT_WEIGHT.BOLD}>
+            {t('setAdvancedPrivacySettings')}
+          </Typography>
+          <Typography variant={TYPOGRAPHY.H4}>
+            {t('setAdvancedPrivacySettingsDetails')}
+          </Typography>
+        </div>
+        <div
+          className="privacy-settings__settings"
+          data-testid="privacy-settings-settings"
+        >
+          <Setting
+            value={showIncomingTransactions}
+            setValue={setShowIncomingTransactions}
+            title={t('showIncomingTransactions')}
+            description={t('onboardingShowIncomingTransactionsDescription', [
+              <a
+                key="etherscan"
+                href="https://etherscan.io/"
+                target="_blank"
+                rel="noreferrer"
+              >
+                {t('etherscan')}
+              </a>,
+              <a
+                href="https://cn.etherscan.com/privacyPolicy"
+                target="_blank"
+                rel="noreferrer"
+                key="privacyMsg"
+              >
+                {t('privacyMsg')}
+              </a>,
+            ])}
+          />
+          <Setting
+            value={usePhishingDetection}
+            setValue={setUsePhishingDetection}
+            title={t('usePhishingDetection')}
+            description={t('onboardingUsePhishingDetectionDescription', [
+              <a
+                href="https://www.jsdelivr.com"
+                target="_blank"
+                rel="noreferrer"
+                key="jsDeliver"
+              >
+                {t('jsDeliver')}
+              </a>,
+              <a
+                href="https://www.jsdelivr.com/terms/privacy-policy-jsdelivr-com"
+                target="_blank"
+                rel="noreferrer"
+                key="privacyMsg"
+              >
+                {t('privacyMsg')}
+              </a>,
+            ])}
+          />
+          <Setting
+            value={turnOnTokenDetection}
+            setValue={setTurnOnTokenDetection}
+            title={t('turnOnTokenDetection')}
+            description={t('useTokenDetectionDescription')}
+          />
+        </div>
+        <Button type="primary" rounded onClick={handleSubmit}>
+          {t('done')}
+        </Button>
+      </div>
+    </>
+  );
+}
diff --git a/ui/pages/onboarding-flow/privacy-settings/privacy-settings.stories.js b/ui/pages/onboarding-flow/privacy-settings/privacy-settings.stories.js
new file mode 100644
index 000000000..c0f775439
--- /dev/null
+++ b/ui/pages/onboarding-flow/privacy-settings/privacy-settings.stories.js
@@ -0,0 +1,15 @@
+import React from 'react';
+import PrivacySettings from './privacy-settings';
+
+export default {
+  title: 'Onboarding - Privacy Settings',
+  id: __filename,
+};
+
+export const Base = () => {
+  return (
+    <div style={{ maxHeight: '2000px' }}>
+      <PrivacySettings />
+    </div>
+  );
+};
diff --git a/ui/pages/onboarding-flow/privacy-settings/privacy-settings.test.js b/ui/pages/onboarding-flow/privacy-settings/privacy-settings.test.js
new file mode 100644
index 000000000..51c9df987
--- /dev/null
+++ b/ui/pages/onboarding-flow/privacy-settings/privacy-settings.test.js
@@ -0,0 +1,66 @@
+import React from 'react';
+import { fireEvent } from '@testing-library/react';
+import configureMockStore from 'redux-mock-store';
+import thunk from 'redux-thunk';
+import * as actions from '../../../store/actions';
+import { renderWithProvider } from '../../../../test/jest';
+import PrivacySettings from './privacy-settings';
+
+describe('Privacy Settings Onboarding View', () => {
+  const mockStore = {
+    metamask: {
+      provider: {
+        type: 'test',
+      },
+    },
+  };
+
+  const store = configureMockStore([thunk])(mockStore);
+  const setFeatureFlagStub = jest.fn();
+  const setUsePhishDetectStub = jest.fn();
+  const setUseTokenDetectionStub = jest.fn();
+
+  actions._setBackgroundConnection({
+    setFeatureFlag: setFeatureFlagStub,
+    setUsePhishDetect: setUsePhishDetectStub,
+    setUseTokenDetection: setUseTokenDetectionStub,
+  });
+
+  it('should update preferences', () => {
+    const { container, getByText } = renderWithProvider(
+      <PrivacySettings />,
+      store,
+    );
+    // All settings are initialized toggled to true
+    expect(setFeatureFlagStub).toHaveBeenCalledTimes(0);
+    expect(setUsePhishDetectStub).toHaveBeenCalledTimes(0);
+    expect(setUseTokenDetectionStub).toHaveBeenCalledTimes(0);
+    const toggles = container.querySelectorAll('input[type=checkbox]');
+    const submitButton = getByText('Done');
+
+    // toggle to false
+    fireEvent.click(toggles[0]);
+    fireEvent.click(toggles[1]);
+    fireEvent.click(toggles[2]);
+    fireEvent.click(submitButton);
+
+    expect(setFeatureFlagStub).toHaveBeenCalledTimes(1);
+    expect(setUsePhishDetectStub).toHaveBeenCalledTimes(1);
+    expect(setUseTokenDetectionStub).toHaveBeenCalledTimes(1);
+    expect(setFeatureFlagStub.mock.calls[0][1]).toStrictEqual(false);
+    expect(setUsePhishDetectStub.mock.calls[0][0]).toStrictEqual(false);
+    expect(setUseTokenDetectionStub.mock.calls[0][0]).toStrictEqual(false);
+
+    // toggle back to true
+    fireEvent.click(toggles[0]);
+    fireEvent.click(toggles[1]);
+    fireEvent.click(toggles[2]);
+    fireEvent.click(submitButton);
+    expect(setFeatureFlagStub).toHaveBeenCalledTimes(2);
+    expect(setUsePhishDetectStub).toHaveBeenCalledTimes(2);
+    expect(setUseTokenDetectionStub).toHaveBeenCalledTimes(2);
+    expect(setFeatureFlagStub.mock.calls[1][1]).toStrictEqual(true);
+    expect(setUsePhishDetectStub.mock.calls[1][0]).toStrictEqual(true);
+    expect(setUseTokenDetectionStub.mock.calls[1][0]).toStrictEqual(true);
+  });
+});
diff --git a/ui/pages/onboarding-flow/privacy-settings/setting.js b/ui/pages/onboarding-flow/privacy-settings/setting.js
new file mode 100644
index 000000000..82ba1275c
--- /dev/null
+++ b/ui/pages/onboarding-flow/privacy-settings/setting.js
@@ -0,0 +1,33 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Box from '../../../components/ui/box';
+import Typography from '../../../components/ui/typography';
+import ToggleButton from '../../../components/ui/toggle-button';
+import {
+  JUSTIFY_CONTENT,
+  TYPOGRAPHY,
+  FONT_WEIGHT,
+} from '../../../helpers/constants/design-system';
+
+export const Setting = ({ value, setValue, title, description }) => {
+  return (
+    <Box justifyContent={JUSTIFY_CONTENT.CENTER} margin={3}>
+      <div className="privacy-settings__setting">
+        <Typography variant={TYPOGRAPHY.H5} fontWeight={FONT_WEIGHT.BOLD}>
+          {title}
+        </Typography>
+        <Typography variant={TYPOGRAPHY.H6}>{description}</Typography>
+      </div>
+      <div className="privacy-settings__setting__toggle">
+        <ToggleButton value={value} onToggle={(val) => setValue(!val)} />
+      </div>
+    </Box>
+  );
+};
+
+Setting.propTypes = {
+  value: PropTypes.bool,
+  setValue: PropTypes.func,
+  title: PropTypes.string,
+  description: PropTypes.string,
+};

From 18ca9d14e042e2c2c6eb9ace727de60961923310 Mon Sep 17 00:00:00 2001
From: Dan J Miller <danjm.com@gmail.com>
Date: Fri, 8 Oct 2021 18:57:45 -0230
Subject: [PATCH 53/56] Do case-insensitive comparisons of token addresses in
 view-quote and build-quote (#12315)

---
 ui/pages/swaps/build-quote/build-quote.js | 31 ++++++++++++++---------
 ui/pages/swaps/view-quote/view-quote.js   |  9 ++++---
 2 files changed, 25 insertions(+), 15 deletions(-)

diff --git a/ui/pages/swaps/build-quote/build-quote.js b/ui/pages/swaps/build-quote/build-quote.js
index 1503da608..32e25816d 100644
--- a/ui/pages/swaps/build-quote/build-quote.js
+++ b/ui/pages/swaps/build-quote/build-quote.js
@@ -51,7 +51,10 @@ import {
   hexToDecimal,
 } from '../../../helpers/utils/conversions.util';
 import { calcTokenAmount } from '../../../helpers/utils/token-util';
-import { getURLHostName } from '../../../helpers/utils/util';
+import {
+  getURLHostName,
+  isEqualCaseInsensitive,
+} from '../../../helpers/utils/util';
 import { usePrevious } from '../../../hooks/usePrevious';
 import { useTokenTracker } from '../../../hooks/useTokenTracker';
 import { useTokenFiatAmount } from '../../../hooks/useTokenFiatAmount';
@@ -169,8 +172,9 @@ export default function BuildQuote({
     shuffledTokensList,
   });
   const selectedToToken =
-    tokensToSearch.find(({ address }) => address === toToken?.address) ||
-    toToken;
+    tokensToSearch.find(({ address }) =>
+      isEqualCaseInsensitive(address, toToken?.address),
+    ) || toToken;
   const toTokenIsNotDefault =
     selectedToToken?.address &&
     !isSwapsDefaultTokenAddress(selectedToToken?.address, chainId);
@@ -226,8 +230,8 @@ export default function BuildQuote({
     }
     if (
       token?.address &&
-      !memoizedUsersTokens.find(
-        (usersToken) => usersToken.address === token.address,
+      !memoizedUsersTokens.find((usersToken) =>
+        isEqualCaseInsensitive(usersToken.address, token.address),
       )
     ) {
       fetchTokenBalance(token.address, selectedAccountAddress).then(
@@ -298,12 +302,12 @@ export default function BuildQuote({
   );
 
   const hideDropdownItemIf = useCallback(
-    (item) => item.address === fromTokenAddress,
+    (item) => isEqualCaseInsensitive(item.address, fromTokenAddress),
     [fromTokenAddress],
   );
 
-  const tokensWithBalancesFromToken = tokensWithBalances.find(
-    (token) => token.address === fromToken?.address,
+  const tokensWithBalancesFromToken = tokensWithBalances.find((token) =>
+    isEqualCaseInsensitive(token.address, fromToken?.address),
   );
   const previousTokensWithBalancesFromToken = usePrevious(
     tokensWithBalancesFromToken,
@@ -314,9 +318,10 @@ export default function BuildQuote({
       tokensWithBalancesFromToken?.address,
       chainId,
     );
-    const addressesAreTheSame =
-      tokensWithBalancesFromToken?.address ===
-      previousTokensWithBalancesFromToken?.address;
+    const addressesAreTheSame = isEqualCaseInsensitive(
+      tokensWithBalancesFromToken?.address,
+      previousTokensWithBalancesFromToken?.address,
+    );
     const balanceHasChanged =
       tokensWithBalancesFromToken?.balance !==
       previousTokensWithBalancesFromToken?.balance;
@@ -490,7 +495,9 @@ export default function BuildQuote({
               !Object.keys(topAssets).length)
           }
           selectPlaceHolderText={t('swapSelect')}
-          hideItemIf={(item) => item.address === selectedToToken?.address}
+          hideItemIf={(item) =>
+            isEqualCaseInsensitive(item.address, selectedToToken?.address)
+          }
           listContainerClassName="build-quote__open-dropdown"
           autoFocus
         />
diff --git a/ui/pages/swaps/view-quote/view-quote.js b/ui/pages/swaps/view-quote/view-quote.js
index 6b6f2e37e..e983310f7 100644
--- a/ui/pages/swaps/view-quote/view-quote.js
+++ b/ui/pages/swaps/view-quote/view-quote.js
@@ -50,7 +50,10 @@ import {
 } from '../../../selectors';
 import { getNativeCurrency, getTokens } from '../../../ducks/metamask/metamask';
 
-import { toPrecisionWithoutTrailingZeros } from '../../../helpers/utils/util';
+import {
+  toPrecisionWithoutTrailingZeros,
+  isEqualCaseInsensitive,
+} from '../../../helpers/utils/util';
 
 import {
   safeRefetchQuotes,
@@ -214,8 +217,8 @@ export default function ViewQuote() {
   const balanceToken =
     fetchParamsSourceToken === defaultSwapsToken.address
       ? defaultSwapsToken
-      : tokensWithBalances.find(
-          ({ address }) => address === fetchParamsSourceToken,
+      : tokensWithBalances.find(({ address }) =>
+          isEqualCaseInsensitive(address, fetchParamsSourceToken),
         );
 
   const selectedFromToken = balanceToken || usedQuote.sourceTokenInfo;

From a723179219fd0ca8b779966bb6123dc39a1f2b5d Mon Sep 17 00:00:00 2001
From: Mark Stacey <markjstacey@gmail.com>
Date: Fri, 8 Oct 2021 22:46:11 -0230
Subject: [PATCH 54/56] Remove unused `test/e2e/run-all.sh` script (#12314)

This script was replaced by `test/e2e/run-all.js` in #11301, but was
accidentally left behind despite being unused. It has been removed.
---
 test/e2e/run-all.sh | 15 ---------------
 1 file changed, 15 deletions(-)
 delete mode 100755 test/e2e/run-all.sh

diff --git a/test/e2e/run-all.sh b/test/e2e/run-all.sh
deleted file mode 100755
index 88cc695d8..000000000
--- a/test/e2e/run-all.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/env bash
-
-set -x
-set -e
-set -u
-set -o pipefail
-
-readonly __DIR__=$( cd "${BASH_SOURCE[0]%/*}" && pwd )
-
-for spec in "${__DIR__}"/tests/*.spec.js
-do
-  node "${__DIR__}/run-e2e-test.js" "${spec}"
-done
-
-node "${__DIR__}/run-e2e-test.js" "${__DIR__}/metamask-ui.spec.js"

From 9da60c7a42476a12004064e4885bb2f940f57b1a Mon Sep 17 00:00:00 2001
From: Mark Stacey <markjstacey@gmail.com>
Date: Fri, 8 Oct 2021 22:47:41 -0230
Subject: [PATCH 55/56] Update `caniuse-lite` (#12312)

`caniuse-lite` has been updated to the latest published version. This
update prevents various console warnings that appear during the build.
---
 package.json |  2 +-
 yarn.lock    | 15 ++++-----------
 2 files changed, 5 insertions(+), 12 deletions(-)

diff --git a/package.json b/package.json
index 0c7609ab6..c59266cb0 100644
--- a/package.json
+++ b/package.json
@@ -72,7 +72,7 @@
   },
   "resolutions": {
     "**/regenerator-runtime": "^0.13.7",
-    "**/caniuse-lite": "1.0.30001260",
+    "**/caniuse-lite": "1.0.30001265",
     "**/configstore/dot-prop": "^5.1.1",
     "**/ethers/elliptic": "^6.5.4",
     "**/knex/minimist": "^1.2.5",
diff --git a/yarn.lock b/yarn.lock
index e534ce6d7..eeb5a4573 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7756,12 +7756,10 @@ camelcase@^6.2.0:
   resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809"
   integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==
 
-caniuse-lite@1.0.30001260, caniuse-lite@^1.0.30000810, caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30001035, caniuse-lite@^1.0.30001097, caniuse-lite@^1.0.30001157:
-  version "1.0.30001260"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001260.tgz#e3be3f34ddad735ca4a2736fa9e768ef34316270"
-  integrity sha512-Fhjc/k8725ItmrvW5QomzxLeojewxvqiYCKeFcfFEhut28IVLdpHU19dneOmltZQIE5HNbawj1HYD+1f2bM1Dg==
-  dependencies:
-    nanocolors "^0.1.0"
+caniuse-lite@1.0.30001265, caniuse-lite@^1.0.30000810, caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30001035, caniuse-lite@^1.0.30001097, caniuse-lite@^1.0.30001157:
+  version "1.0.30001265"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001265.tgz#0613c9e6c922e422792e6fcefdf9a3afeee4f8c3"
+  integrity sha512-YzBnspggWV5hep1m9Z6sZVLOt7vrju8xWooFAgN6BA5qvy98qPAPb7vNUzypFaoh2pb3vlfzbDO8tB57UPGbtw==
 
 capture-exit@^2.0.0:
   version "2.0.0"
@@ -19681,11 +19679,6 @@ nanoassert@^1.0.0:
   resolved "https://registry.yarnpkg.com/nanoassert/-/nanoassert-1.1.0.tgz#4f3152e09540fde28c76f44b19bbcd1d5a42478d"
   integrity sha1-TzFS4JVA/eKMdvRLGbvNHVpCR40=
 
-nanocolors@^0.1.0:
-  version "0.1.12"
-  resolved "https://registry.yarnpkg.com/nanocolors/-/nanocolors-0.1.12.tgz#8577482c58cbd7b5bb1681db4cf48f11a87fd5f6"
-  integrity sha512-2nMHqg1x5PU+unxX7PGY7AuYxl2qDx7PSrTRjizr8sxdd3l/3hBuWWaki62qmtYm2U5i4Z5E7GbjlyDFhs9/EQ==
-
 nanoid@^2.0.0, nanoid@^2.1.6:
   version "2.1.11"
   resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280"

From 3332a11f772132a0735b0aa94314c7fff5cc2f8a Mon Sep 17 00:00:00 2001
From: Jean P <10632523+TamtamHero@users.noreply.github.com>
Date: Sat, 9 Oct 2021 19:30:46 +0200
Subject: [PATCH 56/56] Update warning message when Ledger app has contract
 data/blind signing setting disabled (#12256)

---
 app/_locales/en/messages.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index e1d91e249..38ec6484f 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -1251,7 +1251,7 @@
     "message": "Prior to clicking confirm:"
   },
   "ledgerLiveDialogStepFour": {
-    "message": "Enable smart contract data on your Ledger device"
+    "message": "Enable \"smart contract data\" or \"blind signing\" on your Ledger device"
   },
   "ledgerLiveDialogStepOne": {
     "message": "Enable Use Ledger Live under Settings > Advanced"